| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066 |
- (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,
- /**
- * A jQuery reference to a module element that should be
- * added to a new node after it has been rendered.
- *
- * @since 1.0
- * @access private
- * @property {Object} _addModuleAfterNodeRender
- */
- _addModuleAfterNodeRender : 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,
- /**
- * 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,
- /**
- * Initializes the builder interface.
- *
- * @since 1.0
- * @access private
- * @method _init
- */
- _init: function()
- {
- FLBuilder._initJQueryReadyFix();
- FLBuilder._initGlobalErrorHandling();
- FLBuilder._initPostLock();
- FLBuilder._initClassNames();
- FLBuilder._initMediaUploader();
- FLBuilder._initOverflowFix();
- FLBuilder._initScrollbars();
- FLBuilder._initLightboxes();
- FLBuilder._initDropTargets();
- FLBuilder._initSortables();
- FLBuilder._initStrings();
- FLBuilder._initTipTips();
- FLBuilder._initTinyMCE();
- FLBuilder._bindEvents();
- FLBuilder._bindOverlayEvents();
- FLBuilder._setupEmptyLayout();
- FLBuilder._highlightEmptyCols();
- FLBuilder.addHook('didInitUI', FLBuilder._showTourOrTemplates.bind(FLBuilder) );
- 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 );
- }
- });
- };
- },
- /**
- * 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(30);
- 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()
- {
- $('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()
- {
- $('.fl-nanoscroller').nanoScroller({
- alwaysVisible: true,
- preventPageScrolling: true,
- paneClass: 'fl-nanoscroller-pane',
- sliderClass: 'fl-nanoscroller-slider',
- contentClass: 'fl-nanoscroller-content'
- });
- },
- /**
- * Initializes jQuery sortables for drag and drop.
- *
- * @since 1.0
- * @access private
- * @method _initSortables
- */
- _initSortables: function()
- {
- var defaults = {
- appendTo: 'body',
- 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 = '';
- // Module Connections.
- if ( 'row' == FLBuilderConfig.userTemplateType || 'column' == FLBuilderConfig.userTemplateType ) {
- moduleConnections = FLBuilder._contentClass + ' .fl-col-group-drop-target, ' +
- FLBuilder._contentClass + ' .fl-col-drop-target, ' +
- FLBuilder._contentClass + ' .fl-col-content';
- }
- else {
- moduleConnections = FLBuilder._contentClass + ' .fl-row-drop-target, ' +
- FLBuilder._contentClass + ' .fl-col-group-drop-target, ' +
- FLBuilder._contentClass + ' .fl-col-drop-target, ' +
- FLBuilder._contentClass + ' .fl-col:not(.fl-node-global) .fl-col-content';
- }
- // Column Connections.
- if ( 'row' == FLBuilderConfig.userTemplateType ) {
- columnConnections = FLBuilder._contentClass + ' .fl-col-group-drop-target, ' +
- FLBuilder._contentClass + ' .fl-col-drop-target';
- }
- else {
- columnConnections = FLBuilder._contentClass + ' .fl-row-drop-target, ' +
- FLBuilder._contentClass + ' .fl-col-group-drop-target, ' +
- FLBuilder._contentClass + ' .fl-col-drop-target';
- }
- // Row Connections.
- if ( FLBuilderConfig.nestedColumns ) {
- rowConnections = moduleConnections;
- }
- else if ( 'row' == FLBuilderConfig.userTemplateType ) {
- rowConnections = FLBuilder._contentClass + ' .fl-col-group-drop-target, ' +
- FLBuilder._contentClass + ' .fl-col-drop-target';
- }
- else {
- rowConnections = FLBuilder._contentClass + ' .fl-row-drop-target, ' +
- FLBuilder._contentClass + ' .fl-col-group-drop-target, ' +
- FLBuilder._contentClass + ' .fl-col-drop-target';
- }
- // Row layouts from the builder panel.
- $('.fl-builder-rows').sortable($.extend({}, defaults, {
- connectWith: rowConnections,
- items: '.fl-builder-block-row',
- stop: FLBuilder._rowDragStop
- }));
- // Row templates from the builder panel.
- $('.fl-builder-row-templates').sortable($.extend({}, defaults, {
- connectWith: FLBuilder._contentClass + ' .fl-row-drop-target',
- items: '.fl-builder-block-row-template',
- stop: FLBuilder._nodeTemplateDragStop
- }));
- // Saved rows from the builder panel.
- $('.fl-builder-saved-rows').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').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').sortable($.extend({}, defaults, {
- connectWith: moduleConnections,
- items: '.fl-builder-block-module',
- stop: FLBuilder._moduleDragStop
- }));
- // Module templates from the builder panel.
- $('.fl-builder-module-templates').sortable($.extend({}, defaults, {
- connectWith: moduleConnections,
- items: '.fl-builder-block-module-template',
- stop: FLBuilder._nodeTemplateDragStop
- }));
- // Saved modules from the builder panel.
- $('.fl-builder-saved-modules').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').sortable($.extend({}, defaults, {
- connectWith: FLBuilder._contentClass + ' .fl-row-drop-target',
- helper: FLBuilder._rowDragHelper,
- start: FLBuilder._rowDragStart,
- stop: FLBuilder._rowDragStop
- }));
- // Columns
- $('.fl-col-sortable-proxy').sortable($.extend({}, defaults, {
- connectWith: moduleConnections,
- helper: FLBuilder._colDragHelper,
- start: FLBuilder._colDragStart,
- stop: FLBuilder._colDragStop
- }));
- // Modules
- $(FLBuilder._contentClass + ' .fl-col-content').sortable($.extend({}, defaults, {
- connectWith: moduleConnections,
- handle: '.fl-module-overlay .fl-block-overlay-actions .fl-block-move',
- helper: FLBuilder._moduleDragHelper,
- items: '.fl-module, .fl-col-group',
- start: FLBuilder._moduleDragStart,
- stop: FLBuilder._moduleDragStop
- }));
- // 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 );
- },
- /**
- * 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').delegate('.fl-builder-content a', 'click', FLBuilder._preventDefault);
- $('body').delegate('button.fl-builder-button', 'mouseup', this._buttonMouseUp.bind(this) );
- /* Heartbeat */
- $(document).on('heartbeat-tick', FLBuilder._initPostLock);
- /* Unload Warning */
- $(window).on('beforeunload', FLBuilder._warnBeforeUnload);
- /* Submenus */
- $('body').delegate('.fl-builder-has-submenu', 'click', FLBuilder._submenuParentClicked);
- $('body').delegate('.fl-builder-has-submenu a', 'click', FLBuilder._submenuChildClicked);
- $('body').delegate('.fl-builder-submenu', 'mouseenter', FLBuilder._submenuMouseenter);
- $('body').delegate('.fl-builder-submenu', 'mouseleave', FLBuilder._submenuMouseleave);
- $('body').delegate('.fl-builder-submenu .fl-builder-has-submenu', 'mouseenter', FLBuilder._submenuNestedParentMouseenter);
- /* Panel */
- $('.fl-builder-panel-actions .fl-builder-panel-close').on('click', FLBuilder._closePanel);
- $('.fl-builder-blocks-section-title').on('click', FLBuilder._blockSectionTitleClicked);
- $('body').delegate('.fl-builder-node-template-actions', 'mousedown', FLBuilder._stopPropagation);
- $('body').delegate('.fl-builder-node-template-edit', 'mousedown', FLBuilder._stopPropagation);
- $('body').delegate('.fl-builder-node-template-delete', 'mousedown', FLBuilder._stopPropagation);
- $('body').delegate('.fl-builder-node-template-edit', 'click', FLBuilder._editNodeTemplateClicked);
- $('body').delegate('.fl-builder-node-template-delete', 'click', FLBuilder._deleteNodeTemplateClicked);
- $('body').delegate('.fl-builder-block', 'mousedown', FLBuilder._blockDragInit );
- $('body').on('mouseup', FLBuilder._blockDragCancel);
- /* Actions Lightbox */
- $('body').delegate('.fl-builder-actions .fl-builder-cancel-button', 'click', FLBuilder._cancelButtonClicked);
- /* Tools Actions */
- $('body').delegate('.fl-builder-save-user-template-button', 'click', FLBuilder._saveUserTemplateClicked);
- $('body').delegate('.fl-builder-duplicate-layout-button', 'click', FLBuilder._duplicateLayoutClicked);
- $('body').delegate('.fl-builder-layout-settings-button', 'click', FLBuilder._layoutSettingsClicked);
- $('body').delegate('.fl-builder-layout-settings .fl-builder-settings-save', 'click', FLBuilder._saveLayoutSettingsClicked);
- $('body').delegate('.fl-builder-layout-settings .fl-builder-settings-cancel', 'click', FLBuilder._cancelLayoutSettingsClicked);
- $('body').delegate('.fl-builder-global-settings-button', 'click', FLBuilder._globalSettingsClicked);
- $('body').delegate('.fl-builder-global-settings .fl-builder-settings-save', 'click', FLBuilder._saveGlobalSettingsClicked);
- $('body').delegate('.fl-builder-global-settings .fl-builder-settings-cancel', 'click', FLBuilder._cancelLayoutSettingsClicked);
- /* Template Panel Tab */
- $('body').delegate('.fl-user-template', 'click', FLBuilder._userTemplateClicked);
- $('body').delegate('.fl-user-template-edit', 'click', FLBuilder._editUserTemplateClicked);
- $('body').delegate('.fl-user-template-delete', 'click', FLBuilder._deleteUserTemplateClicked);
- $('body').delegate('.fl-builder-template-replace-button', 'click', FLBuilder._templateReplaceClicked);
- $('body').delegate('.fl-builder-template-append-button', 'click', FLBuilder._templateAppendClicked);
- $('body').delegate('.fl-builder-template-actions .fl-builder-cancel-button', 'click', FLBuilder._templateCancelClicked);
- /* User Template Settings */
- $('body').delegate('.fl-builder-user-template-settings .fl-builder-settings-save', 'click', FLBuilder._saveUserTemplateSettings);
- /* Help Actions */
- $('body').delegate('.fl-builder-help-tour-button', 'click', FLBuilder._startHelpTour);
- $('body').delegate('.fl-builder-knowledge-base-button', 'click', FLBuilder._viewKnowledgeBaseClicked);
- $('body').delegate('.fl-builder-forums-button', 'click', FLBuilder._visitForumsClicked);
- /* Welcome Actions */
- $('body').delegate('.fl-builder-no-tour-button', 'click', FLBuilder._noTourButtonClicked);
- $('body').delegate('.fl-builder-yes-tour-button', 'click', FLBuilder._yesTourButtonClicked);
- /* Alert Lightbox */
- $('body').delegate('.fl-builder-alert-close', 'click', FLBuilder._alertClose);
- /* General Overlays */
- $('body').delegate('.fl-block-overlay', 'contextmenu', FLBuilder._onContextmenu);
- /* Rows */
- $('body').delegate('.fl-row-overlay .fl-block-remove', 'click', FLBuilder._deleteRowClicked);
- $('body').delegate('.fl-row-overlay .fl-block-copy', 'click', FLBuilder._rowCopyClicked);
- $('body').delegate('.fl-row-overlay .fl-block-move', 'mousedown', FLBuilder._rowDragInit);
- $('body').delegate('.fl-row-overlay .fl-block-settings', 'click', FLBuilder._rowSettingsClicked);
- $('body').delegate('.fl-row-overlay', 'click', FLBuilder._rowSettingsClicked);
- $('body').delegate('.fl-builder-row-settings .fl-builder-settings-save', 'click', FLBuilder._saveSettings);
- /* Rows Submenu */
- $('body').delegate('.fl-block-col-submenu .fl-block-row-reset', 'click', FLBuilder._resetRowWidthClicked);
- /* Columns */
- $('body').delegate('.fl-col-overlay .fl-block-move', 'mousedown', FLBuilder._colDragInit);
- $('body').delegate('.fl-block-col-copy', 'click', FLBuilder._copyColClicked);
- $('body').delegate('.fl-col-overlay .fl-block-remove', 'click', FLBuilder._deleteColClicked);
- $('body').delegate('.fl-col-overlay .fl-block-settings', 'click', FLBuilder._colSettingsClicked);
- $('body').delegate('.fl-col-overlay', 'click', FLBuilder._colSettingsClicked);
- $('body').delegate('.fl-builder-col-settings .fl-builder-settings-save', 'click', FLBuilder._saveSettings);
- /* Columns Submenu */
- $('body').delegate('.fl-block-col-submenu .fl-block-col-move', 'mousedown', FLBuilder._colDragInit);
- $('body').delegate('.fl-block-col-submenu .fl-block-col-edit', 'click', FLBuilder._colSettingsClicked);
- $('body').delegate('.fl-block-col-submenu .fl-block-col-delete', 'click', FLBuilder._deleteColClicked);
- $('body').delegate('.fl-block-col-submenu .fl-block-col-reset', 'click', FLBuilder._resetColumnWidthsClicked);
- $('body').delegate('.fl-block-col-submenu li', 'mouseenter', FLBuilder._showColHighlightGuide);
- $('body').delegate('.fl-block-col-submenu li', 'mouseleave', FLBuilder._removeColHighlightGuides);
- /* Columns Submenu (Parent Column) */
- $('body').delegate('.fl-block-col-submenu .fl-block-col-move-parent', 'mousedown', FLBuilder._colDragInit);
- $('body').delegate('.fl-block-col-submenu .fl-block-col-edit-parent', 'click', FLBuilder._colSettingsClicked);
- /* Modules */
- $('body').delegate('.fl-module-overlay .fl-block-remove', 'click', FLBuilder._deleteModuleClicked);
- $('body').delegate('.fl-module-overlay .fl-block-copy', 'click', FLBuilder._moduleCopyClicked);
- $('body').delegate('.fl-module-overlay .fl-block-move', 'mousedown', FLBuilder._blockDragInit);
- $('body').delegate('.fl-module-overlay .fl-block-settings', 'click', FLBuilder._moduleSettingsClicked);
- $('body').delegate('.fl-module-overlay', 'click', FLBuilder._moduleSettingsClicked);
- $('body').delegate('.fl-builder-module-settings .fl-builder-settings-save', 'click', FLBuilder._saveModuleClicked);
- $('body').delegate('.fl-module-overlay .fl-block-col-settings', 'click', FLBuilder._colSettingsClicked);
- /* Node Templates */
- $('body').delegate('.fl-builder-settings-save-as', 'click', FLBuilder._showNodeTemplateSettings);
- $('body').delegate('.fl-builder-node-template-settings .fl-builder-settings-save', 'click', FLBuilder._saveNodeTemplate);
- /* Settings */
- $('body').delegate('.fl-builder-settings-tabs a', 'click', FLBuilder._settingsTabClicked);
- $('body').delegate('.fl-builder-settings-tabs a', 'show', FLBuilder._calculateSettingsTabsOverflow);
- $('body').delegate('.fl-builder-settings-tabs a', 'hide', FLBuilder._calculateSettingsTabsOverflow);
- $('body').delegate('.fl-builder-settings-cancel', 'click', FLBuilder._settingsCancelClicked);
- /* Settings Tabs Overflow menu */
- $('body').delegate('.fl-builder-settings-tabs-overflow-menu > a', 'click', FLBuilder._settingsTabsToOverflowMenuItemClicked.bind(this));
- $('body').delegate('.fl-builder-settings-tabs-more', 'click', FLBuilder._toggleTabsOverflowMenu.bind(this) );
- $('body').delegate('.fl-builder-settings-tabs-overflow-click-mask', 'click', FLBuilder._hideTabsOverflowMenu.bind(this));
- /* Tooltips */
- $('body').delegate('.fl-help-tooltip-icon', 'mouseover', FLBuilder._showHelpTooltip);
- $('body').delegate('.fl-help-tooltip-icon', 'mouseout', FLBuilder._hideHelpTooltip);
- /* Multiple Fields */
- $('body').delegate('.fl-builder-field-add', 'click', FLBuilder._addFieldClicked);
- $('body').delegate('.fl-builder-field-copy', 'click', FLBuilder._copyFieldClicked);
- $('body').delegate('.fl-builder-field-delete', 'click', FLBuilder._deleteFieldClicked);
- /* Select Fields */
- $('body').delegate('.fl-builder-settings-fields select', 'change', FLBuilder._settingsSelectChanged);
- /* Photo Fields */
- $('body').delegate('.fl-photo-field .fl-photo-select', 'click', FLBuilder._selectSinglePhoto);
- $('body').delegate('.fl-photo-field .fl-photo-edit', 'click', FLBuilder._selectSinglePhoto);
- $('body').delegate('.fl-photo-field .fl-photo-replace', 'click', FLBuilder._selectSinglePhoto);
- $('body').delegate('.fl-photo-field .fl-photo-remove', 'click', FLBuilder._singlePhotoRemoved);
- /* Multiple Photo Fields */
- $('body').delegate('.fl-multiple-photos-field .fl-multiple-photos-select', 'click', FLBuilder._selectMultiplePhotos);
- $('body').delegate('.fl-multiple-photos-field .fl-multiple-photos-edit', 'click', FLBuilder._selectMultiplePhotos);
- $('body').delegate('.fl-multiple-photos-field .fl-multiple-photos-add', 'click', FLBuilder._selectMultiplePhotos);
- /* Video Fields */
- $('body').delegate('.fl-video-field .fl-video-select', 'click', FLBuilder._selectSingleVideo);
- $('body').delegate('.fl-video-field .fl-video-replace', 'click', FLBuilder._selectSingleVideo);
- $('body').delegate('.fl-video-field .fl-video-remove', 'click', FLBuilder._singleVideoRemoved);
- /* Multiple Audio Fields */
- $('body').delegate('.fl-multiple-audios-field .fl-multiple-audios-select', 'click', FLBuilder._selectMultipleAudios);
- $('body').delegate('.fl-multiple-audios-field .fl-multiple-audios-edit', 'click', FLBuilder._selectMultipleAudios);
- $('body').delegate('.fl-multiple-audios-field .fl-multiple-audios-add', 'click', FLBuilder._selectMultipleAudios);
- /* Icon Fields */
- $('body').delegate('.fl-icon-field .fl-icon-select', 'click', FLBuilder._selectIcon);
- $('body').delegate('.fl-icon-field .fl-icon-replace', 'click', FLBuilder._selectIcon);
- $('body').delegate('.fl-icon-field .fl-icon-remove', 'click', FLBuilder._removeIcon);
- /* Settings Form Fields */
- $('body').delegate('.fl-form-field .fl-form-field-edit', 'click', FLBuilder._formFieldClicked);
- $('body').delegate('.fl-form-field-settings .fl-builder-settings-save', 'click', FLBuilder._saveFormFieldClicked);
- /* Layout Fields */
- $('body').delegate('.fl-layout-field-option', 'click', FLBuilder._layoutFieldClicked);
- /* Links Fields */
- $('body').delegate('.fl-link-field-select', 'click', FLBuilder._linkFieldSelectClicked);
- $('body').delegate('.fl-link-field-search-cancel', 'click', FLBuilder._linkFieldSelectCancelClicked);
- /* Loop Settings Fields */
- $('body').delegate('.fl-loop-data-source-select select[name=data_source]', 'change', FLBuilder._loopDataSourceChange);
- $('body').delegate('.fl-custom-query select[name=post_type]', 'change', FLBuilder._customQueryPostTypeChange);
- /* Text Fields - Add Predefined Value Selector */
- $('body').delegate('.fl-text-field-add-value', 'change', FLBuilder._textFieldAddValueSelectChange);
- /* Number Fields */
- $('body').delegate('.fl-field input[type=number]', 'focus', FLBuilder._onNumberFieldFocus );
- $('body').delegate('.fl-field input[type=number]', 'blur', 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').delegate('.fl-builder-content a', 'click', FLBuilder._preventDefault);
- },
- /**
- * Binds the events for overlays that appear when
- * mousing over a row, column or module.
- *
- * @since 1.0
- * @access private
- * @method _bindOverlayEvents
- */
- _bindOverlayEvents: function()
- {
- var content = $(FLBuilder._contentClass);
- content.delegate('.fl-row', 'mouseenter', FLBuilder._rowMouseenter);
- content.delegate('.fl-row', 'mouseleave', FLBuilder._rowMouseleave);
- content.delegate('.fl-row-overlay', 'mouseleave', FLBuilder._rowMouseleave);
- content.delegate('.fl-col', 'mouseenter', FLBuilder._colMouseenter);
- content.delegate('.fl-col', 'mouseleave', FLBuilder._colMouseleave);
- content.delegate('.fl-module', 'mouseenter', FLBuilder._moduleMouseenter);
- content.delegate('.fl-module', 'mouseleave', FLBuilder._moduleMouseleave);
- },
- /**
- * Unbinds the events for overlays that appear when
- * mousing over a row, column or module.
- *
- * @since 1.0
- * @access private
- * @method _destroyOverlayEvents
- */
- _destroyOverlayEvents: function()
- {
- var content = $(FLBuilder._contentClass);
- content.undelegate('.fl-row', 'mouseenter', FLBuilder._rowMouseenter);
- content.undelegate('.fl-row', 'mouseleave', FLBuilder._rowMouseleave);
- content.undelegate('.fl-row-overlay', 'mouseleave', FLBuilder._rowMouseleave);
- content.undelegate('.fl-col', 'mouseenter', FLBuilder._colMouseenter);
- content.undelegate('.fl-col', 'mouseleave', FLBuilder._colMouseleave);
- content.undelegate('.fl-module', 'mouseenter', FLBuilder._moduleMouseenter);
- content.undelegate('.fl-module', 'mouseleave', FLBuilder._moduleMouseleave);
- },
- /**
- * Hides overlays when the contextmenu event is fired on them.
- * This allows us to inspect the actual node in the console
- * instead of getting the overlay.
- *
- * @since 2.2
- * @access private
- * @method _onContextmenu
- * @param {Object} e The event object.
- */
- _onContextmenu: function( e )
- {
- $( this ).hide();
- },
- /**
- * 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').length > 0,
- colSettings = $('.fl-builder-col-settings').length > 0,
- moduleSettings = $('.fl-builder-module-settings').length > 0;
- if(rowSettings || colSettings || moduleSettings) {
- return FLBuilderStrings.unloadWarning;
- }
- },
- /* TipTips
- ----------------------------------------------------------*/
- /**
- * Initializes tooltip help messages.
- *
- * @since 1.1.9
- * @access private
- * @method _initTipTips
- */
- _initTipTips: function()
- {
- $('.fl-tip:not(.fl-has-tip)').each( function(){
- var ele = $( this );
- ele.addClass( 'fl-has-tip' );
- if ( undefined == ele.attr( 'data-title' ) ) {
- ele.attr( 'data-title', ele.attr( 'title' ) );
- }
- } ).tipTip( {
- defaultPosition : 'top',
- delay : 1000
- } );
- },
- /**
- * Removes all tooltip help messages from the screen.
- *
- * @since 1.1.9
- * @access private
- * @method _hideTipTips
- */
- _hideTipTips: function()
- {
- $('#tiptip_holder').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 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 )
- {
- 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 )
- {
- 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 ),
- submenu = parent.find( '.fl-builder-submenu' );
- if( parent.width() + parent.offset().left + submenu.width() > $( window ).width() ) {
- parent.addClass( 'fl-builder-submenu-right' );
- }
- },
- /**
- * Closes all open submenus.
- *
- * @since 1.9
- * @access private
- * @method _closeAllSubmenus
- */
- _closeAllSubmenus: function()
- {
- $( '.fl-builder-submenu-open' ).removeClass( 'fl-builder-submenu-open' );
- },
- /* Bar
- ----------------------------------------------------------*/
- /**
- * Opens a new window with the upgrade URL when the
- * upgrade button is clicked.
- *
- * @since 1.0
- * @access private
- * @method _upgradeClicked
- */
- _upgradeClicked: function()
- {
- window.open(FLBuilderConfig.upgradeUrl);
- },
- /**
- * 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');
- },
- /**
- * Opens or closes a section in the builder's content panel
- * when a section title is clicked.
- *
- * @since 1.0
- * @access private
- * @method _blockSectionTitleClicked
- */
- _blockSectionTitleClicked: function()
- {
- var title = $(this),
- section = title.parent();
- if(section.hasClass('fl-active')) {
- section.removeClass('fl-active');
- }
- else {
- $('.fl-builder-blocks-section').removeClass('fl-active');
- section.addClass('fl-active');
- }
- FLBuilder._initScrollbars();
- },
- /* Save Actions
- ----------------------------------------------------------*/
- /**
- * Publish the current layout
- *
- * @since 2.0
- * @access private
- * @method _publishLayout
- * @param bool whether or not builder should exit after publish
- * @return void
- */
- _publishLayout: function( shouldExit ) {
- // Save existing settings first if any exist. Don't proceed if it fails.
- if ( ! FLBuilder._triggerSettingsSave( false, true ) ) {
- return;
- }
- if ( _.isUndefined( shouldExit ) ) {
- var shouldExit = true;
- }
- FLBuilder.ajax( {
- action: 'save_layout'
- }, this._onPublishComplete.bind( this, shouldExit ) );
- },
- /**
- * 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' );
- },
- /**
- * Exits the builder when the save draft button is clicked.
- *
- * @since 1.0
- * @access private
- * @method _draftButtonClicked
- */
- _draftButtonClicked: function()
- {
- FLBuilder.showAjaxLoader();
- FLBuilder.ajax({
- action: 'save_draft'
- }, FLBuilder._exit);
- },
- /**
- * 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();
- FLBuilder.ajax({
- action: 'clear_draft_layout'
- }, FLBuilder._exit);
- } 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.location.href;
- if ( FLBuilderConfig.isUserTemplate && typeof window.opener != 'undefined' && window.opener ) {
- if ( typeof window.opener.FLBuilder != 'undefined' ) {
- if ( 'undefined' === typeof FLBuilderGlobalNodeId ) {
- window.opener.FLBuilder._updateLayout();
- } else {
- window.opener.FLBuilder._updateNode( FLBuilderGlobalNodeId );
- }
- }
- window.close();
- }
- else {
- if ( FLBuilder._exitUrl ) {
- href = FLBuilder._exitUrl;
- }
- else {
- href = href.replace( '?fl_builder&', '?' );
- href = href.replace( '?fl_builder', '' );
- href = href.replace( '&fl_builder', '' );
- }
- window.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.location.href;
- if ( FLBuilderConfig.isUserTemplate && typeof window.opener != 'undefined' && window.opener ) {
- if ( typeof window.opener.FLBuilder != 'undefined' ) {
- if ( 'undefined' === typeof FLBuilderGlobalNodeId ) {
- window.opener.FLBuilder._updateLayout();
- } else {
- window.opener.FLBuilder._updateNode( FLBuilderGlobalNodeId );
- }
- }
- window.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.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)' );
- 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' ),
- 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;
- FLBuilder.ajax( {
- action: 'save_layout_settings',
- settings: settings
- }, function() {
- FLBuilder.triggerHook( 'didSaveLayoutSettingsComplete', settings );
- FLBuilder._updateLayout();
- } );
- },
- /**
- * 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' );
- 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;
- },
- /* Global Settings
- ----------------------------------------------------------*/
- /**
- * Shows the global builder settings lightbox when the global
- * settings button is clicked.
- *
- * @since 1.0
- * @access private
- * @method _globalSettingsClicked
- */
- _globalSettingsClicked: function()
- {
- FLBuilderSettingsForms.render( {
- id : 'global',
- className : 'fl-builder-global-settings',
- settings : FLBuilderSettingsConfig.settings.global
- }, function() {
- FLBuilder._layoutSettingsInitCSS();
- } );
- },
- /**
- * 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;
- FLBuilder.ajax({
- action: 'save_global_settings',
- settings: settings
- }, FLBuilder._saveGlobalSettingsComplete);
- FLBuilder._lightbox.close();
- }
- },
- /**
- * Saves the global settings when the save button is clicked.
- *
- * @since 1.0
- * @access private
- * @method _saveGlobalSettingsComplete
- * @param {String} response
- */
- _saveGlobalSettingsComplete: function( response )
- {
- FLBuilderConfig.global = JSON.parse( response );
- FLBuilder.triggerHook( 'didSaveGlobalSettingsComplete', FLBuilderConfig.global );
- FLBuilder._updateLayout();
- },
- /* 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();
- if(type == 'core') {
- FLBuilder.ajax({
- action: 'apply_template',
- template_id: id,
- append: append
- }, FLBuilder._applyTemplateComplete);
- }
- else {
- FLBuilder.ajax({
- action: 'apply_user_template',
- template_id: id,
- append: append
- }, FLBuilder._applyUserTemplateComplete);
- }
- 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 = JSON.parse( 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 = JSON.parse( 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()
- {
- 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) {
- FLBuilder.ajax({
- action: 'save_user_template',
- settings: settings
- }, FLBuilder._saveUserTemplateSettingsComplete);
- 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 = JSON.parse(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.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 + ']' ),
- parent = null,
- index = null,
- i = null,
- item = null;
- if ( confirm( FLBuilderStrings.deleteTemplate ) ) {
- FLBuilder.ajax( {
- action: 'delete_user_template',
- template_id: 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 Actions
- ----------------------------------------------------------*/
- /**
- * Opens a new window with the knowledge base URL when the
- * view knowledge base button is clicked.
- *
- * @since 1.4.9
- * @access private
- * @method _viewKnowledgeBaseClicked
- */
- _viewKnowledgeBaseClicked: function()
- {
- window.open( FLBuilderConfig.help.knowledge_base_url );
- },
- /**
- * Opens a new window with the forums URL when the
- * visit forums button is clicked.
- *
- * @since 1.4.9
- * @access private
- * @method _visitForumsClicked
- */
- _visitForumsClicked: function()
- {
- window.open( FLBuilderConfig.help.forums_url );
- },
- /* 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();
- }
- }
- },
- /**
- * 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 );
- FLBuilder.ajax( {
- action : 'render_node',
- node_id : nodeId
- }, function( response ) {
- FLBuilder._renderLayout( JSON.parse( response ), callback );
- }.bind( this ) );
- },
- /**
- * 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();
- FLBuilder.ajax({
- action: 'render_layout'
- }, FLBuilder._renderLayout);
- },
- /**
- * 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 )
- {
- 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;
- }
- },
- /**
- * 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;
- 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 group drop targets to empty rows.
- for ( ; i < rows.length; i++ ) {
- row = rows.eq( i );
- if ( 0 === row.find( '.fl-col-group' ).length ) {
- row.find( '.fl-row-content' ).prepend( '<div class="fl-drop-target fl-col-group-drop-target"></div>' );
- }
- }
- // Add drop target to root parent column.
- 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>' );
- }
- // Loop through the column groups.
- 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>' );
- }
- },
- /**
- * 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._disableGlobalRows();
- FLBuilder._disableGlobalCols();
- FLBuilder._destroyOverlayEvents();
- FLBuilder._removeAllOverlays();
- FLBuilder._initSortables();
- $( 'body' ).addClass( 'fl-builder-dragging' );
- $( '.fl-builder-empty-message' ).hide();
- $( '.fl-sortable-disabled' ).removeClass( 'fl-sortable-disabled' );
- // 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;
- }
- // Find the placeholder title.
- if(parent.hasClass('fl-col-content')) {
- 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.attr('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 )
- {
- 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' ),
- 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' );
- // 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;
- }
- // 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._highlightEmptyCols();
- FLBuilder._enableGlobalRows();
- FLBuilder._enableGlobalCols();
- FLBuilder._setupEmptyLayout();
- $( 'body' ).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._highlightEmptyCols();
- FLBuilder._enableGlobalRows();
- FLBuilder._setupEmptyLayout();
- $( 'body' ).removeClass( 'fl-builder-dragging' );
- $( '.fl-node-drag-init' ).removeClass( 'fl-node-drag-init' );
- $( '.fl-node-dragging' ).removeClass( 'fl-node-dragging' );
- scrollTo( 0, FLBuilder._dragInitialScrollTop );
- }
- },
- /**
- * 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 )
- {
- FLBuilder.ajax( {
- action: 'reorder_node',
- node_id: nodeId,
- position: 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 )
- {
- FLBuilder.ajax({
- action: 'move_node',
- new_parent: newParent,
- node_id: nodeId,
- position: position
- });
- },
- /**
- * Removes all node overlays and hides any tooltip helpies.
- *
- * @since 1.0
- * @access private
- * @method _removeAllOverlays
- */
- _removeAllOverlays: function()
- {
- FLBuilder._removeRowOverlays();
- FLBuilder._removeColOverlays();
- FLBuilder._removeColHighlightGuides();
- FLBuilder._removeModuleOverlays();
- FLBuilder._hideTipTips();
- FLBuilder._closeAllSubmenus();
- },
- /**
- * Appends a node action overlay to the layout.
- *
- * @since 1.6.3.3
- * @access private
- * @method _appendOverlay
- * @param {Object} node A jQuery reference to the node this overlay is associated with.
- * @param {Object} template A rendered wp.template.
- * @return {Object} The overlay element.
- */
- _appendOverlay: function( node, template )
- {
- var overlayPos = 0,
- overlay = null,
- isRow = node.hasClass( 'fl-row' ),
- content = isRow ? node.find( '> .fl-row-content-wrap' ) : node.find( '> .fl-node-content' ),
- margins = {
- 'top' : parseInt( content.css( 'margin-top' ), 10 ),
- 'bottom' : parseInt( content.css( 'margin-bottom' ), 10 )
- };
- // Append the template.
- node.append( template );
- // Add the active class to the node.
- node.addClass( 'fl-block-overlay-active' );
- // Init TipTips
- FLBuilder._initTipTips();
- // Get a reference to the overlay.
- overlay = node.find( '> .fl-block-overlay' );
- // Adjust the overlay positions to account for negative margins.
- if ( margins.top < 0 ) {
- overlayPos = parseInt( overlay.css( 'top' ), 10 );
- overlayPos = isNaN( overlayPos ) ? 0 : overlayPos;
- overlay.css( 'top', ( margins.top + overlayPos ) + 'px' );
- }
- if ( margins.bottom < 0 ) {
- overlayPos = parseInt( overlay.css( 'bottom' ), 10 );
- overlayPos = isNaN( overlayPos ) ? 0 : overlayPos;
- overlay.css( 'bottom', ( margins.bottom + overlayPos ) + 'px' );
- }
- return overlay;
- },
- /**
- * Builds the overflow menu for an overlay if necessary.
- *
- * @since 1.9
- * @access private
- * @method _buildOverlayOverflowMenu
- * @param {Object} overlay The overlay object.
- */
- _buildOverlayOverflowMenu: function( overlay )
- {
- var header = overlay.find( '.fl-block-overlay-header' )
- actions = overlay.find( '.fl-block-overlay-actions' ),
- hasRules = overlay.find( '.fl-block-has-rules' ),
- original = actions.data( 'original' ),
- actionsWidth = 0,
- items = null,
- itemsWidth = 0,
- item = null,
- i = 0,
- visibleItems = [],
- overflowItems = [],
- menuData = [],
- template = wp.template( 'fl-overlay-overflow-menu' );
- // Use the original copy if we have one.
- if ( undefined != original ) {
- actions.after( original );
- actions.remove();
- actions = original;
- }
- // Save a copy of the original actions.
- actions.data( 'original', actions.clone() );
- // Get the actions width and items. Subtract any padding plus 2px (8px)
- actionsWidth = Math.floor(actions[0].getBoundingClientRect().width) - 8;
- items = actions.find( ' > i, > span.fl-builder-has-submenu' );
- // Add the width of the visibility rules indicator if there is one.
- if ( hasRules.length && actionsWidth + hasRules.outerWidth() > header.outerWidth() ) {
- itemsWidth += hasRules.outerWidth();
- }
- // Find visible and overflow items.
- for( ; i < items.length; i++ ) {
- item = items.eq( i );
- itemsWidth += Math.floor(item[0].getBoundingClientRect().width);
- if ( itemsWidth > actionsWidth ) {
- overflowItems.push( item );
- item.remove();
- }
- else {
- visibleItems.push( item );
- }
- }
- // Build the menu if we have overflow items.
- if ( overflowItems.length > 0 ) {
- if( visibleItems.length > 0 ) {
- overflowItems.unshift( visibleItems.pop().remove() );
- }
- for( i = 0; i < overflowItems.length; i++ ) {
- if ( overflowItems[ i ].is( '.fl-builder-has-submenu' ) ) {
- menuData.push( {
- type : 'submenu',
- label : overflowItems[ i ].find( '.fa, .fas, .far' ).data( 'title' ),
- submenu : overflowItems[ i ].find( '.fl-builder-submenu' )[0].outerHTML
- } );
- }
- else {
- menuData.push( {
- type : 'action',
- label : overflowItems[ i ].data( 'title' ),
- className : overflowItems[ i ].removeClass( function( i, c ) {
- return c.replace( /fl-block-([^\s]+)/, '' );
- } ).attr( 'class' )
- } );
- }
- }
- actions.append( template( menuData ) );
- FLBuilder._initTipTips();
- }
- },
- /* Rows
- ----------------------------------------------------------*/
- /**
- * Removes all row overlays from the page.
- *
- * @since 1.0
- * @access private
- * @method _removeRowOverlays
- */
- _removeRowOverlays: function()
- {
- $('.fl-row').removeClass('fl-block-overlay-active');
- $('.fl-row-overlay').remove();
- $('.fl-module').removeClass('fl-module-adjust-height');
- $('body').removeClass( 'fl-builder-row-resizing' );
- FLBuilder._closeAllSubmenus();
- },
- /**
- * Removes all row overlays from the page.
- *
- * @since 1.0
- * @access private
- * @method _removeRowOverlays
- */
- _disableGlobalRows: function()
- {
- if ( 'row' == FLBuilderConfig.userTemplateType ) {
- return;
- }
- $('.fl-row.fl-node-global').addClass( 'fl-node-disabled' );
- },
- /**
- * Removes all global column overlays from the page.
- *
- * @since 2.1
- * @access private
- * @method _disableGlobalCols
- */
- _disableGlobalCols: function()
- {
- if ( 'column' == FLBuilderConfig.userTemplateType ) {
- return;
- }
- $('.fl-row:not(.fl-node-global) .fl-col.fl-node-global').addClass( 'fl-node-disabled' );
- },
- /**
- * Removes all row overlays from the page.
- *
- * @since 1.0
- * @access private
- * @method _removeRowOverlays
- */
- _enableGlobalRows: function()
- {
- if ( 'row' == FLBuilderConfig.userTemplateType ) {
- return;
- }
- $( '.fl-node-disabled' ).removeClass( 'fl-node-disabled' );
- },
- /**
- * Re-enable global column from the page.
- *
- * @since 2.1
- * @access private
- * @method _enableGlobalCols
- */
- _enableGlobalCols: function()
- {
- if ( 'column' == FLBuilderConfig.userTemplateType ) {
- return;
- }
- $( '.fl-node-disabled' ).removeClass( 'fl-node-disabled' );
- },
- /**
- * Shows an overlay with actions when the mouse enters a row.
- *
- * @since 1.0
- * @access private
- * @method _rowMouseenter
- */
- _rowMouseenter: function()
- {
- var row = $( this ),
- rowTop = row.offset().top,
- childTop = null,
- overlay = null,
- template = wp.template( 'fl-row-overlay' );
- if ( row.closest( '.fl-builder-node-loading' ).length ) {
- return;
- }
- else if ( ! row.hasClass( 'fl-block-overlay-active' ) ) {
- // Append the overlay.
- overlay = FLBuilder._appendOverlay( row, template( {
- node : row.attr('data-node'),
- global : row.hasClass( 'fl-node-global' ),
- hasRules : row.hasClass( 'fl-node-has-rules' ),
- } ) );
- // Adjust the overlay position if covered by negative margin content.
- row.find( '.fl-node-content:visible' ).each( function(){
- var top = $( this ).offset().top;
- childTop = ( null === childTop || childTop > top ) ? top : childTop;
- } );
- if ( null !== childTop && childTop < rowTop ) {
- overlay.css( 'top', ( childTop - rowTop - 30 ) + 'px' );
- }
- // Put action headers on the bottom if they're hidden.
- if ( overlay.offset().top < 43 ) {
- overlay.addClass( 'fl-row-overlay-header-bottom' );
- }
- // Adjust the height of modules if needed.
- row.find( '.fl-module' ).each( function(){
- var module = $( this );
- if ( module.outerHeight( true ) < 20 ) {
- module.addClass( 'fl-module-adjust-height' );
- }
- } );
- // Build the overlay overflow menu if needed.
- FLBuilder._buildOverlayOverflowMenu( overlay );
- }
- },
- /**
- * Removes overlays when the mouse leaves a row.
- *
- * @since 1.0
- * @access private
- * @method _rowMouseleave
- * @param {Object} e The event object.
- */
- _rowMouseleave: function(e)
- {
- var toElement = $(e.toElement) || $(e.relatedTarget),
- isOverlay = toElement.hasClass('fl-row-overlay'),
- isOverlayChild = toElement.closest('.fl-row-overlay').length > 0,
- isTipTip = toElement.is('#tiptip_holder'),
- isTipTipChild = toElement.closest('#tiptip_holder').length > 0;
- if(isOverlay || isOverlayChild || isTipTip || isTipTipChild) {
- return;
- }
- FLBuilder._removeRowOverlays();
- },
- /**
- * 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' ),
- row = handle.closest( '.fl-row' );
- row.addClass( 'fl-node-dragging' );
- FLBuilder._blockDragInit( e );
- e.target = helper[ 0 ];
- helper.trigger( e );
- },
- /**
- * 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' );
- position = item.closest( '.fl-row' ).find( '.fl-row-content > .fl-col-group' ).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();
- // Show the module list.
- $( '.fl-builder-modules' ).siblings( '.fl-builder-blocks-section-title' ).eq( 0 ).trigger( 'click' );
- }
- // 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' ).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.
- */
- _addRow: function(cols, position)
- {
- FLBuilder._showNodeLoadingPlaceholder( $( FLBuilder._contentClass ), position );
- FLBuilder._newRowPosition = position;
- FLBuilder.ajax({
- action: 'render_new_row',
- cols: cols,
- position: position
- }, FLBuilder._addRowComplete);
- },
- /**
- * Adds the HTML for a new row to the layout when the AJAX
- * add operation is complete. Adds a module if one is queued
- * to go in the new row.
- *
- * @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 : JSON.parse(response),
- content = $(FLBuilder._contentClass),
- rowId = $(data.html).data('node'),
- module = FLBuilder._addModuleAfterNodeRender;
- // Add new row info to the data.
- data.nodeParent = content;
- data.nodePosition = FLBuilder._newRowPosition;
- // Render the layout.
- FLBuilder._renderLayout( data, function(){
- // Add a module to the newly created row.
- if(module !== null) {
- $('.fl-node-' + rowId + ' .fl-col-content').append(module);
- FLBuilder._reorderModule(module);
- FLBuilder._addModuleAfterNodeRender = null;
- }
- 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 row = $(this).closest('.fl-row'),
- result = null;
- if(!row.find('.fl-module').length) {
- FLBuilder._deleteRow(row);
- }
- else {
- result = confirm(FLBuilderStrings.deleteRowMessage);
- if(result) {
- FLBuilder._deleteRow(row);
- }
- }
- FLBuilder._removeAllOverlays();
- 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');
- FLBuilder.ajax({
- action: 'delete_node',
- node_id: nodeId
- });
- row.empty();
- row.remove();
- FLBuilder._setupEmptyLayout();
- FLBuilder._removeRowOverlays();
- FLBuilder.triggerHook( 'didDeleteRow', nodeId );
- },
- /**
- * Duplicates a row.
- *
- * @since 1.3.8
- * @access private
- * @method _rowCopyClicked
- * @param {Object} e The event object.
- */
- _rowCopyClicked: function(e)
- {
- var row = $( this ).closest( '.fl-row' ),
- nodeId = row.attr( 'data-node' ),
- position = $( FLBuilder._contentClass + ' > .fl-row' ).index( row ) + 1,
- clone = row.clone(),
- form = $( '.fl-builder-settings[data-node]' ),
- 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();
- row.after( clone );
- $( 'html, body' ).animate( {
- scrollTop: clone.offset().top - 75
- }, 500 );
- FLBuilder._showNodeLoading( nodeId + '-clone' );
- FLBuilder._newRowPosition = position;
- FLBuilder.ajax( {
- action: 'copy_row',
- node_id: nodeId,
- settings: settings,
- settings_id: formNodeId
- }, function( response ) {
- var data = JSON.parse( response );
- data.duplicatedRow = nodeId;
- FLBuilder._rowCopyComplete( data );
- } );
- e.stopPropagation();
- },
- /**
- * Callback for when a row has been duplicated.
- *
- * @since 1.7
- * @access private
- * @method _rowCopyComplete
- * @param {Object} data
- */
- _rowCopyComplete: function( data )
- {
- data.nodeParent = $( FLBuilder._contentClass );
- data.nodePosition = FLBuilder._newRowPosition;
- FLBuilder._renderLayout( data, function() {
- FLBuilder.triggerHook( 'didDuplicateRow', {
- newNodeId : data.nodeId,
- oldNodeId : data.duplicatedRow
- } );
- data.nodeParent.find( '.fl-builder-node-loading' ).eq( 0 ).remove();
- } );
- },
- /**
- * 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 = button.closest( '.fl-row' ).attr( 'data-node' ),
- global = button.closest( '.fl-block-overlay-global' ).length > 0,
- win = null;
- if ( global && 'row' != FLBuilderConfig.userTemplateType ) {
- if ( FLBuilderConfig.userCanEditGlobalTemplates ) {
- win = window.open( $( '.fl-row[data-node="' + nodeId + '"]' ).attr( 'data-template-url' ) );
- win.FLBuilderGlobalNodeId = nodeId;
- }
- }
- else if ( button.hasClass( 'fl-block-settings' ) ) {
- 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' ).on( 'change', FLBuilder._rowWidthChanged );
- $( '#fl-field-content_width select' ).on( 'change', FLBuilder._rowWidthChanged );
- } );
- }
- e.stopPropagation();
- },
- /**
- * 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' ).val(),
- contentWidth = $( '#fl-field-content_width select' ).val(),
- maxWidth = $( '#fl-field-max_content_width' );
- 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' );
- 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( '' );
- }
- FLBuilder.ajax({
- action : 'resize_row_content',
- node : nodeId,
- width : ''
- });
- 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 = 'row' == FLBuilderConfig.userTemplateType || 'column' == FLBuilderConfig.userTemplateType ? '' : ':not(.fl-node-global)',
- rows = $(FLBuilder._contentClass + ' .fl-row' + notGlobal),
- cols = $(FLBuilder._contentClass + ' .fl-col' + notGlobal);
- $( '.fl-row-highlight' ).removeClass('fl-row-highlight');
- cols.removeClass('fl-col-highlight').find('.fl-col-content').css( 'height', '' );
- cols.each(function(){
- var col = $(this);
- if(col.find('.fl-module, .fl-col').length === 0) {
- col.addClass('fl-col-highlight');
- }
- });
- },
- /**
- * Remove any column highlights
- *
- * @since 2.0
- * @access private
- * @method _removeEmptyColHighlights
- */
- _removeEmptyColHighlights: function() {
- $( '.fl-row-highlight' ).removeClass('fl-row-highlight');
- $( '.fl-col-highlight' ).removeClass('fl-col-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.closest( '.fl-row-overlay' ).length ) {
- $( FLBuilder._contentClass + ' .fl-col' + notGlobal ).addClass( 'fl-col-highlight' );
- }
- },
- /**
- * 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++ ) {
- FLBuilder._adjustColHeightForDrag( nested.eq( i ) );
- }
- for ( i = 0; i < notNested.length; i++ ) {
- 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.height( col.height() + 45 );
- }
- },
- /**
- * Adds a border guide to a column when the column
- * actions submenu is open for a module.
- *
- * @since 1.9
- * @access private
- * @method _showColHighlightGuide
- */
- _showColHighlightGuide: function()
- {
- var li = $( this ),
- link = li.find( 'a' ),
- col = li.closest( '.fl-col' ),
- parentCol = col.parents( '.fl-col' ),
- guide = $( '<div class="fl-col-highlight-guide"></div>' ),
- guideTop = null,
- overlayTop = li.closest( '.fl-block-overlay' ).offset().top;
- if ( link.hasClass( 'fl-block-col-move-parent' ) || link.hasClass( 'fl-block-col-edit-parent' ) ) {
- col = parentCol;
- }
- if ( col.hasClass( 'fl-col-highlight' ) ) {
- return;
- }
- col.find( '> .fl-col-content' ).append( guide );
- col.addClass( 'fl-col-has-highlight-guide' );
- guideTop = guide.offset().top;
- if ( guideTop > overlayTop ) {
- guide.css( 'top', ( overlayTop - guideTop + 4 ) + 'px' );
- }
- },
- /**
- * Removes all column highlight guides.
- *
- * @since 1.9
- * @access private
- * @method _showColHighlightGuide
- */
- _removeColHighlightGuides: function()
- {
- $( '.fl-col-has-highlight-guide' ).removeClass( 'fl-col-has-highlight-guide' );
- $( '.fl-col-highlight-guide' ).remove();
- },
- /**
- * Shows an overlay with actions when the mouse enters a column.
- *
- * @since 1.1.9
- * @access private
- * @method _colMouseenter
- */
- _colMouseenter: function()
- {
- var col = $( this ),
- group = col.closest( '.fl-col-group' ),
- groupLoading = group.hasClass( 'fl-col-group-has-child-loading' ),
- global = col.hasClass( 'fl-node-global' ),
- parentGlobal = col.parents( '.fl-node-global' ).length > 0,
- numCols = col.closest( '.fl-col-group' ).find( '> .fl-col' ).length,
- index = group.find( '> .fl-col' ).index( col ),
- first = 0 === index,
- last = numCols === index + 1,
- hasChildCols = col.find( '.fl-col' ).length > 0,
- hasModules = col.find('.fl-module').length > 0,
- parentCol = col.parents( '.fl-col' ),
- parentGroup = parentCol.closest( '.fl-col-group' ),
- hasParentCol = parentCol.length > 0,
- isColTemplate = 'undefined' !== typeof col.data('template-url'),
- isRootCol = 'column' == FLBuilderConfig.userTemplateType && ! hasParentCol;
- numParentCols = hasParentCol ? parentGroup.find( '> .fl-col' ).length : 0,
- parentIndex = parentGroup.find( '> .fl-col' ).index( parentCol ),
- parentFirst = hasParentCol ? 0 === parentIndex : false,
- parentLast = hasParentCol ? numParentCols === parentIndex + 1 : false,
- contentWidth = col.find( '> .fl-col-content' ).width(),
- row = col.closest('.fl-row'),
- rowIsFixedWidth = !! row.find('.fl-row-fixed-width').addBack('.fl-row-fixed-width').length,
- userCanResizeRows = FLBuilderConfig.rowResize.userCanResizeRows,
- hasRules = col.hasClass( 'fl-node-has-rules' ),
- template = wp.template( 'fl-col-overlay' ),
- overlay = null;
- if ( FLBuilderConfig.simpleUi ) {
- return;
- }
- else if ( global && parentGlobal && hasModules && ! isColTemplate ) {
- return;
- }
- else if ( global && 'column' == FLBuilderConfig.userTemplateType && hasModules ) {
- return;
- }
- else if ( ! global && col.find( '.fl-module' ).length > 0 ) {
- return;
- }
- else if ( col.find( '.fl-builder-node-loading-placeholder' ).length > 0 ) {
- return;
- }
- else if ( ! hasModules && hasChildCols ) {
- return;
- }
- else if ( parentGlobal && hasChildCols && ! isColTemplate ) {
- return;
- }
- else if ( col.closest( '.fl-builder-node-loading' ).length ) {
- return;
- }
- else if ( ! col.hasClass( 'fl-block-overlay-active' ) ) {
- // Remove existing overlays.
- FLBuilder._removeColOverlays();
- FLBuilder._removeModuleOverlays();
- // Append the template.
- overlay = FLBuilder._appendOverlay( col, template( {
- global : global,
- groupLoading : groupLoading,
- numCols : numCols,
- first : first,
- last : last,
- isRootCol : isRootCol,
- hasChildCols : hasChildCols,
- hasParentCol : hasParentCol,
- parentFirst : parentFirst,
- parentLast : parentLast,
- numParentCols : numParentCols,
- contentWidth : contentWidth,
- rowIsFixedWidth : rowIsFixedWidth,
- userCanResizeRows : userCanResizeRows,
- hasRules : hasRules,
- } ) );
- // Build the overlay overflow menu if needed.
- FLBuilder._buildOverlayOverflowMenu( overlay );
- // Init column resizing.
- FLBuilder._initColDragResizing();
- }
- $( 'body' ).addClass( 'fl-block-overlay-muted' );
- },
- /**
- * Removes overlays when the mouse leaves a column.
- *
- * @since 1.1.9
- * @access private
- * @method _colMouseleave
- * @param {Object} e The event object.
- */
- _colMouseleave: function(e)
- {
- var col = $(this),
- toElement = $(e.toElement) || $(e.relatedTarget),
- hasModules = col.find('.fl-module').length > 0,
- global = col.hasClass( 'fl-node-global' ),
- isColTemplate = 'undefined' !== typeof col.data('template-url'),
- isTipTip = toElement.is('#tiptip_holder'),
- isTipTipChild = toElement.closest('#tiptip_holder').length > 0;
- if( isTipTip || isTipTipChild ) {
- return;
- }
- if( hasModules && ! isColTemplate ) {
- return;
- }
- FLBuilder._removeColOverlays();
- FLBuilder._removeColHighlightGuides();
- FLBuilder._closeAllSubmenus();
- },
- /**
- * Removes all column overlays from the page.
- *
- * @since 1.6.4
- * @access private
- * @method _removeColOverlays
- */
- _removeColOverlays: function()
- {
- var cols = $( '.fl-col' );
- cols.removeClass('fl-block-overlay-active');
- cols.find('.fl-col-overlay').remove();
- $('body').removeClass('fl-block-overlay-muted');
- FLBuilder._closeAllSubmenus();
- },
- /**
- * 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' ),
- col = handle.closest( '.fl-col' );
- if ( handle.hasClass( 'fl-block-col-move-parent' ) ) {
- col = col.parents( '.fl-col' );
- }
- col.addClass( 'fl-node-dragging' );
- FLBuilder._blockDragInit( e );
- FLBuilder._removeColHighlightGuides();
- e.target = helper[ 0 ];
- helper.trigger( e );
- },
- /**
- * 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;
- // Cancel the drop if the sortable is disabled?
- 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 ) {
- 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.
- if ( oldGroupId == newGroupId ) {
- FLBuilder.ajax( {
- action: 'reorder_col',
- node_id: colId,
- position: col.index()
- } );
- }
- else {
- FLBuilder.ajax( {
- action: 'move_col',
- node_id: colId,
- new_parent: newGroupId,
- position: col.index(),
- resize: [ 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 ) {
- oldGroup.remove();
- }
- // Find the new group position.
- position = newRow.find( '.fl-row-content > .fl-col-group' ).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 ) {
- oldGroup.remove();
- }
- // Revert the proxy to its parent.
- $( '.fl-col-sortable-proxy' ).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'),
- content = col.find( '> .fl-col-content' ),
- hasSubmenu = button.parent().find( 'ul.fl-builder-submenu' ).length > 0,
- global = button.closest( '.fl-block-overlay-global' ).length > 0,
- isGlobalCol = button.closest( '.fl-block-overlay-global' ).hasClass( 'fl-col-overlay' ),
- isColTemplate = 'column' != FLBuilderConfig.userTemplateType && 'undefined' !== typeof col.attr( 'data-template-url' ),
- nodeId = null;
- if ( FLBuilder._colResizing ) {
- return;
- }
- if ( global && ! FLBuilderConfig.userCanEditGlobalTemplates ) {
- return;
- }
- if ( hasSubmenu && ! button.hasClass( 'fl-col-overlay') ) {
- return;
- }
- if ( button.hasClass( 'fl-block-col-edit-parent' ) ) {
- col = col.parents( '.fl-col' );
- }
- nodeId = col.attr('data-node');
- if ( global && isGlobalCol && isColTemplate ) {
- if ( FLBuilderConfig.userCanEditGlobalTemplates ) {
- win = window.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() {
- if ( col.siblings( '.fl-col' ).length === 0 ) {
- $( '#fl-builder-settings-section-general' ).hide();
- }
- else if( content.width() <= 40 ) {
- $( '#fl-field-size' ).hide();
- }
- } );
- }
- e.stopPropagation();
- },
- /**
- * 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 col = $( this ).closest( '.fl-col' ),
- nodeId = col.attr( 'data-node' ),
- clone = col.clone(),
- group = col.parent(),
- form = $( '.fl-builder-settings[data-node]' ),
- 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();
- col.after( clone );
- FLBuilder._showNodeLoading( nodeId + '-clone' );
- FLBuilder._newColParent = group;
- FLBuilder._newColPosition = col.index() + 1;
- FLBuilder._resetColumnWidths( group );
- FLBuilder.ajax( {
- action: 'copy_col',
- node_id: nodeId,
- settings: settings,
- settings_id: formNodeId
- }, function( response ){
- var data = JSON.parse( response );
- data.duplicatedColumn = nodeId;
- FLBuilder._copyColComplete( data );
- } );
- e.stopPropagation();
- },
- /**
- * Callback for when a column has been duplicated.
- *
- * @since 2.0
- * @access private
- * @method _copyColComplete
- * @param {Object} data
- */
- _copyColComplete: function( data )
- {
- data.nodeParent = FLBuilder._newColParent;
- data.nodePosition = FLBuilder._newColPosition;
- FLBuilder._renderLayout( data, function(){
- FLBuilder.triggerHook( 'didDuplicateColumn', {
- newNodeId : data.nodeId,
- oldNodeId : data.duplicatedColumn
- } );
- data.nodeParent.find( '.fl-builder-node-loading' ).eq( 0 ).remove();
- } );
- },
- /**
- * 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 button = $( this ),
- col = button.closest( '.fl-col' ),
- 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' ),
- result = true;
- if ( col.find( '.fl-module' ).length > 0 ) {
- result = confirm( FLBuilderStrings.deleteColumnMessage );
- }
- // 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;
- }
- }
- if ( result ) {
- FLBuilder._deleteCol( col );
- FLBuilder._removeAllOverlays();
- FLBuilder._highlightEmptyCols();
- }
- e.stopPropagation();
- },
- /**
- * 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');
- 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
- } );
- }
- FLBuilder.ajax({
- action : 'delete_col',
- node_id : nodeId,
- new_width : width
- });
- FLBuilder._initDropTargets();
- FLBuilder._initSortables();
- FLBuilder.triggerHook( 'didDeleteColumn', nodeId );
- }
- },
- /**
- * Inserts a column (or columns) before or after another column.
- *
- * @since 1.6.4
- * @access private
- * @method _addCols
- * @param {Object} 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.
- */
- _addCols: function( col, insert, type, nested )
- {
- var parent = col.closest( '.fl-col-group' ),
- position = parent.find( '.fl-col' ).index( col );
- type = typeof type == 'undefined' ? '1-col' : type;
- nested = typeof nested == 'undefined' ? false : nested;
- if ( 'after' == insert ) {
- position++;
- }
- FLBuilder._showNodeLoadingPlaceholder( parent, position );
- FLBuilder._removeAllOverlays();
- FLBuilder.ajax( {
- action : 'render_new_columns',
- node_id : col.attr('data-node'),
- insert : insert,
- type : type,
- nested : nested ? 1 : 0
- }, FLBuilder._addColsComplete );
- },
- /**
- * 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 : JSON.parse( response ),
- col = null,
- moduleData = FLBuilder._addModuleAfterNodeRender,
- module = null;
- data.nodeParent = FLBuilder._newColParent;
- data.nodePosition = FLBuilder._newColPosition;
- // Render the layout.
- FLBuilder._renderLayout( data, function() {
- // Add a module to a newly created column.
- if ( moduleData !== null ) {
- $( '.fl-module[data-node="' + moduleData.module.data( 'node' ) + '"]' ).remove()
- col = $( '.fl-col[data-node="' + moduleData.colId + '"]' );
- if ( 'after' == moduleData.position ) {
- col.next().find( '.fl-col-content' ).append( moduleData.module );
- }
- else {
- col.prev().find( '.fl-col-content' ).append( moduleData.module );
- }
- FLBuilder._reorderModule( moduleData.module );
- FLBuilder._addModuleAfterNodeRender = null;
- }
- // Remove the loading placeholder.
- 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.
- */
- _addColGroup: function(nodeId, cols, position)
- {
- 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 );
- // Send the request.
- FLBuilder.ajax({
- action : 'render_new_column_group',
- cols : cols,
- node_id : nodeId,
- position : position
- }, FLBuilder._addColGroupComplete);
- },
- /**
- * 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 = JSON.parse(response),
- html = $(data.html),
- groupId = html.data('node'),
- colId = html.find('.fl-col').data('node'),
- module = FLBuilder._addModuleAfterNodeRender;
- // 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' );
- }
- // Add a module to the newly created column group.
- if(module !== null) {
- $('.fl-node-' + colId + ' .fl-col-content').append(module);
- FLBuilder._reorderModule(module);
- FLBuilder._addModuleAfterNodeRender = null;
- }
- // 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' ),
- 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' );
- // 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,
- colWidth : parseFloat( col[ 0 ].style.width ) / 100,
- sibling : sibling,
- offset : ui.position.left,
- availWidth : availWidth,
- setting : setting,
- settingType : settingType
- };
- // 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;
- }
- // Set the feedback values.
- if ( directionRef == data.direction ) {
- data.feedbackLeft.html( colRound.toFixed( 1 ) + '%' ).show();
- data.feedbackRight.html( siblingRound.toFixed( 1 ) + '%' ).show();
- }
- else {
- data.feedbackLeft.html( siblingRound.toFixed( 1 ) + '%' ).show();
- data.feedbackRight.html( colRound.toFixed( 1 ) + '%' ).show();
- }
- // 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 ) );
- }
- }
- // 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.col.data( 'node' ),
- 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();
- // Save the resize data.
- FLBuilder.ajax({
- action : 'resize_cols',
- col_id : colId,
- col_width : colWidth,
- sibling_id : siblingId,
- sibling_width : 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' ),
- 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 ) );
- }
- FLBuilder.ajax({
- action : 'reset_col_widths',
- group_id : 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} e The event object.
- */
- _resetColumnWidths: function( group )
- {
- var cols = group.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
- ----------------------------------------------------------*/
- /**
- * Shows an overlay with actions when the mouse enters a module.
- *
- * @since 1.0
- * @access private
- * @method _moduleMouseenter
- */
- _moduleMouseenter: function()
- {
- var module = $( this ),
- moduleName = module.attr( 'data-name' ),
- global = module.hasClass( 'fl-node-global' ),
- parentGlobal = module.parents( '.fl-node-global' ).length > 0,
- group = module.parents( '.fl-col-group' ).last(),
- groupLoading = group.hasClass( 'fl-col-group-has-child-loading' ),
- numCols = module.closest( '.fl-col-group' ).find( '> .fl-col' ).length,
- col = module.closest( '.fl-col' ),
- colFirst = 0 === col.index(),
- colLast = numCols === col.index() + 1,
- parentCol = col.parents( '.fl-col' ),
- hasParentCol = parentCol.length > 0,
- numParentCols = hasParentCol ? parentCol.closest( '.fl-col-group' ).find( '> .fl-col' ).length : 0,
- parentFirst = hasParentCol ? 0 === parentCol.index() : false,
- parentLast = hasParentCol ? numParentCols === parentCol.index() + 1 : false,
- isRootCol = 'column' == FLBuilderConfig.userTemplateType && ! hasParentCol;
- contentWidth = col.find( '> .fl-col-content' ).width(),
- row = module.closest('.fl-row'),
- isGlobalRow = row.hasClass( 'fl-node-global' ),
- rowIsFixedWidth = !! row.find('.fl-row-fixed-width').addBack('.fl-row-fixed-width').length,
- userCanResizeRows = FLBuilderConfig.rowResize.userCanResizeRows,
- hasRules = module.hasClass( 'fl-node-has-rules' ),
- colHasRules = col.hasClass( 'fl-node-has-rules' ),
- template = wp.template( 'fl-module-overlay' ),
- overlay = null;
- // Remove existing overlays.
- FLBuilder._removeColOverlays();
- FLBuilder._removeModuleOverlays();
- if ( global && parentGlobal && 'row' != FLBuilderConfig.userTemplateType && isGlobalRow ) {
- return;
- }
- else if ( global && parentGlobal && 'column' != FLBuilderConfig.userTemplateType && ! isGlobalRow ) {
- return;
- }
- else if ( module.closest( '.fl-builder-node-loading' ).length ) {
- return;
- }
- else if ( module.find( '.fl-inline-editor:visible' ).length ) {
- return;
- }
- else if ( ! module.hasClass( 'fl-block-overlay-active' ) ) {
- // Append the template.
- overlay = FLBuilder._appendOverlay( module, template( {
- global : global,
- moduleName : moduleName,
- groupLoading : groupLoading,
- numCols : numCols,
- colFirst : colFirst,
- colLast : colLast,
- isRootCol : isRootCol,
- hasParentCol : hasParentCol,
- numParentCols : numParentCols,
- parentFirst : parentFirst,
- parentLast : parentLast,
- contentWidth : contentWidth,
- rowIsFixedWidth : rowIsFixedWidth,
- userCanResizeRows : userCanResizeRows,
- hasRules : hasRules,
- colHasRules : colHasRules,
- } ) );
- // Build the overlay overflow menu if necessary.
- FLBuilder._buildOverlayOverflowMenu( overlay );
- // Init column resizing.
- FLBuilder._initColDragResizing();
- }
- $( 'body' ).addClass( 'fl-block-overlay-muted' );
- },
- /**
- * Removes overlays when the mouse leaves a module.
- *
- * @since 1.0
- * @access private
- * @method _moduleMouseleave
- * @param {Object} e The event object.
- */
- _moduleMouseleave: function(e)
- {
- var module = $(this),
- toElement = $(e.toElement) || $(e.relatedTarget),
- isTipTip = toElement.is('#tiptip_holder'),
- isTipTipChild = toElement.closest('#tiptip_holder').length > 0;
- if(isTipTip || isTipTipChild) {
- return;
- }
- FLBuilder._removeModuleOverlays();
- FLBuilder._removeColHighlightGuides();
- },
- /**
- * Removes all module overlays from the page.
- *
- * @since 1.6.4
- * @access private
- * @method _removeModuleOverlays
- */
- _removeModuleOverlays: function()
- {
- var modules = $('.fl-module');
- modules.removeClass('fl-block-overlay-active');
- modules.find('.fl-module-overlay').remove();
- $('body').removeClass('fl-block-overlay-muted');
- FLBuilder._closeAllSubmenus();
- },
- /**
- * 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)
- {
- return $('<div class="fl-builder-block-drag-helper">' + item.attr('data-name') + '</div>');
- },
- /**
- * 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 )
- {
- $( ui.item ).data( 'original-position', ui.item.index() );
- 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 item = ui.item,
- parent = item.parent(),
- node = null,
- position = 0,
- parentId = 0;
- // 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' );
- position = parent.find( ' > .fl-col-group' ).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 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();
- }
- // Cancel the drop if the sortable is disabled?
- else if ( parent.hasClass( 'fl-sortable-disabled' ) ) {
- $( e.target ).append( ui.item );
- $( e.target ).children().eq( ui.item.data( 'original-position' ) ).before( ui.item );
- FLBuilder._highlightEmptyCols();
- return;
- }
- // 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;
- FLBuilder._addModuleAfterNodeRender = item;
- FLBuilder._addRow( '1-col', position );
- item.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;
- FLBuilder._addModuleAfterNodeRender = item;
- FLBuilder._addColGroup( item.closest( '.fl-row' ).attr( 'data-node' ), '1-col', position );
- item.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._addModuleAfterNodeRender = { module: item, colId: node.data( 'node' ), position: position };
- FLBuilder._addCols( node, position, '1-col', item.closest( '.fl-col-group-nested' ).length > 0 );
- item.remove();
- }
- // A module was dropped into another column.
- else {
- FLBuilder._reorderModule( item );
- }
- FLBuilder._resizeLayout();
- },
- /**
- * 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.closest('.fl-col').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 module = $(this).closest('.fl-module'),
- result = confirm(FLBuilderStrings.deleteModuleMessage);
- if(result) {
- FLBuilder._deleteModule(module);
- FLBuilder._removeAllOverlays();
- }
- 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'),
- nodeId = module.attr('data-node');
- FLBuilder.ajax({
- action: 'delete_node',
- node_id: nodeId
- });
- module.empty();
- module.remove();
- row.removeClass('fl-block-overlay-muted');
- FLBuilder._highlightEmptyCols();
- FLBuilder._removeAllOverlays();
- FLBuilder.triggerHook( 'didDeleteModule', nodeId );
- },
- /**
- * Duplicates a module.
- *
- * @since 1.0
- * @access private
- * @method _moduleCopyClicked
- * @param {Object} e The event object.
- */
- _moduleCopyClicked: function(e)
- {
- var module = $( this ).closest( '.fl-module' ),
- nodeId = module.attr( 'data-node' ),
- parent = module.parent(),
- position = parent.find( ' > .fl-col-group, > .fl-module' ).index( module ) + 1,
- clone = module.clone(),
- form = $( '.fl-builder-module-settings[data-node=' + nodeId + ']' ),
- settings = null;
- if ( form.length ) {
- settings = FLBuilder._getSettings( form );
- FLBuilderSettingsConfig.nodes[ nodeId ] = settings;
- }
- clone.addClass( 'fl-node-' + nodeId + '-clone fl-builder-node-clone' );
- clone.find( '.fl-block-overlay' ).remove();
- module.after( clone );
- $( 'html, body' ).animate( {
- scrollTop: clone.offset().top - 75
- }, 500 );
- FLBuilder._showNodeLoading( nodeId + '-clone' );
- FLBuilder._newModuleParent = parent;
- FLBuilder._newModulePosition = position;
- FLBuilder.ajax({
- action: 'copy_module',
- node_id: nodeId,
- settings: settings
- }, function( response ) {
- var data = JSON.parse( response );
- data.duplicatedModule = nodeId;
- FLBuilder._moduleCopyComplete( data );
- } );
- e.stopPropagation();
- },
- /**
- * Callback for when a module has been duplicated.
- *
- * @since 1.7
- * @access private
- * @method _moduleCopyComplete
- * @param {Object}
- */
- _moduleCopyComplete: function( data )
- {
- data.nodeParent = FLBuilder._newModuleParent;
- data.nodePosition = FLBuilder._newModulePosition;
- FLBuilder._renderLayout( data, function(){
- FLBuilder.triggerHook( 'didDuplicateModule', {
- newNodeId : data.nodeId,
- oldNodeId : data.duplicatedModule
- } );
- data.nodeParent.find( '.fl-builder-node-loading' ).eq( 0 ).remove();
- } );
- },
- /**
- * 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 ),
- 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;
- }
- FLBuilder._showModuleSettings( {
- type : type,
- nodeId : nodeId,
- parentId : parentId,
- global : global
- } );
- },
- /**
- * 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' ),
- layout = null;
- // 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,
- preview : {
- type : 'module',
- layout : data.layout,
- callback : function() {
- FLBuilder.triggerHook( 'didAddModule', data.nodeId );
- }
- }
- }, 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;
- }
- // Send the request.
- FLBuilder.ajax( {
- action : 'render_new_module',
- parent_id : parentId,
- type : type,
- position : position,
- node_preview : 1,
- widget : typeof widget === 'undefined' ? '' : widget,
- alias : typeof alias === 'undefined' ? '' : alias
- }, FLBuilder._addModuleComplete );
- },
- /**
- * 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 = JSON.parse( response );
- // Setup a preview layout if we have one.
- if ( data.layout ) {
- 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' ).length ) {
- if ( data.layout ) {
- FLBuilder._renderLayout( data.layout );
- }
- } else {
- FLBuilder._showModuleSettings( data, function() {
- $( '.fl-builder-module-settings' ).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 = {
- rules: {},
- init: function(){},
- submit: function(){ return true; },
- preview: function(){}
- };
- 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);
- },
- /* 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' ),
- 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() {
- if ( ! FLBuilderConfig.userCanEditGlobalTemplates ) {
- $( '#fl-field-global' ).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' ),
- nodeId = form.attr( 'data-node' ),
- valid = form.validate().form();
- if ( valid ) {
- FLBuilder._showNodeLoading( nodeId );
- FLBuilder.ajax({
- action : 'save_node_template',
- node_id : nodeId,
- settings : FLBuilder._getSettings( form )
- }, function( response ) {
- FLBuilder._saveNodeTemplateComplete( response );
- FLBuilder._hideNodeLoading( nodeId );
- } );
- 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 = JSON.parse( response ),
- panel = $( '.fl-builder-saved-' + data.type + 's' ),
- 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';
- 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 > .fl-col-group' ).index( item.closest( '.fl-col-group' ) );
- }
- // 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' ).index( item.closest( '.fl-col-group' ) );
- }
- // 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 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 );
- }
- // Apply and render the node template.
- FLBuilder.ajax({
- action : action,
- template_id : item.attr( 'data-id' ),
- template_type : item.attr( 'data-type' ),
- parent_id : parentId,
- position : position
- }, function( response ) {
- if ( action.indexOf( 'row' ) > -1 ) {
- var data = JSON.parse( response );
- FLBuilder.triggerHook( 'didApplyRowTemplateComplete', data.config );
- callback( data.layout );
- } else if ( action.indexOf( 'col' ) > -1 ) {
- var data = JSON.parse( response );
- FLBuilder.triggerHook( 'didApplyColTemplateComplete', data.config );
- callback( data.layout );
- } else {
- callback( response );
- }
- } );
- // 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.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' ),
- callback = global ? FLBuilder._updateLayout : undefined,
- message = global ? FLBuilderStrings.deleteGlobalTemplate : FLBuilderStrings.deleteTemplate,
- index = null;
- 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.
- FLBuilder.ajax({
- action : 'delete_node_template',
- template_id : block.attr( 'data-id' )
- }, callback);
- // 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._initCodeFields();
- FLBuilder._initColorPickers();
- FLBuilder._initSelectFields();
- FLBuilder._initEditorFields();
- FLBuilder._initMultipleFields();
- FLBuilder._initAutoSuggestFields();
- FLBuilder._initLinkFields();
- FLBuilder._initFontFields();
- FLBuilder._initOrderingFields();
- FLBuilder._initTimezoneFields();
- FLBuilder._focusFirstSettingsControl();
- FLBuilder._lightbox._resizeEditors();
- $( '.fl-builder-settings-fields' ).css( 'visibility', 'visible' );
- /**
- * 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' ).remove();
- $( 'body' ).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' );
- FLBuilder._focusFirstSettingsControl();
- e.preventDefault();
- },
- _resetSettingsTabsState: function() {
- var $lightbox = $('.fl-lightbox:visible');
- 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'),
- 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 - 60, /* 60 is size of "more" btn */
- tabsWidthTotal = 0,
- tabPadding = isSlim ? ( 8 * 2 ) : ( 15 * 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),
- 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'),
- $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');
- $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');
- $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();
- },
- /**
- * 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]' ),
- moduleSettings = $('.fl-builder-module-settings'),
- existingNodes = null,
- previewModule = null,
- previewCol = null,
- existingCol = null;
- // 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'));
- previewCol = previewModule.closest('.fl-col');
- existingCol = existingNodes.find('.fl-node-' + previewCol.data('node'));
- if(existingCol.length > 0) {
- FLBuilder._deleteModule(previewModule);
- }
- else {
- FLBuilder._deleteCol(previewCol);
- }
- }
- // Do a standard preview revert.
- else if( FLBuilder.preview ) {
- FLBuilder.preview.revert();
- }
- FLBuilder.preview = null;
- FLLightbox.closeParent(this);
- },
- /**
- * Focus the first visible control in a settings panel
- *
- * @since 2.0
- */
- _focusFirstSettingsControl: function() {
- var form = $( '.fl-builder-settings:visible' ),
- 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 tinyMCE && input.hasClass('fl-editor-field') ) {
- // TinyMCE fields
- var id = input.find('textarea.wp-editor-area').attr('id');
- 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').last();
- 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'),
- 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 = '',
- name = '',
- key = '',
- keys = [],
- matches = [],
- 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 text editor textareas.
- if ( data[ i ].name.indexOf( 'flrich' ) > -1 ) {
- continue;
- }
- // Support foo[]... setting keys.
- else if ( data[ i ].name.indexOf( '[' ) > -1 ) {
- name = data[ i ].name.replace( /\[(.*)\]/, '' );
- key = data[ i ].name.replace( name, '' );
- keys = [];
- matches = key.match( /\[[^\]]*\]/g );
- // Remove [] from the keys.
- for ( k = 0; k < matches.length; k++ ) {
- if ( '[]' == matches[ k ] ) {
- continue;
- }
- keys.push( matches[ k ].replace( /\[|\]/g, '' ) );
- }
- // foo[][key][key]
- if ( key.match( /\[\]\[[^\]]*\]\[[^\]]+\]/ ) ) {
- if ( 'undefined' == typeof settings[ name ] ) {
- settings[ name ] = {};
- }
- if ( 'undefined' == typeof settings[ name ][ keys[ 0 ] ] ) {
- settings[ name ][ keys[ 0 ] ] = {};
- }
- if ( 'undefined' == typeof settings[ name ][ keys[ 0 ] ][ keys[ 1 ] ] ) {
- settings[ name ][ keys[ 0 ] ][ keys[ 1 ] ] = {};
- }
- settings[ name ][ keys[ 0 ] ][ keys[ 1 ] ] = value;
- }
- // foo[][key][]
- else if ( key.match( /\[\]\[[^\]]*\]\[\]/ ) ) {
- if ( 'undefined' == typeof settings[ name ] ) {
- settings[ name ] = {};
- }
- if ( 'undefined' == typeof settings[ name ][ keys[ 0 ] ] ) {
- settings[ name ][ keys[ 0 ] ] = [];
- }
- settings[ name ][ keys[ 0 ] ].push( value );
- }
- // foo[][key]
- else if ( key.match( /\[\]\[[^\]]*\]/ ) ) {
- if ( 'undefined' == typeof settings[ name ] ) {
- settings[ name ] = {};
- }
- settings[ name ][ keys[ 0 ] ] = value;
- }
- // foo[]
- else if ( key.match( /\[\]/ ) ) {
- if ( 'undefined' == typeof settings[ name ] ) {
- settings[ name ] = [];
- }
- settings[ name ].push( value );
- }
- }
- // 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 ) ) {
- 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 = JSON.parse( formJSON.val().replace( /'/g, "'" ) );
- }
- if ( original ) {
- for ( key in original ) {
- if ( $( '#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' ),
- newModule = form.data( 'new-module' ),
- nodeId = form.attr( 'data-node' ),
- settings = FLBuilder._getSettings( form ),
- preview = FLBuilder.preview;
- // Default to true for render.
- if ( _.isUndefined( render ) || ! _.isBoolean( render ) ) {
- render = true;
- }
- // Only proceed if the settings have changed.
- if ( preview && ! preview._settingsHaveChanged() && _.isUndefined( newModule ) ) {
- FLBuilder._lightbox.close();
- return;
- }
- // Show the loader.
- FLBuilder._showNodeLoading( nodeId );
- // Update the settings config object.
- FLBuilderSettingsConfig.nodes[ nodeId ] = settings;
- // Make the AJAX call.
- FLBuilder.ajax( {
- action : 'save_settings',
- node_id : nodeId,
- settings : settings
- }, FLBuilder._saveSettingsComplete.bind( this, render, preview ) );
- // Trigger the hook.
- FLBuilder.triggerHook( 'didSaveNodeSettings', {
- nodeId : nodeId,
- settings : settings
- } );
- // Close the lightbox.
- FLBuilder._lightbox.close();
- },
- /**
- * 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 {Object} preview The preview object for this settings save.
- * @param {String} response The layout data from the server.
- */
- _saveSettingsComplete: function( render, preview, response )
- {
- var data = JSON.parse( response ),
- callback = function() {
- if( preview && data.layout.partial && data.layout.nodeId === preview.nodeId ) {
- preview.clear();
- preview = null;
- }
- };
- if ( true === render ) {
- FLBuilder._renderLayout( data.layout, callback );
- } else {
- callback();
- }
- FLBuilder.triggerHook( 'didSaveNodeSettingsComplete', {
- nodeId : data.node_id,
- 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' ),
- changed = false,
- valid = true;
- disableClose = _.isUndefined( disableClose ) ? false : disableClose;
- showAlert = _.isUndefined( showAlert ) ? false : showAlert;
- destroy = _.isUndefined( destroy ) ? true : destroy;
- 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' ).length ) {
- valid = false;
- }
- }
- // Do a validation check of the main form to see if we should save.
- if ( valid && ! form.validate().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' ).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;
- }
- }
- // Close the main lightbox if it doesn't have changes and closing isn't disabled.
- if ( ! changed && ! disableClose ) {
- lightbox.close();
- }
- }
- if ( ! valid ) {
- FLBuilder.triggerHook( 'didFailSettingsSave' );
- FLBuilder._toggleSettingsTabErrors();
- if ( showAlert && ! $( '.fl-builder-alert-lightbox:visible' ).length ) {
- FLBuilder.alert( FLBuilderStrings.settingsHaveErrors );
- }
- }
- 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' ),
- 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' ),
- 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 + '"]' ),
- parentBox = parentBoxWrap.find( '.fl-lightbox' ),
- parentBoxForm = parentBoxWrap.find('form'),
- parentBoxObj = FLLightbox._instances[ parentBoxId ];
- nestedBoxObj.on( 'close', function() {
- parentBox.attr( 'style', nestedBox.attr( 'style' ) );
- parentBoxWrap.show();
- parentBoxObj._resize();
- parentBoxWrap.find( 'label.error' ).remove();
- parentBoxForm.validate().hideErrors();
- FLBuilder._toggleSettingsTabErrors();
- } );
- 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();
- },
- /* 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'),
- 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 = JSON.parse( response );
- for ( name in values ) {
- $( '.fl-suggest-field[name="' + name + '"]' ).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')
- }, 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)
- {
- $(this).siblings('.as-values').val(selections.join(',')).trigger('change');
- },
- /* Code Fields
- ----------------------------------------------------------*/
- /**
- * Initializes all code fields in a settings form.
- *
- * @since 2.0
- * @access private
- * @method _initCodeFields
- */
- _initCodeFields: function()
- {
- $( '.fl-builder-settings:visible' ).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;
- editDiv.insertBefore( textarea );
- 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;
- }
- }
- if ( hasError && ! errorBtn.length && FLBuilderConfig.CheckCodeErrors ) {
- saveBtn.addClass( 'fl-builder-settings-error' );
- saveBtn.on( 'click', FLBuilder._showCodeFieldError );
- } else if ( ! hasError && errorBtn.length ) {
- errorBtn.removeClass( 'fl-builder-settings-error' );
- errorBtn.off( 'click', FLBuilder._showCodeFieldError );
- }
- });
- 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' );
- saveBtn.removeClass( 'fl-builder-settings-error' );
- saveBtn.off( 'click', FLBuilder._showCodeFieldError );
- saveBtn.trigger( 'click' );
- },
- strings: {
- ok: FLBuilderStrings.codeErrorFix,
- cancel: FLBuilderStrings.codeErrorIgnore
- }
- } );
- },
- /* Multiple Fields
- ----------------------------------------------------------*/
- /**
- * Initializes all multiple fields in a settings form.
- *
- * @since 1.0
- * @access private
- * @method _initMultipleFields
- */
- _initMultipleFields: function()
- {
- var multiples = $('.fl-builder-settings:visible .fl-builder-field-multiples'),
- multiple = null,
- fields = null,
- i = 0,
- cursorAt = FLBuilderConfig.isRtl ? { left: 10 } : { right: 10 };
- 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').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"
- });
- },
- /**
- * 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,
- 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('');
- 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 ] ) );
- }
- },
- /**
- * 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();
- 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(),
- result = confirm(FLBuilderStrings.deleteFieldMessage);
- if(result) {
- row.remove();
- FLBuilder._renumberFields(parent);
- FLBuilder._initMultipleFields();
- 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'),
- i = 0;
- for( ; i < rows.length; i++) {
- rows.eq(i).find('th label span.fl-builder-field-index').html(i + 1);
- }
- },
- /**
- * 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());
- FLBuilder.preview.delayPreview();
- },
- /* Select Fields
- ----------------------------------------------------------*/
- /**
- * Initializes select fields for a settings form.
- *
- * @since 1.0
- * @access private
- * @method _initSelectFields
- */
- _initSelectFields: function()
- {
- $('.fl-builder-settings:visible').find('.fl-builder-settings-fields select').trigger('change');
- },
- /**
- * 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()
- {
- var select = $(this),
- toggle = select.attr('data-toggle'),
- hide = select.attr('data-hide'),
- trigger = select.attr('data-trigger'),
- val = select.val(),
- i = 0,
- k = 0;
- // TOGGLE sections, fields or tabs.
- if(typeof toggle !== 'undefined') {
- toggle = JSON.parse(toggle);
- for(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-', ']');
- }
- 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-', ']');
- }
- }
- // HIDE sections, fields or tabs.
- if(typeof hide !== 'undefined') {
- hide = JSON.parse(hide);
- 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 = JSON.parse(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');
- }
- }
- }
- }
- FLBuilder._calculateSettingsTabsOverflow();
- },
- /**
- * @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').find(prefix + inputArray[i] + suffix)[func]();
- // Resize code editor fields.
- $( prefix + inputArray[i] + suffix ).parent().find( '.fl-field[data-type="code"]' ).each( function() {
- $( this ).data( 'editor' ).resize();
- } );
- }
- }
- },
- /* Color Pickers
- ----------------------------------------------------------*/
- /**
- * Initializes color picker fields for a settings form.
- *
- * @since 1.0
- * @access private
- * @method _initColorPickers
- */
- _initColorPickers: function()
- {
- var colorPresets = FLBuilderConfig.colorPresets ? FLBuilderConfig.colorPresets : [];
- FLBuilder.colorPicker = new FLBuilderColorPicker({
- mode: 'hsv',
- elements: '.fl-color-picker .fl-color-picker-value',
- presets: colorPresets,
- 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', function( event, data ) {
- FLBuilder.ajax({
- action: 'save_color_presets',
- presets: data.presets
- });
- });
- },
- /* Single Photo Fields
- ----------------------------------------------------------*/
- /**
- * 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 : 'image' },
- multiple: false
- });
- FLBuilder._singlePhotoSelector.on( 'open', FLBuilder._wpmedia_reset_errors );
- _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');
- 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.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,
- title = '',
- 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();
- }
- for(size in photo.sizes) {
- if ( 'undefined' != typeof titles[ size ] ) {
- title = titles[ size ] + ' - ';
- }
- else if ( 'undefined' != typeof FLBuilderConfig.customImageSizeTitles[ size ] ) {
- title = FLBuilderConfig.customImageSizeTitles[ size ] + ' - ';
- }
- else {
- title = '';
- }
- selected = '';
- if ( ! selectedSize ) {
- selected = size == 'full' ? ' selected="selected"' : '';
- } else if( selectedSize === photo.sizes[ size ].url.split(/[\\/]/).pop() ) {
- selected = ' selected="selected"';
- }
- html += '<option value="' + photo.sizes[size].url + '"' + selected + '>' + title + photo.sizes[size].width + ' x ' + photo.sizes[size].height + '</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 === '' ? '' : JSON.parse(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();
- }
- // 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) {
- FLBuilder._singleVideoSelector = wp.media({
- title: FLBuilderStrings.selectVideo,
- button: { text: FLBuilderStrings.selectVideo },
- library : { type : 'video' },
- multiple: false
- });
- FLBuilder._singleVideoSelector.on( 'open', FLBuilder._wpmedia_reset_errors );
- _wpPluploadSettings['defaults']['multipart_params']['fl_upload_type']= 'video';
- }
- },
- /**
- * 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="'+ JSON.parse(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
- ----------------------------------------------------------*/
- /**
- * 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 ] );
- }
- FLBuilderSettingsForms.render( {
- id : type,
- nodeId : form.attr( 'data-node' ),
- nodeSettings : FLBuilder._getSettings( form ),
- settings : JSON.parse( settings.replace( /'/g, "'" ) ),
- lightbox : lightbox,
- helper : helper,
- rules : helper ? helper.rules : null
- }, function() {
- link.attr( 'id', 'fl-' + lightbox._node.attr( 'data-instance-id' ) );
- lightbox._node.find( 'form.fl-builder-settings' ).attr( 'data-type', type );
- } );
- },
- /**
- * 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'),
- lightboxId = $(this).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),
- preview = link.parent().attr('data-preview-text'),
- previewField = form.find( '#fl-field-' + preview ),
- previewText = settings[preview],
- selectPreview = $( 'select[name="' + preview + '"]' ),
- tmp = document.createElement('div'),
- valid = true;
- if ( selectPreview.length > 0 ) {
- previewText = selectPreview.find( 'option[value="' + settings[ preview ] + '"]' ).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") + '...';
- }
- link.siblings('.fl-form-field-preview-text').html(previewText);
- }
- oldSettings = link.siblings('input').val().replace(/'/g, "'");
- if ( '' != oldSettings ) {
- settings = $.extend( JSON.parse( oldSettings ), settings );
- }
- link.siblings('input').val(JSON.stringify(settings)).trigger('change');
- FLBuilder._closeNestedSettings();
- return true;
- }
- else {
- FLBuilder._toggleSettingsTabErrors();
- 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').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');
- 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
- });
- },
- /**
- * 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();
- },
- /* 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').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' );
- font.on( 'change', function(){
- FLBuilder._getFontWeights( font );
- } );
- if ( value.indexOf( 'family' ) > -1 ) {
- value = JSON.parse( value );
- 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.next( '.fl-font-field-weight' ),
- font = currentFont.val(),
- weightMap = {
- 'default' : 'Default',
- 'regular' : 'Regular',
- '100': 'Thin 100',
- '200': 'Extra-Light 200',
- '300': 'Light 300',
- '400': 'Normal 400',
- '500': 'Medium 500',
- '600': 'Semi-Bold 600',
- '700': 'Bold 700',
- '800': 'Extra-Bold 800',
- '900': 'Ultra-Bold 900'
- },
- weights = {};
- selectWeight.html('');
- 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 ){
- selectWeight.append( '<option value="' + value + '">' + weightMap[ value ] + '</option>' );
- } );
- },
- /* Editor Fields
- ----------------------------------------------------------*/
- /**
- * InitializeS TinyMCE when the builder is first loaded.
- *
- * @since 2.0
- * @access private
- * @method _initEditorFields
- */
- _initTinyMCE: function()
- {
- if ( tinymce.ui.FloatPanel ) {
- tinymce.ui.FloatPanel.zIndex = 100100; // Fix zIndex issue in wp 4.8.1
- }
- $( '.fl-builder-hidden-editor' ).each( FLBuilder._initEditorField );
- },
- /**
- * Initialize all TinyMCE editor fields.
- *
- * @since 1.10
- * @access private
- * @method _initEditorFields
- */
- _initEditorFields: function()
- {
- $( '.fl-builder-settings:visible .fl-editor-field' ).each( FLBuilder._initEditorField );
- },
- /**
- * Initialize a single TinyMCE editor field.
- *
- * @since 2.0
- * @method _initEditorField
- */
- _initEditorField: function()
- {
- var field = $( this ),
- textarea = field.find( 'textarea' ),
- name = field.attr( 'data-name' ),
- editorId = 'flrich' + new Date().getTime() + '_' + name,
- html = FLBuilderConfig.wp_editor,
- config = tinyMCEPreInit,
- buttons = Number( field.attr( 'data-buttons' ) ),
- rows = field.attr( 'data-rows' ),
- init = null,
- wrap = null;
- html = html.replace( /flbuildereditor/g , editorId );
- config = JSON.parse( JSON.stringify( config ).replace( /flbuildereditor/g , editorId ) );
- textarea.after( html ).remove();
- $( 'textarea#' + editorId ).val( textarea.val() )
- if ( undefined !== typeof tinymce && undefined !== config.mceInit[ editorId ] ) {
- init = config.mceInit[ editorId ];
- wrap = tinymce.$( '#wp-' + editorId + '-wrap' );
- wrap.find( 'textarea' ).attr( 'rows', rows );
- if ( ! buttons ) {
- wrap.find( '.wp-media-buttons' ).remove();
- }
- if ( ( wrap.hasClass( 'tmce-active' ) || ! config.qtInit.hasOwnProperty( editorId ) ) && ! init.wp_skip_init ) {
- tinymce.init( init );
- }
- }
- if ( undefined !== typeof quicktags ) {
- quicktags( config.qtInit[ editorId ] );
- }
- window.wpActiveEditor = editorId;
- },
- /**
- * Reinitialize all TinyMCE editor fields.
- *
- * @since 2.0
- * @access private
- * @method _reinitEditorFields
- */
- _reinitEditorFields: function()
- {
- if ( ! $( '.fl-lightbox-resizable:visible' ).length ) {
- return;
- }
- // Do this on a timeout so TinyMCE doesn't hold up other operations.
- setTimeout( function() {
- var i, id;
- if ( 'undefined' === typeof tinymce ) {
- return;
- }
- for ( i = tinymce.editors.length - 1; i > -1 ; i-- ) {
- if ( ! tinymce.editors[ i ].inline ) {
- id = tinymce.editors[ i ].id;
- tinyMCE.execCommand( 'mceRemoveEditor', true, id );
- tinyMCE.execCommand( 'mceAddEditor', true, id );
- }
- }
- if ( FLBuilder.preview ) {
- FLBuilder.preview._initDefaultFieldPreviews( $( '.fl-field[data-type="editor"]' ) );
- }
- }, 1 );
- },
- /**
- * Destroy all TinyMCE editors.
- *
- * @since 1.10.8
- * @method _destroyEditorFields
- */
- _destroyEditorFields: function()
- {
- var i, id;
- if ( 'undefined' === typeof tinymce ) {
- return;
- }
- for ( i = tinymce.editors.length - 1; i > -1 ; i-- ) {
- if ( ! tinymce.editors[ i ].inline ) {
- tinyMCE.execCommand( 'mceRemoveEditor', true, tinymce.editors[ i ].id );
- }
- }
- $( '.wplink-autocomplete' ).remove();
- $( '.ui-helper-hidden-accessible' ).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');
- 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 tinymce == 'undefined' ? false : 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>' ).hide();
- textarea.after( hidden );
- }
- // Save editor content.
- if ( wpautop ) {
- if ( editor && wrap.hasClass( 'tmce-active' ) ) {
- hidden.val( editor.getContent() );
- }
- else if ( 'undefined' != typeof switchEditors ) {
- hidden.val( 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
- ----------------------------------------------------------*/
- /**
- * 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').hide();
- $('.fl-loop-data-source[data-source="' + val + '"]').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').hide();
- $('.fl-custom-query-' + val + '-filter').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' ).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' ) + '"]' ),
- 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', 1).removeClass('mousetrap');
- },
- /* Timezone Fields
- ----------------------------------------------------------*/
- /**
- * @since 2.0
- * @access private
- * @method _initTimezoneFields
- */
- _initTimezoneFields: function() {
- $( '.fl-builder-settings:visible .fl-field[data-type=timezone]' ).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 + '"]' ).attr( 'selected', 'selected' );
- },
- /* 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;
- 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;
- // 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 ) );
- }
- if ( 'undefined' != typeof data.node_settings ) {
- data.node_settings = FLBuilder._ajaxModSecFix( $.extend( true, {}, data.node_settings ) );
- }
- // Store the data in a single variable to avoid conflicts.
- data = { fl_builder_data: data };
- // Do the ajax call.
- return $.post(FLBuilder._ajaxUrl(), data, function(response) {
- FLBuilder._ajaxComplete();
- if(typeof callback !== 'undefined') {
- callback.call(this, response);
- }
- FLBuilder.triggerHook('didCompleteAJAX', data );
- });
- },
- /**
- * Callback for when an AJAX request is complete.
- *
- * @since 1.0
- * @access private
- * @method _ajaxComplete
- */
- _ajaxComplete: function()
- {
- FLBuilder.hideAjaxLoader();
- },
- /**
- * 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 url = window.location.href.split( '#' ).shift(),
- 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' ).length ) {
- $( '.fl-builder-loading' ).show();
- }
- },
- /**
- * Hides the AJAX loading overlay.
- *
- * @since 1.0
- * @method hideAjaxLoader
- */
- hideAjaxLoader: function()
- {
- $( '.fl-builder-loading' ).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.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>' );
- // 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' );
- }
- // 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 ) {
- if ( 'string' == typeof settings[ prop ] ) {
- settings[ prop ] = FLBuilder._btoa( settings[ prop ] );
- }
- else if( 'object' == typeof settings[ prop ] ) {
- 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').remove()
- $('.media-uploader-status' ).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()
- {
- FLBuilder._lightbox.open('<div class="fl-builder-lightbox-loading"></div>');
- 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();
- 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()
- {
- FLBuilder.triggerHook( 'settings-lightbox-closed' );
- FLBuilder._lightbox.empty();
- clearTimeout( FLBuilder._lightboxScrollbarTimeout );
- },
- /**
- * 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
- ----------------------------------------------------------*/
- /**
- * 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 } ) );
- },
- /**
- * 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 = {
- 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',
- 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 )
- {
- 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 );
- FLBuilder.log( '************************************************************************' );
- }
- },
- /**
- * 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( '************************************************************************' );
- }
- },
- };
- /* Start the party!!! */
- $(function(){
- FLBuilder._init();
- });
- })(jQuery);
|