You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

semantic.js 406KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992
  1. /*
  2. * # Fomantic UI - 2.8.7
  3. * https://github.com/fomantic/Fomantic-UI
  4. * http://fomantic-ui.com/
  5. *
  6. * Copyright 2014 Contributors
  7. * Released under the MIT license
  8. * http://opensource.org/licenses/MIT
  9. *
  10. */
  11. /*!
  12. * # Fomantic-UI - API
  13. * http://github.com/fomantic/Fomantic-UI/
  14. *
  15. *
  16. * Released under the MIT license
  17. * http://opensource.org/licenses/MIT
  18. *
  19. */
  20. ;(function ($, window, document, undefined) {
  21. 'use strict';
  22. $.isWindow = $.isWindow || function(obj) {
  23. return obj != null && obj === obj.window;
  24. };
  25. window = (typeof window != 'undefined' && window.Math == Math)
  26. ? window
  27. : (typeof self != 'undefined' && self.Math == Math)
  28. ? self
  29. : Function('return this')()
  30. ;
  31. $.api = $.fn.api = function(parameters) {
  32. var
  33. // use window context if none specified
  34. $allModules = $.isFunction(this)
  35. ? $(window)
  36. : $(this),
  37. moduleSelector = $allModules.selector || '',
  38. time = new Date().getTime(),
  39. performance = [],
  40. query = arguments[0],
  41. methodInvoked = (typeof query == 'string'),
  42. queryArguments = [].slice.call(arguments, 1),
  43. returnedValue
  44. ;
  45. $allModules
  46. .each(function() {
  47. var
  48. settings = ( $.isPlainObject(parameters) )
  49. ? $.extend(true, {}, $.fn.api.settings, parameters)
  50. : $.extend({}, $.fn.api.settings),
  51. // internal aliases
  52. namespace = settings.namespace,
  53. metadata = settings.metadata,
  54. selector = settings.selector,
  55. error = settings.error,
  56. className = settings.className,
  57. // define namespaces for modules
  58. eventNamespace = '.' + namespace,
  59. moduleNamespace = 'module-' + namespace,
  60. // element that creates request
  61. $module = $(this),
  62. $form = $module.closest(selector.form),
  63. // context used for state
  64. $context = (settings.stateContext)
  65. ? $(settings.stateContext)
  66. : $module,
  67. // request details
  68. ajaxSettings,
  69. requestSettings,
  70. url,
  71. data,
  72. requestStartTime,
  73. // standard module
  74. element = this,
  75. context = $context[0],
  76. instance = $module.data(moduleNamespace),
  77. module
  78. ;
  79. module = {
  80. initialize: function() {
  81. if(!methodInvoked) {
  82. module.bind.events();
  83. }
  84. module.instantiate();
  85. },
  86. instantiate: function() {
  87. module.verbose('Storing instance of module', module);
  88. instance = module;
  89. $module
  90. .data(moduleNamespace, instance)
  91. ;
  92. },
  93. destroy: function() {
  94. module.verbose('Destroying previous module for', element);
  95. $module
  96. .removeData(moduleNamespace)
  97. .off(eventNamespace)
  98. ;
  99. },
  100. bind: {
  101. events: function() {
  102. var
  103. triggerEvent = module.get.event()
  104. ;
  105. if( triggerEvent ) {
  106. module.verbose('Attaching API events to element', triggerEvent);
  107. $module
  108. .on(triggerEvent + eventNamespace, module.event.trigger)
  109. ;
  110. }
  111. else if(settings.on == 'now') {
  112. module.debug('Querying API endpoint immediately');
  113. module.query();
  114. }
  115. }
  116. },
  117. decode: {
  118. json: function(response) {
  119. if(response !== undefined && typeof response == 'string') {
  120. try {
  121. response = JSON.parse(response);
  122. }
  123. catch(e) {
  124. // isnt json string
  125. }
  126. }
  127. return response;
  128. }
  129. },
  130. read: {
  131. cachedResponse: function(url) {
  132. var
  133. response
  134. ;
  135. if(window.Storage === undefined) {
  136. module.error(error.noStorage);
  137. return;
  138. }
  139. response = sessionStorage.getItem(url);
  140. module.debug('Using cached response', url, response);
  141. response = module.decode.json(response);
  142. return response;
  143. }
  144. },
  145. write: {
  146. cachedResponse: function(url, response) {
  147. if(response && response === '') {
  148. module.debug('Response empty, not caching', response);
  149. return;
  150. }
  151. if(window.Storage === undefined) {
  152. module.error(error.noStorage);
  153. return;
  154. }
  155. if( $.isPlainObject(response) ) {
  156. response = JSON.stringify(response);
  157. }
  158. sessionStorage.setItem(url, response);
  159. module.verbose('Storing cached response for url', url, response);
  160. }
  161. },
  162. query: function() {
  163. if(module.is.disabled()) {
  164. module.debug('Element is disabled API request aborted');
  165. return;
  166. }
  167. if(module.is.loading()) {
  168. if(settings.interruptRequests) {
  169. module.debug('Interrupting previous request');
  170. module.abort();
  171. }
  172. else {
  173. module.debug('Cancelling request, previous request is still pending');
  174. return;
  175. }
  176. }
  177. // pass element metadata to url (value, text)
  178. if(settings.defaultData) {
  179. $.extend(true, settings.urlData, module.get.defaultData());
  180. }
  181. // Add form content
  182. if(settings.serializeForm) {
  183. settings.data = module.add.formData(settings.data);
  184. }
  185. // call beforesend and get any settings changes
  186. requestSettings = module.get.settings();
  187. // check if before send cancelled request
  188. if(requestSettings === false) {
  189. module.cancelled = true;
  190. module.error(error.beforeSend);
  191. return;
  192. }
  193. else {
  194. module.cancelled = false;
  195. }
  196. // get url
  197. url = module.get.templatedURL();
  198. if(!url && !module.is.mocked()) {
  199. module.error(error.missingURL);
  200. return;
  201. }
  202. // replace variables
  203. url = module.add.urlData( url );
  204. // missing url parameters
  205. if( !url && !module.is.mocked()) {
  206. return;
  207. }
  208. requestSettings.url = settings.base + url;
  209. // look for jQuery ajax parameters in settings
  210. ajaxSettings = $.extend(true, {}, settings, {
  211. type : settings.method || settings.type,
  212. data : data,
  213. url : settings.base + url,
  214. beforeSend : settings.beforeXHR,
  215. success : function() {},
  216. failure : function() {},
  217. complete : function() {}
  218. });
  219. module.debug('Querying URL', ajaxSettings.url);
  220. module.verbose('Using AJAX settings', ajaxSettings);
  221. if(settings.cache === 'local' && module.read.cachedResponse(url)) {
  222. module.debug('Response returned from local cache');
  223. module.request = module.create.request();
  224. module.request.resolveWith(context, [ module.read.cachedResponse(url) ]);
  225. return;
  226. }
  227. if( !settings.throttle ) {
  228. module.debug('Sending request', data, ajaxSettings.method);
  229. module.send.request();
  230. }
  231. else {
  232. if(!settings.throttleFirstRequest && !module.timer) {
  233. module.debug('Sending request', data, ajaxSettings.method);
  234. module.send.request();
  235. module.timer = setTimeout(function(){}, settings.throttle);
  236. }
  237. else {
  238. module.debug('Throttling request', settings.throttle);
  239. clearTimeout(module.timer);
  240. module.timer = setTimeout(function() {
  241. if(module.timer) {
  242. delete module.timer;
  243. }
  244. module.debug('Sending throttled request', data, ajaxSettings.method);
  245. module.send.request();
  246. }, settings.throttle);
  247. }
  248. }
  249. },
  250. should: {
  251. removeError: function() {
  252. return ( settings.hideError === true || (settings.hideError === 'auto' && !module.is.form()) );
  253. }
  254. },
  255. is: {
  256. disabled: function() {
  257. return ($module.filter(selector.disabled).length > 0);
  258. },
  259. expectingJSON: function() {
  260. return settings.dataType === 'json' || settings.dataType === 'jsonp';
  261. },
  262. form: function() {
  263. return $module.is('form') || $context.is('form');
  264. },
  265. mocked: function() {
  266. return (settings.mockResponse || settings.mockResponseAsync || settings.response || settings.responseAsync);
  267. },
  268. input: function() {
  269. return $module.is('input');
  270. },
  271. loading: function() {
  272. return (module.request)
  273. ? (module.request.state() == 'pending')
  274. : false
  275. ;
  276. },
  277. abortedRequest: function(xhr) {
  278. if(xhr && xhr.readyState !== undefined && xhr.readyState === 0) {
  279. module.verbose('XHR request determined to be aborted');
  280. return true;
  281. }
  282. else {
  283. module.verbose('XHR request was not aborted');
  284. return false;
  285. }
  286. },
  287. validResponse: function(response) {
  288. if( (!module.is.expectingJSON()) || !$.isFunction(settings.successTest) ) {
  289. module.verbose('Response is not JSON, skipping validation', settings.successTest, response);
  290. return true;
  291. }
  292. module.debug('Checking JSON returned success', settings.successTest, response);
  293. if( settings.successTest(response) ) {
  294. module.debug('Response passed success test', response);
  295. return true;
  296. }
  297. else {
  298. module.debug('Response failed success test', response);
  299. return false;
  300. }
  301. }
  302. },
  303. was: {
  304. cancelled: function() {
  305. return (module.cancelled || false);
  306. },
  307. succesful: function() {
  308. module.verbose('This behavior will be deleted due to typo. Use "was successful" instead.');
  309. return module.was.successful();
  310. },
  311. successful: function() {
  312. return (module.request && module.request.state() == 'resolved');
  313. },
  314. failure: function() {
  315. return (module.request && module.request.state() == 'rejected');
  316. },
  317. complete: function() {
  318. return (module.request && (module.request.state() == 'resolved' || module.request.state() == 'rejected') );
  319. }
  320. },
  321. add: {
  322. urlData: function(url, urlData) {
  323. var
  324. requiredVariables,
  325. optionalVariables
  326. ;
  327. if(url) {
  328. requiredVariables = url.match(settings.regExp.required);
  329. optionalVariables = url.match(settings.regExp.optional);
  330. urlData = urlData || settings.urlData;
  331. if(requiredVariables) {
  332. module.debug('Looking for required URL variables', requiredVariables);
  333. $.each(requiredVariables, function(index, templatedString) {
  334. var
  335. // allow legacy {$var} style
  336. variable = (templatedString.indexOf('$') !== -1)
  337. ? templatedString.substr(2, templatedString.length - 3)
  338. : templatedString.substr(1, templatedString.length - 2),
  339. value = ($.isPlainObject(urlData) && urlData[variable] !== undefined)
  340. ? urlData[variable]
  341. : ($module.data(variable) !== undefined)
  342. ? $module.data(variable)
  343. : ($context.data(variable) !== undefined)
  344. ? $context.data(variable)
  345. : urlData[variable]
  346. ;
  347. // remove value
  348. if(value === undefined) {
  349. module.error(error.requiredParameter, variable, url);
  350. url = false;
  351. return false;
  352. }
  353. else {
  354. module.verbose('Found required variable', variable, value);
  355. value = (settings.encodeParameters)
  356. ? module.get.urlEncodedValue(value)
  357. : value
  358. ;
  359. url = url.replace(templatedString, value);
  360. }
  361. });
  362. }
  363. if(optionalVariables) {
  364. module.debug('Looking for optional URL variables', requiredVariables);
  365. $.each(optionalVariables, function(index, templatedString) {
  366. var
  367. // allow legacy {/$var} style
  368. variable = (templatedString.indexOf('$') !== -1)
  369. ? templatedString.substr(3, templatedString.length - 4)
  370. : templatedString.substr(2, templatedString.length - 3),
  371. value = ($.isPlainObject(urlData) && urlData[variable] !== undefined)
  372. ? urlData[variable]
  373. : ($module.data(variable) !== undefined)
  374. ? $module.data(variable)
  375. : ($context.data(variable) !== undefined)
  376. ? $context.data(variable)
  377. : urlData[variable]
  378. ;
  379. // optional replacement
  380. if(value !== undefined) {
  381. module.verbose('Optional variable Found', variable, value);
  382. url = url.replace(templatedString, value);
  383. }
  384. else {
  385. module.verbose('Optional variable not found', variable);
  386. // remove preceding slash if set
  387. if(url.indexOf('/' + templatedString) !== -1) {
  388. url = url.replace('/' + templatedString, '');
  389. }
  390. else {
  391. url = url.replace(templatedString, '');
  392. }
  393. }
  394. });
  395. }
  396. }
  397. return url;
  398. },
  399. formData: function(data) {
  400. var
  401. canSerialize = ($.fn.serializeObject !== undefined),
  402. formData = (canSerialize)
  403. ? $form.serializeObject()
  404. : $form.serialize(),
  405. hasOtherData
  406. ;
  407. data = data || settings.data;
  408. hasOtherData = $.isPlainObject(data);
  409. if(hasOtherData) {
  410. if(canSerialize) {
  411. module.debug('Extending existing data with form data', data, formData);
  412. data = $.extend(true, {}, data, formData);
  413. }
  414. else {
  415. module.error(error.missingSerialize);
  416. module.debug('Cant extend data. Replacing data with form data', data, formData);
  417. data = formData;
  418. }
  419. }
  420. else {
  421. module.debug('Adding form data', formData);
  422. data = formData;
  423. }
  424. return data;
  425. }
  426. },
  427. send: {
  428. request: function() {
  429. module.set.loading();
  430. module.request = module.create.request();
  431. if( module.is.mocked() ) {
  432. module.mockedXHR = module.create.mockedXHR();
  433. }
  434. else {
  435. module.xhr = module.create.xhr();
  436. }
  437. settings.onRequest.call(context, module.request, module.xhr);
  438. }
  439. },
  440. event: {
  441. trigger: function(event) {
  442. module.query();
  443. if(event.type == 'submit' || event.type == 'click') {
  444. event.preventDefault();
  445. }
  446. },
  447. xhr: {
  448. always: function() {
  449. // nothing special
  450. },
  451. done: function(response, textStatus, xhr) {
  452. var
  453. context = this,
  454. elapsedTime = (new Date().getTime() - requestStartTime),
  455. timeLeft = (settings.loadingDuration - elapsedTime),
  456. translatedResponse = ( $.isFunction(settings.onResponse) )
  457. ? module.is.expectingJSON() && !settings.rawResponse
  458. ? settings.onResponse.call(context, $.extend(true, {}, response))
  459. : settings.onResponse.call(context, response)
  460. : false
  461. ;
  462. timeLeft = (timeLeft > 0)
  463. ? timeLeft
  464. : 0
  465. ;
  466. if(translatedResponse) {
  467. module.debug('Modified API response in onResponse callback', settings.onResponse, translatedResponse, response);
  468. response = translatedResponse;
  469. }
  470. if(timeLeft > 0) {
  471. module.debug('Response completed early delaying state change by', timeLeft);
  472. }
  473. setTimeout(function() {
  474. if( module.is.validResponse(response) ) {
  475. module.request.resolveWith(context, [response, xhr]);
  476. }
  477. else {
  478. module.request.rejectWith(context, [xhr, 'invalid']);
  479. }
  480. }, timeLeft);
  481. },
  482. fail: function(xhr, status, httpMessage) {
  483. var
  484. context = this,
  485. elapsedTime = (new Date().getTime() - requestStartTime),
  486. timeLeft = (settings.loadingDuration - elapsedTime)
  487. ;
  488. timeLeft = (timeLeft > 0)
  489. ? timeLeft
  490. : 0
  491. ;
  492. if(timeLeft > 0) {
  493. module.debug('Response completed early delaying state change by', timeLeft);
  494. }
  495. setTimeout(function() {
  496. if( module.is.abortedRequest(xhr) ) {
  497. module.request.rejectWith(context, [xhr, 'aborted', httpMessage]);
  498. }
  499. else {
  500. module.request.rejectWith(context, [xhr, 'error', status, httpMessage]);
  501. }
  502. }, timeLeft);
  503. }
  504. },
  505. request: {
  506. done: function(response, xhr) {
  507. module.debug('Successful API Response', response);
  508. if(settings.cache === 'local' && url) {
  509. module.write.cachedResponse(url, response);
  510. module.debug('Saving server response locally', module.cache);
  511. }
  512. settings.onSuccess.call(context, response, $module, xhr);
  513. },
  514. complete: function(firstParameter, secondParameter) {
  515. var
  516. xhr,
  517. response
  518. ;
  519. // have to guess callback parameters based on request success
  520. if( module.was.successful() ) {
  521. response = firstParameter;
  522. xhr = secondParameter;
  523. }
  524. else {
  525. xhr = firstParameter;
  526. response = module.get.responseFromXHR(xhr);
  527. }
  528. module.remove.loading();
  529. settings.onComplete.call(context, response, $module, xhr);
  530. },
  531. fail: function(xhr, status, httpMessage) {
  532. var
  533. // pull response from xhr if available
  534. response = module.get.responseFromXHR(xhr),
  535. errorMessage = module.get.errorFromRequest(response, status, httpMessage)
  536. ;
  537. if(status == 'aborted') {
  538. module.debug('XHR Aborted (Most likely caused by page navigation or CORS Policy)', status, httpMessage);
  539. settings.onAbort.call(context, status, $module, xhr);
  540. return true;
  541. }
  542. else if(status == 'invalid') {
  543. module.debug('JSON did not pass success test. A server-side error has most likely occurred', response);
  544. }
  545. else if(status == 'error') {
  546. if(xhr !== undefined) {
  547. module.debug('XHR produced a server error', status, httpMessage);
  548. // make sure we have an error to display to console
  549. if( (xhr.status < 200 || xhr.status >= 300) && httpMessage !== undefined && httpMessage !== '') {
  550. module.error(error.statusMessage + httpMessage, ajaxSettings.url);
  551. }
  552. settings.onError.call(context, errorMessage, $module, xhr);
  553. }
  554. }
  555. if(settings.errorDuration && status !== 'aborted') {
  556. module.debug('Adding error state');
  557. module.set.error();
  558. if( module.should.removeError() ) {
  559. setTimeout(module.remove.error, settings.errorDuration);
  560. }
  561. }
  562. module.debug('API Request failed', errorMessage, xhr);
  563. settings.onFailure.call(context, response, $module, xhr);
  564. }
  565. }
  566. },
  567. create: {
  568. request: function() {
  569. // api request promise
  570. return $.Deferred()
  571. .always(module.event.request.complete)
  572. .done(module.event.request.done)
  573. .fail(module.event.request.fail)
  574. ;
  575. },
  576. mockedXHR: function () {
  577. var
  578. // xhr does not simulate these properties of xhr but must return them
  579. textStatus = false,
  580. status = false,
  581. httpMessage = false,
  582. responder = settings.mockResponse || settings.response,
  583. asyncResponder = settings.mockResponseAsync || settings.responseAsync,
  584. asyncCallback,
  585. response,
  586. mockedXHR
  587. ;
  588. mockedXHR = $.Deferred()
  589. .always(module.event.xhr.complete)
  590. .done(module.event.xhr.done)
  591. .fail(module.event.xhr.fail)
  592. ;
  593. if(responder) {
  594. if( $.isFunction(responder) ) {
  595. module.debug('Using specified synchronous callback', responder);
  596. response = responder.call(context, requestSettings);
  597. }
  598. else {
  599. module.debug('Using settings specified response', responder);
  600. response = responder;
  601. }
  602. // simulating response
  603. mockedXHR.resolveWith(context, [ response, textStatus, { responseText: response }]);
  604. }
  605. else if( $.isFunction(asyncResponder) ) {
  606. asyncCallback = function(response) {
  607. module.debug('Async callback returned response', response);
  608. if(response) {
  609. mockedXHR.resolveWith(context, [ response, textStatus, { responseText: response }]);
  610. }
  611. else {
  612. mockedXHR.rejectWith(context, [{ responseText: response }, status, httpMessage]);
  613. }
  614. };
  615. module.debug('Using specified async response callback', asyncResponder);
  616. asyncResponder.call(context, requestSettings, asyncCallback);
  617. }
  618. return mockedXHR;
  619. },
  620. xhr: function() {
  621. var
  622. xhr
  623. ;
  624. // ajax request promise
  625. xhr = $.ajax(ajaxSettings)
  626. .always(module.event.xhr.always)
  627. .done(module.event.xhr.done)
  628. .fail(module.event.xhr.fail)
  629. ;
  630. module.verbose('Created server request', xhr, ajaxSettings);
  631. return xhr;
  632. }
  633. },
  634. set: {
  635. error: function() {
  636. module.verbose('Adding error state to element', $context);
  637. $context.addClass(className.error);
  638. },
  639. loading: function() {
  640. module.verbose('Adding loading state to element', $context);
  641. $context.addClass(className.loading);
  642. requestStartTime = new Date().getTime();
  643. }
  644. },
  645. remove: {
  646. error: function() {
  647. module.verbose('Removing error state from element', $context);
  648. $context.removeClass(className.error);
  649. },
  650. loading: function() {
  651. module.verbose('Removing loading state from element', $context);
  652. $context.removeClass(className.loading);
  653. }
  654. },
  655. get: {
  656. responseFromXHR: function(xhr) {
  657. return $.isPlainObject(xhr)
  658. ? (module.is.expectingJSON())
  659. ? module.decode.json(xhr.responseText)
  660. : xhr.responseText
  661. : false
  662. ;
  663. },
  664. errorFromRequest: function(response, status, httpMessage) {
  665. return ($.isPlainObject(response) && response.error !== undefined)
  666. ? response.error // use json error message
  667. : (settings.error[status] !== undefined) // use server error message
  668. ? settings.error[status]
  669. : httpMessage
  670. ;
  671. },
  672. request: function() {
  673. return module.request || false;
  674. },
  675. xhr: function() {
  676. return module.xhr || false;
  677. },
  678. settings: function() {
  679. var
  680. runSettings
  681. ;
  682. runSettings = settings.beforeSend.call($module, settings);
  683. if(runSettings) {
  684. if(runSettings.success !== undefined) {
  685. module.debug('Legacy success callback detected', runSettings);
  686. module.error(error.legacyParameters, runSettings.success);
  687. runSettings.onSuccess = runSettings.success;
  688. }
  689. if(runSettings.failure !== undefined) {
  690. module.debug('Legacy failure callback detected', runSettings);
  691. module.error(error.legacyParameters, runSettings.failure);
  692. runSettings.onFailure = runSettings.failure;
  693. }
  694. if(runSettings.complete !== undefined) {
  695. module.debug('Legacy complete callback detected', runSettings);
  696. module.error(error.legacyParameters, runSettings.complete);
  697. runSettings.onComplete = runSettings.complete;
  698. }
  699. }
  700. if(runSettings === undefined) {
  701. module.error(error.noReturnedValue);
  702. }
  703. if(runSettings === false) {
  704. return runSettings;
  705. }
  706. return (runSettings !== undefined)
  707. ? $.extend(true, {}, runSettings)
  708. : $.extend(true, {}, settings)
  709. ;
  710. },
  711. urlEncodedValue: function(value) {
  712. var
  713. decodedValue = window.decodeURIComponent(value),
  714. encodedValue = window.encodeURIComponent(value),
  715. alreadyEncoded = (decodedValue !== value)
  716. ;
  717. if(alreadyEncoded) {
  718. module.debug('URL value is already encoded, avoiding double encoding', value);
  719. return value;
  720. }
  721. module.verbose('Encoding value using encodeURIComponent', value, encodedValue);
  722. return encodedValue;
  723. },
  724. defaultData: function() {
  725. var
  726. data = {}
  727. ;
  728. if( !$.isWindow(element) ) {
  729. if( module.is.input() ) {
  730. data.value = $module.val();
  731. }
  732. else if( module.is.form() ) {
  733. }
  734. else {
  735. data.text = $module.text();
  736. }
  737. }
  738. return data;
  739. },
  740. event: function() {
  741. if( $.isWindow(element) || settings.on == 'now' ) {
  742. module.debug('API called without element, no events attached');
  743. return false;
  744. }
  745. else if(settings.on == 'auto') {
  746. if( $module.is('input') ) {
  747. return (element.oninput !== undefined)
  748. ? 'input'
  749. : (element.onpropertychange !== undefined)
  750. ? 'propertychange'
  751. : 'keyup'
  752. ;
  753. }
  754. else if( $module.is('form') ) {
  755. return 'submit';
  756. }
  757. else {
  758. return 'click';
  759. }
  760. }
  761. else {
  762. return settings.on;
  763. }
  764. },
  765. templatedURL: function(action) {
  766. action = action || $module.data(metadata.action) || settings.action || false;
  767. url = $module.data(metadata.url) || settings.url || false;
  768. if(url) {
  769. module.debug('Using specified url', url);
  770. return url;
  771. }
  772. if(action) {
  773. module.debug('Looking up url for action', action, settings.api);
  774. if(settings.api[action] === undefined && !module.is.mocked()) {
  775. module.error(error.missingAction, settings.action, settings.api);
  776. return;
  777. }
  778. url = settings.api[action];
  779. }
  780. else if( module.is.form() ) {
  781. url = $module.attr('action') || $context.attr('action') || false;
  782. module.debug('No url or action specified, defaulting to form action', url);
  783. }
  784. return url;
  785. }
  786. },
  787. abort: function() {
  788. var
  789. xhr = module.get.xhr()
  790. ;
  791. if( xhr && xhr.state() !== 'resolved') {
  792. module.debug('Cancelling API request');
  793. xhr.abort();
  794. }
  795. },
  796. // reset state
  797. reset: function() {
  798. module.remove.error();
  799. module.remove.loading();
  800. },
  801. setting: function(name, value) {
  802. module.debug('Changing setting', name, value);
  803. if( $.isPlainObject(name) ) {
  804. $.extend(true, settings, name);
  805. }
  806. else if(value !== undefined) {
  807. if($.isPlainObject(settings[name])) {
  808. $.extend(true, settings[name], value);
  809. }
  810. else {
  811. settings[name] = value;
  812. }
  813. }
  814. else {
  815. return settings[name];
  816. }
  817. },
  818. internal: function(name, value) {
  819. if( $.isPlainObject(name) ) {
  820. $.extend(true, module, name);
  821. }
  822. else if(value !== undefined) {
  823. module[name] = value;
  824. }
  825. else {
  826. return module[name];
  827. }
  828. },
  829. debug: function() {
  830. if(!settings.silent && settings.debug) {
  831. if(settings.performance) {
  832. module.performance.log(arguments);
  833. }
  834. else {
  835. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  836. module.debug.apply(console, arguments);
  837. }
  838. }
  839. },
  840. verbose: function() {
  841. if(!settings.silent && settings.verbose && settings.debug) {
  842. if(settings.performance) {
  843. module.performance.log(arguments);
  844. }
  845. else {
  846. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  847. module.verbose.apply(console, arguments);
  848. }
  849. }
  850. },
  851. error: function() {
  852. if(!settings.silent) {
  853. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  854. module.error.apply(console, arguments);
  855. }
  856. },
  857. performance: {
  858. log: function(message) {
  859. var
  860. currentTime,
  861. executionTime,
  862. previousTime
  863. ;
  864. if(settings.performance) {
  865. currentTime = new Date().getTime();
  866. previousTime = time || currentTime;
  867. executionTime = currentTime - previousTime;
  868. time = currentTime;
  869. performance.push({
  870. 'Name' : message[0],
  871. 'Arguments' : [].slice.call(message, 1) || '',
  872. //'Element' : element,
  873. 'Execution Time' : executionTime
  874. });
  875. }
  876. clearTimeout(module.performance.timer);
  877. module.performance.timer = setTimeout(module.performance.display, 500);
  878. },
  879. display: function() {
  880. var
  881. title = settings.name + ':',
  882. totalTime = 0
  883. ;
  884. time = false;
  885. clearTimeout(module.performance.timer);
  886. $.each(performance, function(index, data) {
  887. totalTime += data['Execution Time'];
  888. });
  889. title += ' ' + totalTime + 'ms';
  890. if(moduleSelector) {
  891. title += ' \'' + moduleSelector + '\'';
  892. }
  893. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  894. console.groupCollapsed(title);
  895. if(console.table) {
  896. console.table(performance);
  897. }
  898. else {
  899. $.each(performance, function(index, data) {
  900. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  901. });
  902. }
  903. console.groupEnd();
  904. }
  905. performance = [];
  906. }
  907. },
  908. invoke: function(query, passedArguments, context) {
  909. var
  910. object = instance,
  911. maxDepth,
  912. found,
  913. response
  914. ;
  915. passedArguments = passedArguments || queryArguments;
  916. context = element || context;
  917. if(typeof query == 'string' && object !== undefined) {
  918. query = query.split(/[\. ]/);
  919. maxDepth = query.length - 1;
  920. $.each(query, function(depth, value) {
  921. var camelCaseValue = (depth != maxDepth)
  922. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  923. : query
  924. ;
  925. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  926. object = object[camelCaseValue];
  927. }
  928. else if( object[camelCaseValue] !== undefined ) {
  929. found = object[camelCaseValue];
  930. return false;
  931. }
  932. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  933. object = object[value];
  934. }
  935. else if( object[value] !== undefined ) {
  936. found = object[value];
  937. return false;
  938. }
  939. else {
  940. module.error(error.method, query);
  941. return false;
  942. }
  943. });
  944. }
  945. if ( $.isFunction( found ) ) {
  946. response = found.apply(context, passedArguments);
  947. }
  948. else if(found !== undefined) {
  949. response = found;
  950. }
  951. if(Array.isArray(returnedValue)) {
  952. returnedValue.push(response);
  953. }
  954. else if(returnedValue !== undefined) {
  955. returnedValue = [returnedValue, response];
  956. }
  957. else if(response !== undefined) {
  958. returnedValue = response;
  959. }
  960. return found;
  961. }
  962. };
  963. if(methodInvoked) {
  964. if(instance === undefined) {
  965. module.initialize();
  966. }
  967. module.invoke(query);
  968. }
  969. else {
  970. if(instance !== undefined) {
  971. instance.invoke('destroy');
  972. }
  973. module.initialize();
  974. }
  975. })
  976. ;
  977. return (returnedValue !== undefined)
  978. ? returnedValue
  979. : this
  980. ;
  981. };
  982. $.api.settings = {
  983. name : 'API',
  984. namespace : 'api',
  985. debug : false,
  986. verbose : false,
  987. performance : true,
  988. // object containing all templates endpoints
  989. api : {},
  990. // whether to cache responses
  991. cache : true,
  992. // whether new requests should abort previous requests
  993. interruptRequests : true,
  994. // event binding
  995. on : 'auto',
  996. // context for applying state classes
  997. stateContext : false,
  998. // duration for loading state
  999. loadingDuration : 0,
  1000. // whether to hide errors after a period of time
  1001. hideError : 'auto',
  1002. // duration for error state
  1003. errorDuration : 2000,
  1004. // whether parameters should be encoded with encodeURIComponent
  1005. encodeParameters : true,
  1006. // API action to use
  1007. action : false,
  1008. // templated URL to use
  1009. url : false,
  1010. // base URL to apply to all endpoints
  1011. base : '',
  1012. // data that will
  1013. urlData : {},
  1014. // whether to add default data to url data
  1015. defaultData : true,
  1016. // whether to serialize closest form
  1017. serializeForm : false,
  1018. // how long to wait before request should occur
  1019. throttle : 0,
  1020. // whether to throttle first request or only repeated
  1021. throttleFirstRequest : true,
  1022. // standard ajax settings
  1023. method : 'get',
  1024. data : {},
  1025. dataType : 'json',
  1026. // mock response
  1027. mockResponse : false,
  1028. mockResponseAsync : false,
  1029. // aliases for mock
  1030. response : false,
  1031. responseAsync : false,
  1032. // whether onResponse should work with response value without force converting into an object
  1033. rawResponse : false,
  1034. // callbacks before request
  1035. beforeSend : function(settings) { return settings; },
  1036. beforeXHR : function(xhr) {},
  1037. onRequest : function(promise, xhr) {},
  1038. // after request
  1039. onResponse : false, // function(response) { },
  1040. // response was successful, if JSON passed validation
  1041. onSuccess : function(response, $module) {},
  1042. // request finished without aborting
  1043. onComplete : function(response, $module) {},
  1044. // failed JSON success test
  1045. onFailure : function(response, $module) {},
  1046. // server error
  1047. onError : function(errorMessage, $module) {},
  1048. // request aborted
  1049. onAbort : function(errorMessage, $module) {},
  1050. successTest : false,
  1051. // errors
  1052. error : {
  1053. beforeSend : 'The before send function has aborted the request',
  1054. error : 'There was an error with your request',
  1055. exitConditions : 'API Request Aborted. Exit conditions met',
  1056. JSONParse : 'JSON could not be parsed during error handling',
  1057. legacyParameters : 'You are using legacy API success callback names',
  1058. method : 'The method you called is not defined',
  1059. missingAction : 'API action used but no url was defined',
  1060. missingSerialize : 'jquery-serialize-object is required to add form data to an existing data object',
  1061. missingURL : 'No URL specified for api event',
  1062. noReturnedValue : 'The beforeSend callback must return a settings object, beforeSend ignored.',
  1063. noStorage : 'Caching responses locally requires session storage',
  1064. parseError : 'There was an error parsing your request',
  1065. requiredParameter : 'Missing a required URL parameter: ',
  1066. statusMessage : 'Server gave an error: ',
  1067. timeout : 'Your request timed out'
  1068. },
  1069. regExp : {
  1070. required : /\{\$*[A-z0-9]+\}/g,
  1071. optional : /\{\/\$*[A-z0-9]+\}/g,
  1072. },
  1073. className: {
  1074. loading : 'loading',
  1075. error : 'error'
  1076. },
  1077. selector: {
  1078. disabled : '.disabled',
  1079. form : 'form'
  1080. },
  1081. metadata: {
  1082. action : 'action',
  1083. url : 'url'
  1084. }
  1085. };
  1086. })( jQuery, window, document );
  1087. /*!
  1088. * # Fomantic-UI - Dimmer
  1089. * http://github.com/fomantic/Fomantic-UI/
  1090. *
  1091. *
  1092. * Released under the MIT license
  1093. * http://opensource.org/licenses/MIT
  1094. *
  1095. */
  1096. ;(function ($, window, document, undefined) {
  1097. 'use strict';
  1098. $.isFunction = $.isFunction || function(obj) {
  1099. return typeof obj === "function" && typeof obj.nodeType !== "number";
  1100. };
  1101. window = (typeof window != 'undefined' && window.Math == Math)
  1102. ? window
  1103. : (typeof self != 'undefined' && self.Math == Math)
  1104. ? self
  1105. : Function('return this')()
  1106. ;
  1107. $.fn.dimmer = function(parameters) {
  1108. var
  1109. $allModules = $(this),
  1110. time = new Date().getTime(),
  1111. performance = [],
  1112. query = arguments[0],
  1113. methodInvoked = (typeof query == 'string'),
  1114. queryArguments = [].slice.call(arguments, 1),
  1115. returnedValue
  1116. ;
  1117. $allModules
  1118. .each(function() {
  1119. var
  1120. settings = ( $.isPlainObject(parameters) )
  1121. ? $.extend(true, {}, $.fn.dimmer.settings, parameters)
  1122. : $.extend({}, $.fn.dimmer.settings),
  1123. selector = settings.selector,
  1124. namespace = settings.namespace,
  1125. className = settings.className,
  1126. error = settings.error,
  1127. eventNamespace = '.' + namespace,
  1128. moduleNamespace = 'module-' + namespace,
  1129. moduleSelector = $allModules.selector || '',
  1130. clickEvent = "click", unstableClickEvent = ('ontouchstart' in document.documentElement)
  1131. ? 'touchstart'
  1132. : 'click',
  1133. $module = $(this),
  1134. $dimmer,
  1135. $dimmable,
  1136. element = this,
  1137. instance = $module.data(moduleNamespace),
  1138. module
  1139. ;
  1140. module = {
  1141. preinitialize: function() {
  1142. if( module.is.dimmer() ) {
  1143. $dimmable = $module.parent();
  1144. $dimmer = $module;
  1145. }
  1146. else {
  1147. $dimmable = $module;
  1148. if( module.has.dimmer() ) {
  1149. if(settings.dimmerName) {
  1150. $dimmer = $dimmable.find(selector.dimmer).filter('.' + settings.dimmerName);
  1151. }
  1152. else {
  1153. $dimmer = $dimmable.find(selector.dimmer);
  1154. }
  1155. }
  1156. else {
  1157. $dimmer = module.create();
  1158. }
  1159. }
  1160. },
  1161. initialize: function() {
  1162. module.debug('Initializing dimmer', settings);
  1163. module.bind.events();
  1164. module.set.dimmable();
  1165. module.instantiate();
  1166. },
  1167. instantiate: function() {
  1168. module.verbose('Storing instance of module', module);
  1169. instance = module;
  1170. $module
  1171. .data(moduleNamespace, instance)
  1172. ;
  1173. },
  1174. destroy: function() {
  1175. module.verbose('Destroying previous module', $dimmer);
  1176. module.unbind.events();
  1177. module.remove.variation();
  1178. $dimmable
  1179. .off(eventNamespace)
  1180. ;
  1181. },
  1182. bind: {
  1183. events: function() {
  1184. if(settings.on == 'hover') {
  1185. $dimmable
  1186. .on('mouseenter' + eventNamespace, module.show)
  1187. .on('mouseleave' + eventNamespace, module.hide)
  1188. ;
  1189. }
  1190. else if(settings.on == 'click') {
  1191. $dimmable
  1192. .on(clickEvent + eventNamespace, module.toggle)
  1193. ;
  1194. }
  1195. if( module.is.page() ) {
  1196. module.debug('Setting as a page dimmer', $dimmable);
  1197. module.set.pageDimmer();
  1198. }
  1199. if( module.is.closable() ) {
  1200. module.verbose('Adding dimmer close event', $dimmer);
  1201. $dimmable
  1202. .on(clickEvent + eventNamespace, selector.dimmer, module.event.click)
  1203. ;
  1204. }
  1205. }
  1206. },
  1207. unbind: {
  1208. events: function() {
  1209. $module
  1210. .removeData(moduleNamespace)
  1211. ;
  1212. $dimmable
  1213. .off(eventNamespace)
  1214. ;
  1215. }
  1216. },
  1217. event: {
  1218. click: function(event) {
  1219. module.verbose('Determining if event occured on dimmer', event);
  1220. if( $dimmer.find(event.target).length === 0 || $(event.target).is(selector.content) ) {
  1221. module.hide();
  1222. event.stopImmediatePropagation();
  1223. }
  1224. }
  1225. },
  1226. addContent: function(element) {
  1227. var
  1228. $content = $(element)
  1229. ;
  1230. module.debug('Add content to dimmer', $content);
  1231. if($content.parent()[0] !== $dimmer[0]) {
  1232. $content.detach().appendTo($dimmer);
  1233. }
  1234. },
  1235. create: function() {
  1236. var
  1237. $element = $( settings.template.dimmer(settings) )
  1238. ;
  1239. if(settings.dimmerName) {
  1240. module.debug('Creating named dimmer', settings.dimmerName);
  1241. $element.addClass(settings.dimmerName);
  1242. }
  1243. $element
  1244. .appendTo($dimmable)
  1245. ;
  1246. return $element;
  1247. },
  1248. show: function(callback) {
  1249. callback = $.isFunction(callback)
  1250. ? callback
  1251. : function(){}
  1252. ;
  1253. module.debug('Showing dimmer', $dimmer, settings);
  1254. module.set.variation();
  1255. if( (!module.is.dimmed() || module.is.animating()) && module.is.enabled() ) {
  1256. module.animate.show(callback);
  1257. settings.onShow.call(element);
  1258. settings.onChange.call(element);
  1259. }
  1260. else {
  1261. module.debug('Dimmer is already shown or disabled');
  1262. }
  1263. },
  1264. hide: function(callback) {
  1265. callback = $.isFunction(callback)
  1266. ? callback
  1267. : function(){}
  1268. ;
  1269. if( module.is.dimmed() || module.is.animating() ) {
  1270. module.debug('Hiding dimmer', $dimmer);
  1271. module.animate.hide(callback);
  1272. settings.onHide.call(element);
  1273. settings.onChange.call(element);
  1274. }
  1275. else {
  1276. module.debug('Dimmer is not visible');
  1277. }
  1278. },
  1279. toggle: function() {
  1280. module.verbose('Toggling dimmer visibility', $dimmer);
  1281. if( !module.is.dimmed() ) {
  1282. module.show();
  1283. }
  1284. else {
  1285. if ( module.is.closable() ) {
  1286. module.hide();
  1287. }
  1288. }
  1289. },
  1290. animate: {
  1291. show: function(callback) {
  1292. callback = $.isFunction(callback)
  1293. ? callback
  1294. : function(){}
  1295. ;
  1296. if(settings.useCSS && $.fn.transition !== undefined && $dimmer.transition('is supported')) {
  1297. if(settings.useFlex) {
  1298. module.debug('Using flex dimmer');
  1299. module.remove.legacy();
  1300. }
  1301. else {
  1302. module.debug('Using legacy non-flex dimmer');
  1303. module.set.legacy();
  1304. }
  1305. if(settings.opacity !== 'auto') {
  1306. module.set.opacity();
  1307. }
  1308. $dimmer
  1309. .transition({
  1310. displayType : settings.useFlex
  1311. ? 'flex'
  1312. : 'block',
  1313. animation : settings.transition + ' in',
  1314. queue : false,
  1315. duration : module.get.duration(),
  1316. useFailSafe : true,
  1317. onStart : function() {
  1318. module.set.dimmed();
  1319. },
  1320. onComplete : function() {
  1321. module.set.active();
  1322. callback();
  1323. }
  1324. })
  1325. ;
  1326. }
  1327. else {
  1328. module.verbose('Showing dimmer animation with javascript');
  1329. module.set.dimmed();
  1330. if(settings.opacity == 'auto') {
  1331. settings.opacity = 0.8;
  1332. }
  1333. $dimmer
  1334. .stop()
  1335. .css({
  1336. opacity : 0,
  1337. width : '100%',
  1338. height : '100%'
  1339. })
  1340. .fadeTo(module.get.duration(), settings.opacity, function() {
  1341. $dimmer.removeAttr('style');
  1342. module.set.active();
  1343. callback();
  1344. })
  1345. ;
  1346. }
  1347. },
  1348. hide: function(callback) {
  1349. callback = $.isFunction(callback)
  1350. ? callback
  1351. : function(){}
  1352. ;
  1353. if(settings.useCSS && $.fn.transition !== undefined && $dimmer.transition('is supported')) {
  1354. module.verbose('Hiding dimmer with css');
  1355. $dimmer
  1356. .transition({
  1357. displayType : settings.useFlex
  1358. ? 'flex'
  1359. : 'block',
  1360. animation : settings.transition + ' out',
  1361. queue : false,
  1362. duration : module.get.duration(),
  1363. useFailSafe : true,
  1364. onComplete : function() {
  1365. module.remove.dimmed();
  1366. module.remove.variation();
  1367. module.remove.active();
  1368. callback();
  1369. }
  1370. })
  1371. ;
  1372. }
  1373. else {
  1374. module.verbose('Hiding dimmer with javascript');
  1375. $dimmer
  1376. .stop()
  1377. .fadeOut(module.get.duration(), function() {
  1378. module.remove.dimmed();
  1379. module.remove.active();
  1380. $dimmer.removeAttr('style');
  1381. callback();
  1382. })
  1383. ;
  1384. }
  1385. }
  1386. },
  1387. get: {
  1388. dimmer: function() {
  1389. return $dimmer;
  1390. },
  1391. duration: function() {
  1392. if(typeof settings.duration == 'object') {
  1393. if( module.is.active() ) {
  1394. return settings.duration.hide;
  1395. }
  1396. else {
  1397. return settings.duration.show;
  1398. }
  1399. }
  1400. return settings.duration;
  1401. }
  1402. },
  1403. has: {
  1404. dimmer: function() {
  1405. if(settings.dimmerName) {
  1406. return ($module.find(selector.dimmer).filter('.' + settings.dimmerName).length > 0);
  1407. }
  1408. else {
  1409. return ( $module.find(selector.dimmer).length > 0 );
  1410. }
  1411. }
  1412. },
  1413. is: {
  1414. active: function() {
  1415. return $dimmer.hasClass(className.active);
  1416. },
  1417. animating: function() {
  1418. return ( $dimmer.is(':animated') || $dimmer.hasClass(className.animating) );
  1419. },
  1420. closable: function() {
  1421. if(settings.closable == 'auto') {
  1422. if(settings.on == 'hover') {
  1423. return false;
  1424. }
  1425. return true;
  1426. }
  1427. return settings.closable;
  1428. },
  1429. dimmer: function() {
  1430. return $module.hasClass(className.dimmer);
  1431. },
  1432. dimmable: function() {
  1433. return $module.hasClass(className.dimmable);
  1434. },
  1435. dimmed: function() {
  1436. return $dimmable.hasClass(className.dimmed);
  1437. },
  1438. disabled: function() {
  1439. return $dimmable.hasClass(className.disabled);
  1440. },
  1441. enabled: function() {
  1442. return !module.is.disabled();
  1443. },
  1444. page: function () {
  1445. return $dimmable.is('body');
  1446. },
  1447. pageDimmer: function() {
  1448. return $dimmer.hasClass(className.pageDimmer);
  1449. }
  1450. },
  1451. can: {
  1452. show: function() {
  1453. return !$dimmer.hasClass(className.disabled);
  1454. }
  1455. },
  1456. set: {
  1457. opacity: function(opacity) {
  1458. var
  1459. color = $dimmer.css('background-color'),
  1460. colorArray = color.split(','),
  1461. isRGB = (colorArray && colorArray.length >= 3)
  1462. ;
  1463. opacity = settings.opacity === 0 ? 0 : settings.opacity || opacity;
  1464. if(isRGB) {
  1465. colorArray[2] = colorArray[2].replace(')','');
  1466. colorArray[3] = opacity + ')';
  1467. color = colorArray.join(',');
  1468. }
  1469. else {
  1470. color = 'rgba(0, 0, 0, ' + opacity + ')';
  1471. }
  1472. module.debug('Setting opacity to', opacity);
  1473. $dimmer.css('background-color', color);
  1474. },
  1475. legacy: function() {
  1476. $dimmer.addClass(className.legacy);
  1477. },
  1478. active: function() {
  1479. $dimmer.addClass(className.active);
  1480. },
  1481. dimmable: function() {
  1482. $dimmable.addClass(className.dimmable);
  1483. },
  1484. dimmed: function() {
  1485. $dimmable.addClass(className.dimmed);
  1486. },
  1487. pageDimmer: function() {
  1488. $dimmer.addClass(className.pageDimmer);
  1489. },
  1490. disabled: function() {
  1491. $dimmer.addClass(className.disabled);
  1492. },
  1493. variation: function(variation) {
  1494. variation = variation || settings.variation;
  1495. if(variation) {
  1496. $dimmer.addClass(variation);
  1497. }
  1498. }
  1499. },
  1500. remove: {
  1501. active: function() {
  1502. $dimmer
  1503. .removeClass(className.active)
  1504. ;
  1505. },
  1506. legacy: function() {
  1507. $dimmer.removeClass(className.legacy);
  1508. },
  1509. dimmed: function() {
  1510. $dimmable.removeClass(className.dimmed);
  1511. },
  1512. disabled: function() {
  1513. $dimmer.removeClass(className.disabled);
  1514. },
  1515. variation: function(variation) {
  1516. variation = variation || settings.variation;
  1517. if(variation) {
  1518. $dimmer.removeClass(variation);
  1519. }
  1520. }
  1521. },
  1522. setting: function(name, value) {
  1523. module.debug('Changing setting', name, value);
  1524. if( $.isPlainObject(name) ) {
  1525. $.extend(true, settings, name);
  1526. }
  1527. else if(value !== undefined) {
  1528. if($.isPlainObject(settings[name])) {
  1529. $.extend(true, settings[name], value);
  1530. }
  1531. else {
  1532. settings[name] = value;
  1533. }
  1534. }
  1535. else {
  1536. return settings[name];
  1537. }
  1538. },
  1539. internal: function(name, value) {
  1540. if( $.isPlainObject(name) ) {
  1541. $.extend(true, module, name);
  1542. }
  1543. else if(value !== undefined) {
  1544. module[name] = value;
  1545. }
  1546. else {
  1547. return module[name];
  1548. }
  1549. },
  1550. debug: function() {
  1551. if(!settings.silent && settings.debug) {
  1552. if(settings.performance) {
  1553. module.performance.log(arguments);
  1554. }
  1555. else {
  1556. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  1557. module.debug.apply(console, arguments);
  1558. }
  1559. }
  1560. },
  1561. verbose: function() {
  1562. if(!settings.silent && settings.verbose && settings.debug) {
  1563. if(settings.performance) {
  1564. module.performance.log(arguments);
  1565. }
  1566. else {
  1567. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  1568. module.verbose.apply(console, arguments);
  1569. }
  1570. }
  1571. },
  1572. error: function() {
  1573. if(!settings.silent) {
  1574. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  1575. module.error.apply(console, arguments);
  1576. }
  1577. },
  1578. performance: {
  1579. log: function(message) {
  1580. var
  1581. currentTime,
  1582. executionTime,
  1583. previousTime
  1584. ;
  1585. if(settings.performance) {
  1586. currentTime = new Date().getTime();
  1587. previousTime = time || currentTime;
  1588. executionTime = currentTime - previousTime;
  1589. time = currentTime;
  1590. performance.push({
  1591. 'Name' : message[0],
  1592. 'Arguments' : [].slice.call(message, 1) || '',
  1593. 'Element' : element,
  1594. 'Execution Time' : executionTime
  1595. });
  1596. }
  1597. clearTimeout(module.performance.timer);
  1598. module.performance.timer = setTimeout(module.performance.display, 500);
  1599. },
  1600. display: function() {
  1601. var
  1602. title = settings.name + ':',
  1603. totalTime = 0
  1604. ;
  1605. time = false;
  1606. clearTimeout(module.performance.timer);
  1607. $.each(performance, function(index, data) {
  1608. totalTime += data['Execution Time'];
  1609. });
  1610. title += ' ' + totalTime + 'ms';
  1611. if(moduleSelector) {
  1612. title += ' \'' + moduleSelector + '\'';
  1613. }
  1614. if($allModules.length > 1) {
  1615. title += ' ' + '(' + $allModules.length + ')';
  1616. }
  1617. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  1618. console.groupCollapsed(title);
  1619. if(console.table) {
  1620. console.table(performance);
  1621. }
  1622. else {
  1623. $.each(performance, function(index, data) {
  1624. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  1625. });
  1626. }
  1627. console.groupEnd();
  1628. }
  1629. performance = [];
  1630. }
  1631. },
  1632. invoke: function(query, passedArguments, context) {
  1633. var
  1634. object = instance,
  1635. maxDepth,
  1636. found,
  1637. response
  1638. ;
  1639. passedArguments = passedArguments || queryArguments;
  1640. context = element || context;
  1641. if(typeof query == 'string' && object !== undefined) {
  1642. query = query.split(/[\. ]/);
  1643. maxDepth = query.length - 1;
  1644. $.each(query, function(depth, value) {
  1645. var camelCaseValue = (depth != maxDepth)
  1646. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  1647. : query
  1648. ;
  1649. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  1650. object = object[camelCaseValue];
  1651. }
  1652. else if( object[camelCaseValue] !== undefined ) {
  1653. found = object[camelCaseValue];
  1654. return false;
  1655. }
  1656. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  1657. object = object[value];
  1658. }
  1659. else if( object[value] !== undefined ) {
  1660. found = object[value];
  1661. return false;
  1662. }
  1663. else {
  1664. module.error(error.method, query);
  1665. return false;
  1666. }
  1667. });
  1668. }
  1669. if ( $.isFunction( found ) ) {
  1670. response = found.apply(context, passedArguments);
  1671. }
  1672. else if(found !== undefined) {
  1673. response = found;
  1674. }
  1675. if(Array.isArray(returnedValue)) {
  1676. returnedValue.push(response);
  1677. }
  1678. else if(returnedValue !== undefined) {
  1679. returnedValue = [returnedValue, response];
  1680. }
  1681. else if(response !== undefined) {
  1682. returnedValue = response;
  1683. }
  1684. return found;
  1685. }
  1686. };
  1687. module.preinitialize();
  1688. if(methodInvoked) {
  1689. if(instance === undefined) {
  1690. module.initialize();
  1691. }
  1692. module.invoke(query);
  1693. }
  1694. else {
  1695. if(instance !== undefined) {
  1696. instance.invoke('destroy');
  1697. }
  1698. module.initialize();
  1699. }
  1700. })
  1701. ;
  1702. return (returnedValue !== undefined)
  1703. ? returnedValue
  1704. : this
  1705. ;
  1706. };
  1707. $.fn.dimmer.settings = {
  1708. name : 'Dimmer',
  1709. namespace : 'dimmer',
  1710. silent : false,
  1711. debug : false,
  1712. verbose : false,
  1713. performance : true,
  1714. // whether should use flex layout
  1715. useFlex : true,
  1716. // name to distinguish between multiple dimmers in context
  1717. dimmerName : false,
  1718. // whether to add a variation type
  1719. variation : false,
  1720. // whether to bind close events
  1721. closable : 'auto',
  1722. // whether to use css animations
  1723. useCSS : true,
  1724. // css animation to use
  1725. transition : 'fade',
  1726. // event to bind to
  1727. on : false,
  1728. // overriding opacity value
  1729. opacity : 'auto',
  1730. // transition durations
  1731. duration : {
  1732. show : 500,
  1733. hide : 500
  1734. },
  1735. // whether the dynamically created dimmer should have a loader
  1736. displayLoader: false,
  1737. loaderText : false,
  1738. loaderVariation : '',
  1739. onChange : function(){},
  1740. onShow : function(){},
  1741. onHide : function(){},
  1742. error : {
  1743. method : 'The method you called is not defined.'
  1744. },
  1745. className : {
  1746. active : 'active',
  1747. animating : 'animating',
  1748. dimmable : 'dimmable',
  1749. dimmed : 'dimmed',
  1750. dimmer : 'dimmer',
  1751. disabled : 'disabled',
  1752. hide : 'hide',
  1753. legacy : 'legacy',
  1754. pageDimmer : 'page',
  1755. show : 'show',
  1756. loader : 'ui loader'
  1757. },
  1758. selector: {
  1759. dimmer : '> .ui.dimmer',
  1760. content : '.ui.dimmer > .content, .ui.dimmer > .content > .center'
  1761. },
  1762. template: {
  1763. dimmer: function(settings) {
  1764. var d = $('<div/>').addClass('ui dimmer'),l;
  1765. if(settings.displayLoader) {
  1766. l = $('<div/>')
  1767. .addClass(settings.className.loader)
  1768. .addClass(settings.loaderVariation);
  1769. if(!!settings.loaderText){
  1770. l.text(settings.loaderText);
  1771. l.addClass('text');
  1772. }
  1773. d.append(l);
  1774. }
  1775. return d;
  1776. }
  1777. }
  1778. };
  1779. })( jQuery, window, document );
  1780. /*!
  1781. * # Fomantic-UI - Dropdown
  1782. * http://github.com/fomantic/Fomantic-UI/
  1783. *
  1784. *
  1785. * Released under the MIT license
  1786. * http://opensource.org/licenses/MIT
  1787. *
  1788. */
  1789. ;(function ($, window, document, undefined) {
  1790. 'use strict';
  1791. $.isFunction = $.isFunction || function(obj) {
  1792. return typeof obj === "function" && typeof obj.nodeType !== "number";
  1793. };
  1794. window = (typeof window != 'undefined' && window.Math == Math)
  1795. ? window
  1796. : (typeof self != 'undefined' && self.Math == Math)
  1797. ? self
  1798. : Function('return this')()
  1799. ;
  1800. $.fn.dropdown = function(parameters) {
  1801. var
  1802. $allModules = $(this),
  1803. $document = $(document),
  1804. moduleSelector = $allModules.selector || '',
  1805. hasTouch = ('ontouchstart' in document.documentElement),
  1806. clickEvent = "click", unstableClickEvent = hasTouch
  1807. ? 'touchstart'
  1808. : 'click',
  1809. time = new Date().getTime(),
  1810. performance = [],
  1811. query = arguments[0],
  1812. methodInvoked = (typeof query == 'string'),
  1813. queryArguments = [].slice.call(arguments, 1),
  1814. returnedValue
  1815. ;
  1816. $allModules
  1817. .each(function(elementIndex) {
  1818. var
  1819. settings = ( $.isPlainObject(parameters) )
  1820. ? $.extend(true, {}, $.fn.dropdown.settings, parameters)
  1821. : $.extend({}, $.fn.dropdown.settings),
  1822. className = settings.className,
  1823. message = settings.message,
  1824. fields = settings.fields,
  1825. keys = settings.keys,
  1826. metadata = settings.metadata,
  1827. namespace = settings.namespace,
  1828. regExp = settings.regExp,
  1829. selector = settings.selector,
  1830. error = settings.error,
  1831. templates = settings.templates,
  1832. eventNamespace = '.' + namespace,
  1833. moduleNamespace = 'module-' + namespace,
  1834. $module = $(this),
  1835. $context = $(settings.context),
  1836. $text = $module.find(selector.text),
  1837. $search = $module.find(selector.search),
  1838. $sizer = $module.find(selector.sizer),
  1839. $input = $module.find(selector.input),
  1840. $icon = $module.find(selector.icon),
  1841. $clear = $module.find(selector.clearIcon),
  1842. $combo = ($module.prev().find(selector.text).length > 0)
  1843. ? $module.prev().find(selector.text)
  1844. : $module.prev(),
  1845. $menu = $module.children(selector.menu),
  1846. $item = $menu.find(selector.item),
  1847. $divider = settings.hideDividers ? $item.parent().children(selector.divider) : $(),
  1848. activated = false,
  1849. itemActivated = false,
  1850. internalChange = false,
  1851. iconClicked = false,
  1852. element = this,
  1853. instance = $module.data(moduleNamespace),
  1854. selectActionActive,
  1855. initialLoad,
  1856. pageLostFocus,
  1857. willRefocus,
  1858. elementNamespace,
  1859. id,
  1860. selectObserver,
  1861. menuObserver,
  1862. classObserver,
  1863. module
  1864. ;
  1865. module = {
  1866. initialize: function() {
  1867. module.debug('Initializing dropdown', settings);
  1868. if( module.is.alreadySetup() ) {
  1869. module.setup.reference();
  1870. }
  1871. else {
  1872. if (settings.ignoreDiacritics && !String.prototype.normalize) {
  1873. settings.ignoreDiacritics = false;
  1874. module.error(error.noNormalize, element);
  1875. }
  1876. module.setup.layout();
  1877. if(settings.values) {
  1878. module.set.initialLoad();
  1879. module.change.values(settings.values);
  1880. module.remove.initialLoad();
  1881. }
  1882. module.refreshData();
  1883. module.save.defaults();
  1884. module.restore.selected();
  1885. module.create.id();
  1886. module.bind.events();
  1887. module.observeChanges();
  1888. module.instantiate();
  1889. }
  1890. },
  1891. instantiate: function() {
  1892. module.verbose('Storing instance of dropdown', module);
  1893. instance = module;
  1894. $module
  1895. .data(moduleNamespace, module)
  1896. ;
  1897. },
  1898. destroy: function() {
  1899. module.verbose('Destroying previous dropdown', $module);
  1900. module.remove.tabbable();
  1901. module.remove.active();
  1902. $menu.transition('stop all');
  1903. $menu.removeClass(className.visible).addClass(className.hidden);
  1904. $module
  1905. .off(eventNamespace)
  1906. .removeData(moduleNamespace)
  1907. ;
  1908. $menu
  1909. .off(eventNamespace)
  1910. ;
  1911. $document
  1912. .off(elementNamespace)
  1913. ;
  1914. module.disconnect.menuObserver();
  1915. module.disconnect.selectObserver();
  1916. module.disconnect.classObserver();
  1917. },
  1918. observeChanges: function() {
  1919. if('MutationObserver' in window) {
  1920. selectObserver = new MutationObserver(module.event.select.mutation);
  1921. menuObserver = new MutationObserver(module.event.menu.mutation);
  1922. classObserver = new MutationObserver(module.event.class.mutation);
  1923. module.debug('Setting up mutation observer', selectObserver, menuObserver, classObserver);
  1924. module.observe.select();
  1925. module.observe.menu();
  1926. module.observe.class();
  1927. }
  1928. },
  1929. disconnect: {
  1930. menuObserver: function() {
  1931. if(menuObserver) {
  1932. menuObserver.disconnect();
  1933. }
  1934. },
  1935. selectObserver: function() {
  1936. if(selectObserver) {
  1937. selectObserver.disconnect();
  1938. }
  1939. },
  1940. classObserver: function() {
  1941. if(classObserver) {
  1942. classObserver.disconnect();
  1943. }
  1944. }
  1945. },
  1946. observe: {
  1947. select: function() {
  1948. if(module.has.input() && selectObserver) {
  1949. selectObserver.observe($module[0], {
  1950. childList : true,
  1951. subtree : true
  1952. });
  1953. }
  1954. },
  1955. menu: function() {
  1956. if(module.has.menu() && menuObserver) {
  1957. menuObserver.observe($menu[0], {
  1958. childList : true,
  1959. subtree : true
  1960. });
  1961. }
  1962. },
  1963. class: function() {
  1964. if(module.has.search() && classObserver) {
  1965. classObserver.observe($module[0], {
  1966. attributes : true
  1967. });
  1968. }
  1969. }
  1970. },
  1971. create: {
  1972. id: function() {
  1973. id = (Math.random().toString(16) + '000000000').substr(2, 8);
  1974. elementNamespace = '.' + id;
  1975. module.verbose('Creating unique id for element', id);
  1976. },
  1977. userChoice: function(values) {
  1978. var
  1979. $userChoices,
  1980. $userChoice,
  1981. isUserValue,
  1982. html
  1983. ;
  1984. values = values || module.get.userValues();
  1985. if(!values) {
  1986. return false;
  1987. }
  1988. values = Array.isArray(values)
  1989. ? values
  1990. : [values]
  1991. ;
  1992. $.each(values, function(index, value) {
  1993. if(module.get.item(value) === false) {
  1994. html = settings.templates.addition( module.add.variables(message.addResult, value) );
  1995. $userChoice = $('<div />')
  1996. .html(html)
  1997. .attr('data-' + metadata.value, value)
  1998. .attr('data-' + metadata.text, value)
  1999. .addClass(className.addition)
  2000. .addClass(className.item)
  2001. ;
  2002. if(settings.hideAdditions) {
  2003. $userChoice.addClass(className.hidden);
  2004. }
  2005. $userChoices = ($userChoices === undefined)
  2006. ? $userChoice
  2007. : $userChoices.add($userChoice)
  2008. ;
  2009. module.verbose('Creating user choices for value', value, $userChoice);
  2010. }
  2011. });
  2012. return $userChoices;
  2013. },
  2014. userLabels: function(value) {
  2015. var
  2016. userValues = module.get.userValues()
  2017. ;
  2018. if(userValues) {
  2019. module.debug('Adding user labels', userValues);
  2020. $.each(userValues, function(index, value) {
  2021. module.verbose('Adding custom user value');
  2022. module.add.label(value, value);
  2023. });
  2024. }
  2025. },
  2026. menu: function() {
  2027. $menu = $('<div />')
  2028. .addClass(className.menu)
  2029. .appendTo($module)
  2030. ;
  2031. },
  2032. sizer: function() {
  2033. $sizer = $('<span />')
  2034. .addClass(className.sizer)
  2035. .insertAfter($search)
  2036. ;
  2037. }
  2038. },
  2039. search: function(query) {
  2040. query = (query !== undefined)
  2041. ? query
  2042. : module.get.query()
  2043. ;
  2044. module.verbose('Searching for query', query);
  2045. if(module.has.minCharacters(query)) {
  2046. module.filter(query);
  2047. }
  2048. else {
  2049. module.hide(null,true);
  2050. }
  2051. },
  2052. select: {
  2053. firstUnfiltered: function() {
  2054. module.verbose('Selecting first non-filtered element');
  2055. module.remove.selectedItem();
  2056. $item
  2057. .not(selector.unselectable)
  2058. .not(selector.addition + selector.hidden)
  2059. .eq(0)
  2060. .addClass(className.selected)
  2061. ;
  2062. },
  2063. nextAvailable: function($selected) {
  2064. $selected = $selected.eq(0);
  2065. var
  2066. $nextAvailable = $selected.nextAll(selector.item).not(selector.unselectable).eq(0),
  2067. $prevAvailable = $selected.prevAll(selector.item).not(selector.unselectable).eq(0),
  2068. hasNext = ($nextAvailable.length > 0)
  2069. ;
  2070. if(hasNext) {
  2071. module.verbose('Moving selection to', $nextAvailable);
  2072. $nextAvailable.addClass(className.selected);
  2073. }
  2074. else {
  2075. module.verbose('Moving selection to', $prevAvailable);
  2076. $prevAvailable.addClass(className.selected);
  2077. }
  2078. }
  2079. },
  2080. setup: {
  2081. api: function() {
  2082. var
  2083. apiSettings = {
  2084. debug : settings.debug,
  2085. urlData : {
  2086. value : module.get.value(),
  2087. query : module.get.query()
  2088. },
  2089. on : false
  2090. }
  2091. ;
  2092. module.verbose('First request, initializing API');
  2093. $module
  2094. .api(apiSettings)
  2095. ;
  2096. },
  2097. layout: function() {
  2098. if( $module.is('select') ) {
  2099. module.setup.select();
  2100. module.setup.returnedObject();
  2101. }
  2102. if( !module.has.menu() ) {
  2103. module.create.menu();
  2104. }
  2105. if ( module.is.selection() && module.is.clearable() && !module.has.clearItem() ) {
  2106. module.verbose('Adding clear icon');
  2107. $clear = $('<i />')
  2108. .addClass('remove icon')
  2109. .insertBefore($text)
  2110. ;
  2111. }
  2112. if( module.is.search() && !module.has.search() ) {
  2113. module.verbose('Adding search input');
  2114. $search = $('<input />')
  2115. .addClass(className.search)
  2116. .prop('autocomplete', 'off')
  2117. .insertBefore($text)
  2118. ;
  2119. }
  2120. if( module.is.multiple() && module.is.searchSelection() && !module.has.sizer()) {
  2121. module.create.sizer();
  2122. }
  2123. if(settings.allowTab) {
  2124. module.set.tabbable();
  2125. }
  2126. },
  2127. select: function() {
  2128. var
  2129. selectValues = module.get.selectValues()
  2130. ;
  2131. module.debug('Dropdown initialized on a select', selectValues);
  2132. if( $module.is('select') ) {
  2133. $input = $module;
  2134. }
  2135. // see if select is placed correctly already
  2136. if($input.parent(selector.dropdown).length > 0) {
  2137. module.debug('UI dropdown already exists. Creating dropdown menu only');
  2138. $module = $input.closest(selector.dropdown);
  2139. if( !module.has.menu() ) {
  2140. module.create.menu();
  2141. }
  2142. $menu = $module.children(selector.menu);
  2143. module.setup.menu(selectValues);
  2144. }
  2145. else {
  2146. module.debug('Creating entire dropdown from select');
  2147. $module = $('<div />')
  2148. .attr('class', $input.attr('class') )
  2149. .addClass(className.selection)
  2150. .addClass(className.dropdown)
  2151. .html( templates.dropdown(selectValues, fields, settings.preserveHTML, settings.className) )
  2152. .insertBefore($input)
  2153. ;
  2154. if($input.hasClass(className.multiple) && $input.prop('multiple') === false) {
  2155. module.error(error.missingMultiple);
  2156. $input.prop('multiple', true);
  2157. }
  2158. if($input.is('[multiple]')) {
  2159. module.set.multiple();
  2160. }
  2161. if ($input.prop('disabled')) {
  2162. module.debug('Disabling dropdown');
  2163. $module.addClass(className.disabled);
  2164. }
  2165. $input
  2166. .removeAttr('required')
  2167. .removeAttr('class')
  2168. .detach()
  2169. .prependTo($module)
  2170. ;
  2171. }
  2172. module.refresh();
  2173. },
  2174. menu: function(values) {
  2175. $menu.html( templates.menu(values, fields,settings.preserveHTML,settings.className));
  2176. $item = $menu.find(selector.item);
  2177. $divider = settings.hideDividers ? $item.parent().children(selector.divider) : $();
  2178. },
  2179. reference: function() {
  2180. module.debug('Dropdown behavior was called on select, replacing with closest dropdown');
  2181. // replace module reference
  2182. $module = $module.parent(selector.dropdown);
  2183. instance = $module.data(moduleNamespace);
  2184. element = $module.get(0);
  2185. module.refresh();
  2186. module.setup.returnedObject();
  2187. },
  2188. returnedObject: function() {
  2189. var
  2190. $firstModules = $allModules.slice(0, elementIndex),
  2191. $lastModules = $allModules.slice(elementIndex + 1)
  2192. ;
  2193. // adjust all modules to use correct reference
  2194. $allModules = $firstModules.add($module).add($lastModules);
  2195. }
  2196. },
  2197. refresh: function() {
  2198. module.refreshSelectors();
  2199. module.refreshData();
  2200. },
  2201. refreshItems: function() {
  2202. $item = $menu.find(selector.item);
  2203. $divider = settings.hideDividers ? $item.parent().children(selector.divider) : $();
  2204. },
  2205. refreshSelectors: function() {
  2206. module.verbose('Refreshing selector cache');
  2207. $text = $module.find(selector.text);
  2208. $search = $module.find(selector.search);
  2209. $input = $module.find(selector.input);
  2210. $icon = $module.find(selector.icon);
  2211. $combo = ($module.prev().find(selector.text).length > 0)
  2212. ? $module.prev().find(selector.text)
  2213. : $module.prev()
  2214. ;
  2215. $menu = $module.children(selector.menu);
  2216. $item = $menu.find(selector.item);
  2217. $divider = settings.hideDividers ? $item.parent().children(selector.divider) : $();
  2218. },
  2219. refreshData: function() {
  2220. module.verbose('Refreshing cached metadata');
  2221. $item
  2222. .removeData(metadata.text)
  2223. .removeData(metadata.value)
  2224. ;
  2225. },
  2226. clearData: function() {
  2227. module.verbose('Clearing metadata');
  2228. $item
  2229. .removeData(metadata.text)
  2230. .removeData(metadata.value)
  2231. ;
  2232. $module
  2233. .removeData(metadata.defaultText)
  2234. .removeData(metadata.defaultValue)
  2235. .removeData(metadata.placeholderText)
  2236. ;
  2237. },
  2238. toggle: function() {
  2239. module.verbose('Toggling menu visibility');
  2240. if( !module.is.active() ) {
  2241. module.show();
  2242. }
  2243. else {
  2244. module.hide();
  2245. }
  2246. },
  2247. show: function(callback, preventFocus) {
  2248. callback = $.isFunction(callback)
  2249. ? callback
  2250. : function(){}
  2251. ;
  2252. if(!module.can.show() && module.is.remote()) {
  2253. module.debug('No API results retrieved, searching before show');
  2254. module.queryRemote(module.get.query(), module.show);
  2255. }
  2256. if( module.can.show() && !module.is.active() ) {
  2257. module.debug('Showing dropdown');
  2258. if(module.has.message() && !(module.has.maxSelections() || module.has.allResultsFiltered()) ) {
  2259. module.remove.message();
  2260. }
  2261. if(module.is.allFiltered()) {
  2262. return true;
  2263. }
  2264. if(settings.onShow.call(element) !== false) {
  2265. module.animate.show(function() {
  2266. if( module.can.click() ) {
  2267. module.bind.intent();
  2268. }
  2269. if(module.has.search() && !preventFocus) {
  2270. module.focusSearch();
  2271. }
  2272. module.set.visible();
  2273. callback.call(element);
  2274. });
  2275. }
  2276. }
  2277. },
  2278. hide: function(callback, preventBlur) {
  2279. callback = $.isFunction(callback)
  2280. ? callback
  2281. : function(){}
  2282. ;
  2283. if( module.is.active() && !module.is.animatingOutward() ) {
  2284. module.debug('Hiding dropdown');
  2285. if(settings.onHide.call(element) !== false) {
  2286. module.animate.hide(function() {
  2287. module.remove.visible();
  2288. // hidding search focus
  2289. if ( module.is.focusedOnSearch() && preventBlur !== true ) {
  2290. $search.blur();
  2291. }
  2292. callback.call(element);
  2293. });
  2294. }
  2295. } else if( module.can.click() ) {
  2296. module.unbind.intent();
  2297. }
  2298. iconClicked = false;
  2299. },
  2300. hideOthers: function() {
  2301. module.verbose('Finding other dropdowns to hide');
  2302. $allModules
  2303. .not($module)
  2304. .has(selector.menu + '.' + className.visible)
  2305. .dropdown('hide')
  2306. ;
  2307. },
  2308. hideMenu: function() {
  2309. module.verbose('Hiding menu instantaneously');
  2310. module.remove.active();
  2311. module.remove.visible();
  2312. $menu.transition('hide');
  2313. },
  2314. hideSubMenus: function() {
  2315. var
  2316. $subMenus = $menu.children(selector.item).find(selector.menu)
  2317. ;
  2318. module.verbose('Hiding sub menus', $subMenus);
  2319. $subMenus.transition('hide');
  2320. },
  2321. bind: {
  2322. events: function() {
  2323. module.bind.keyboardEvents();
  2324. module.bind.inputEvents();
  2325. module.bind.mouseEvents();
  2326. },
  2327. keyboardEvents: function() {
  2328. module.verbose('Binding keyboard events');
  2329. $module
  2330. .on('keydown' + eventNamespace, module.event.keydown)
  2331. ;
  2332. if( module.has.search() ) {
  2333. $module
  2334. .on(module.get.inputEvent() + eventNamespace, selector.search, module.event.input)
  2335. ;
  2336. }
  2337. if( module.is.multiple() ) {
  2338. $document
  2339. .on('keydown' + elementNamespace, module.event.document.keydown)
  2340. ;
  2341. }
  2342. },
  2343. inputEvents: function() {
  2344. module.verbose('Binding input change events');
  2345. $module
  2346. .on('change' + eventNamespace, selector.input, module.event.change)
  2347. ;
  2348. },
  2349. mouseEvents: function() {
  2350. module.verbose('Binding mouse events');
  2351. if(module.is.multiple()) {
  2352. $module
  2353. .on(clickEvent + eventNamespace, selector.label, module.event.label.click)
  2354. .on(clickEvent + eventNamespace, selector.remove, module.event.remove.click)
  2355. ;
  2356. }
  2357. if( module.is.searchSelection() ) {
  2358. $module
  2359. .on('mousedown' + eventNamespace, module.event.mousedown)
  2360. .on('mouseup' + eventNamespace, module.event.mouseup)
  2361. .on('mousedown' + eventNamespace, selector.menu, module.event.menu.mousedown)
  2362. .on('mouseup' + eventNamespace, selector.menu, module.event.menu.mouseup)
  2363. .on(clickEvent + eventNamespace, selector.icon, module.event.icon.click)
  2364. .on(clickEvent + eventNamespace, selector.clearIcon, module.event.clearIcon.click)
  2365. .on('focus' + eventNamespace, selector.search, module.event.search.focus)
  2366. .on(clickEvent + eventNamespace, selector.search, module.event.search.focus)
  2367. .on('blur' + eventNamespace, selector.search, module.event.search.blur)
  2368. .on(clickEvent + eventNamespace, selector.text, module.event.text.focus)
  2369. ;
  2370. if(module.is.multiple()) {
  2371. $module
  2372. .on(clickEvent + eventNamespace, module.event.click)
  2373. ;
  2374. }
  2375. }
  2376. else {
  2377. if(settings.on == 'click') {
  2378. $module
  2379. .on(clickEvent + eventNamespace, selector.icon, module.event.icon.click)
  2380. .on(clickEvent + eventNamespace, module.event.test.toggle)
  2381. ;
  2382. }
  2383. else if(settings.on == 'hover') {
  2384. $module
  2385. .on('mouseenter' + eventNamespace, module.delay.show)
  2386. .on('mouseleave' + eventNamespace, module.delay.hide)
  2387. ;
  2388. }
  2389. else {
  2390. $module
  2391. .on(settings.on + eventNamespace, module.toggle)
  2392. ;
  2393. }
  2394. $module
  2395. .on('mousedown' + eventNamespace, module.event.mousedown)
  2396. .on('mouseup' + eventNamespace, module.event.mouseup)
  2397. .on('focus' + eventNamespace, module.event.focus)
  2398. .on(clickEvent + eventNamespace, selector.clearIcon, module.event.clearIcon.click)
  2399. ;
  2400. if(module.has.menuSearch() ) {
  2401. $module
  2402. .on('blur' + eventNamespace, selector.search, module.event.search.blur)
  2403. ;
  2404. }
  2405. else {
  2406. $module
  2407. .on('blur' + eventNamespace, module.event.blur)
  2408. ;
  2409. }
  2410. }
  2411. $menu
  2412. .on((hasTouch ? 'touchstart' : 'mouseenter') + eventNamespace, selector.item, module.event.item.mouseenter)
  2413. .on('mouseleave' + eventNamespace, selector.item, module.event.item.mouseleave)
  2414. .on('click' + eventNamespace, selector.item, module.event.item.click)
  2415. ;
  2416. },
  2417. intent: function() {
  2418. module.verbose('Binding hide intent event to document');
  2419. if(hasTouch) {
  2420. $document
  2421. .on('touchstart' + elementNamespace, module.event.test.touch)
  2422. .on('touchmove' + elementNamespace, module.event.test.touch)
  2423. ;
  2424. }
  2425. $document
  2426. .on(clickEvent + elementNamespace, module.event.test.hide)
  2427. ;
  2428. }
  2429. },
  2430. unbind: {
  2431. intent: function() {
  2432. module.verbose('Removing hide intent event from document');
  2433. if(hasTouch) {
  2434. $document
  2435. .off('touchstart' + elementNamespace)
  2436. .off('touchmove' + elementNamespace)
  2437. ;
  2438. }
  2439. $document
  2440. .off(clickEvent + elementNamespace)
  2441. ;
  2442. }
  2443. },
  2444. filter: function(query) {
  2445. var
  2446. searchTerm = (query !== undefined)
  2447. ? query
  2448. : module.get.query(),
  2449. afterFiltered = function() {
  2450. if(module.is.multiple()) {
  2451. module.filterActive();
  2452. }
  2453. if(query || (!query && module.get.activeItem().length == 0)) {
  2454. module.select.firstUnfiltered();
  2455. }
  2456. if( module.has.allResultsFiltered() ) {
  2457. if( settings.onNoResults.call(element, searchTerm) ) {
  2458. if(settings.allowAdditions) {
  2459. if(settings.hideAdditions) {
  2460. module.verbose('User addition with no menu, setting empty style');
  2461. module.set.empty();
  2462. module.hideMenu();
  2463. }
  2464. }
  2465. else {
  2466. module.verbose('All items filtered, showing message', searchTerm);
  2467. module.add.message(message.noResults);
  2468. }
  2469. }
  2470. else {
  2471. module.verbose('All items filtered, hiding dropdown', searchTerm);
  2472. module.hideMenu();
  2473. }
  2474. }
  2475. else {
  2476. module.remove.empty();
  2477. module.remove.message();
  2478. }
  2479. if(settings.allowAdditions) {
  2480. module.add.userSuggestion(module.escape.htmlEntities(query));
  2481. }
  2482. if(module.is.searchSelection() && module.can.show() && module.is.focusedOnSearch() ) {
  2483. module.show();
  2484. }
  2485. }
  2486. ;
  2487. if(settings.useLabels && module.has.maxSelections()) {
  2488. return;
  2489. }
  2490. if(settings.apiSettings) {
  2491. if( module.can.useAPI() ) {
  2492. module.queryRemote(searchTerm, function() {
  2493. if(settings.filterRemoteData) {
  2494. module.filterItems(searchTerm);
  2495. }
  2496. var preSelected = $input.val();
  2497. if(!Array.isArray(preSelected)) {
  2498. preSelected = preSelected && preSelected!=="" ? preSelected.split(settings.delimiter) : [];
  2499. }
  2500. $.each(preSelected,function(index,value){
  2501. $item.filter('[data-value="'+value+'"]')
  2502. .addClass(className.filtered)
  2503. ;
  2504. });
  2505. afterFiltered();
  2506. });
  2507. }
  2508. else {
  2509. module.error(error.noAPI);
  2510. }
  2511. }
  2512. else {
  2513. module.filterItems(searchTerm);
  2514. afterFiltered();
  2515. }
  2516. },
  2517. queryRemote: function(query, callback) {
  2518. var
  2519. apiSettings = {
  2520. errorDuration : false,
  2521. cache : 'local',
  2522. throttle : settings.throttle,
  2523. urlData : {
  2524. query: query
  2525. },
  2526. onError: function() {
  2527. module.add.message(message.serverError);
  2528. callback();
  2529. },
  2530. onFailure: function() {
  2531. module.add.message(message.serverError);
  2532. callback();
  2533. },
  2534. onSuccess : function(response) {
  2535. var
  2536. values = response[fields.remoteValues]
  2537. ;
  2538. if (!Array.isArray(values)){
  2539. values = [];
  2540. }
  2541. module.remove.message();
  2542. var menuConfig = {};
  2543. menuConfig[fields.values] = values;
  2544. module.setup.menu(menuConfig);
  2545. if(values.length===0 && !settings.allowAdditions) {
  2546. module.add.message(message.noResults);
  2547. }
  2548. callback();
  2549. }
  2550. }
  2551. ;
  2552. if( !$module.api('get request') ) {
  2553. module.setup.api();
  2554. }
  2555. apiSettings = $.extend(true, {}, apiSettings, settings.apiSettings);
  2556. $module
  2557. .api('setting', apiSettings)
  2558. .api('query')
  2559. ;
  2560. },
  2561. filterItems: function(query) {
  2562. var
  2563. searchTerm = module.remove.diacritics(query !== undefined
  2564. ? query
  2565. : module.get.query()
  2566. ),
  2567. results = null,
  2568. escapedTerm = module.escape.string(searchTerm),
  2569. regExpFlags = (settings.ignoreSearchCase ? 'i' : '') + 'gm',
  2570. beginsWithRegExp = new RegExp('^' + escapedTerm, regExpFlags)
  2571. ;
  2572. // avoid loop if we're matching nothing
  2573. if( module.has.query() ) {
  2574. results = [];
  2575. module.verbose('Searching for matching values', searchTerm);
  2576. $item
  2577. .each(function(){
  2578. var
  2579. $choice = $(this),
  2580. text,
  2581. value
  2582. ;
  2583. if($choice.hasClass(className.unfilterable)) {
  2584. results.push(this);
  2585. return true;
  2586. }
  2587. if(settings.match === 'both' || settings.match === 'text') {
  2588. text = module.remove.diacritics(String(module.get.choiceText($choice, false)));
  2589. if(text.search(beginsWithRegExp) !== -1) {
  2590. results.push(this);
  2591. return true;
  2592. }
  2593. else if (settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, text)) {
  2594. results.push(this);
  2595. return true;
  2596. }
  2597. else if (settings.fullTextSearch === true && module.fuzzySearch(searchTerm, text)) {
  2598. results.push(this);
  2599. return true;
  2600. }
  2601. }
  2602. if(settings.match === 'both' || settings.match === 'value') {
  2603. value = module.remove.diacritics(String(module.get.choiceValue($choice, text)));
  2604. if(value.search(beginsWithRegExp) !== -1) {
  2605. results.push(this);
  2606. return true;
  2607. }
  2608. else if (settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, value)) {
  2609. results.push(this);
  2610. return true;
  2611. }
  2612. else if (settings.fullTextSearch === true && module.fuzzySearch(searchTerm, value)) {
  2613. results.push(this);
  2614. return true;
  2615. }
  2616. }
  2617. })
  2618. ;
  2619. }
  2620. module.debug('Showing only matched items', searchTerm);
  2621. module.remove.filteredItem();
  2622. if(results) {
  2623. $item
  2624. .not(results)
  2625. .addClass(className.filtered)
  2626. ;
  2627. }
  2628. if(!module.has.query()) {
  2629. $divider
  2630. .removeClass(className.hidden);
  2631. } else if(settings.hideDividers === true) {
  2632. $divider
  2633. .addClass(className.hidden);
  2634. } else if(settings.hideDividers === 'empty') {
  2635. $divider
  2636. .removeClass(className.hidden)
  2637. .filter(function() {
  2638. // First find the last divider in this divider group
  2639. // Dividers which are direct siblings are considered a group
  2640. var lastDivider = $(this).nextUntil(selector.item);
  2641. return (lastDivider.length ? lastDivider : $(this))
  2642. // Count all non-filtered items until the next divider (or end of the dropdown)
  2643. .nextUntil(selector.divider)
  2644. .filter(selector.item + ":not(." + className.filtered + ")")
  2645. // Hide divider if no items are found
  2646. .length === 0;
  2647. })
  2648. .addClass(className.hidden);
  2649. }
  2650. },
  2651. fuzzySearch: function(query, term) {
  2652. var
  2653. termLength = term.length,
  2654. queryLength = query.length
  2655. ;
  2656. query = (settings.ignoreSearchCase ? query.toLowerCase() : query);
  2657. term = (settings.ignoreSearchCase ? term.toLowerCase() : term);
  2658. if(queryLength > termLength) {
  2659. return false;
  2660. }
  2661. if(queryLength === termLength) {
  2662. return (query === term);
  2663. }
  2664. search: for (var characterIndex = 0, nextCharacterIndex = 0; characterIndex < queryLength; characterIndex++) {
  2665. var
  2666. queryCharacter = query.charCodeAt(characterIndex)
  2667. ;
  2668. while(nextCharacterIndex < termLength) {
  2669. if(term.charCodeAt(nextCharacterIndex++) === queryCharacter) {
  2670. continue search;
  2671. }
  2672. }
  2673. return false;
  2674. }
  2675. return true;
  2676. },
  2677. exactSearch: function (query, term) {
  2678. query = (settings.ignoreSearchCase ? query.toLowerCase() : query);
  2679. term = (settings.ignoreSearchCase ? term.toLowerCase() : term);
  2680. return term.indexOf(query) > -1;
  2681. },
  2682. filterActive: function() {
  2683. if(settings.useLabels) {
  2684. $item.filter('.' + className.active)
  2685. .addClass(className.filtered)
  2686. ;
  2687. }
  2688. },
  2689. focusSearch: function(skipHandler) {
  2690. if( module.has.search() && !module.is.focusedOnSearch() ) {
  2691. if(skipHandler) {
  2692. $module.off('focus' + eventNamespace, selector.search);
  2693. $search.focus();
  2694. $module.on('focus' + eventNamespace, selector.search, module.event.search.focus);
  2695. }
  2696. else {
  2697. $search.focus();
  2698. }
  2699. }
  2700. },
  2701. blurSearch: function() {
  2702. if( module.has.search() ) {
  2703. $search.blur();
  2704. }
  2705. },
  2706. forceSelection: function() {
  2707. var
  2708. $currentlySelected = $item.not(className.filtered).filter('.' + className.selected).eq(0),
  2709. $activeItem = $item.not(className.filtered).filter('.' + className.active).eq(0),
  2710. $selectedItem = ($currentlySelected.length > 0)
  2711. ? $currentlySelected
  2712. : $activeItem,
  2713. hasSelected = ($selectedItem.length > 0)
  2714. ;
  2715. if(settings.allowAdditions || (hasSelected && !module.is.multiple())) {
  2716. module.debug('Forcing partial selection to selected item', $selectedItem);
  2717. module.event.item.click.call($selectedItem, {}, true);
  2718. }
  2719. else {
  2720. module.remove.searchTerm();
  2721. }
  2722. },
  2723. change: {
  2724. values: function(values) {
  2725. if(!settings.allowAdditions) {
  2726. module.clear();
  2727. }
  2728. module.debug('Creating dropdown with specified values', values);
  2729. var menuConfig = {};
  2730. menuConfig[fields.values] = values;
  2731. module.setup.menu(menuConfig);
  2732. $.each(values, function(index, item) {
  2733. if(item.selected == true) {
  2734. module.debug('Setting initial selection to', item[fields.value]);
  2735. module.set.selected(item[fields.value]);
  2736. if(!module.is.multiple()) {
  2737. return false;
  2738. }
  2739. }
  2740. });
  2741. if(module.has.selectInput()) {
  2742. module.disconnect.selectObserver();
  2743. $input.html('');
  2744. $input.append('<option disabled selected value></option>');
  2745. $.each(values, function(index, item) {
  2746. var
  2747. value = settings.templates.deQuote(item[fields.value]),
  2748. name = settings.templates.escape(
  2749. item[fields.name] || '',
  2750. settings.preserveHTML
  2751. )
  2752. ;
  2753. $input.append('<option value="' + value + '">' + name + '</option>');
  2754. });
  2755. module.observe.select();
  2756. }
  2757. }
  2758. },
  2759. event: {
  2760. change: function() {
  2761. if(!internalChange) {
  2762. module.debug('Input changed, updating selection');
  2763. module.set.selected();
  2764. }
  2765. },
  2766. focus: function() {
  2767. if(settings.showOnFocus && !activated && module.is.hidden() && !pageLostFocus) {
  2768. module.show();
  2769. }
  2770. },
  2771. blur: function(event) {
  2772. pageLostFocus = (document.activeElement === this);
  2773. if(!activated && !pageLostFocus) {
  2774. module.remove.activeLabel();
  2775. module.hide();
  2776. }
  2777. },
  2778. mousedown: function() {
  2779. if(module.is.searchSelection()) {
  2780. // prevent menu hiding on immediate re-focus
  2781. willRefocus = true;
  2782. }
  2783. else {
  2784. // prevents focus callback from occurring on mousedown
  2785. activated = true;
  2786. }
  2787. },
  2788. mouseup: function() {
  2789. if(module.is.searchSelection()) {
  2790. // prevent menu hiding on immediate re-focus
  2791. willRefocus = false;
  2792. }
  2793. else {
  2794. activated = false;
  2795. }
  2796. },
  2797. click: function(event) {
  2798. var
  2799. $target = $(event.target)
  2800. ;
  2801. // focus search
  2802. if($target.is($module)) {
  2803. if(!module.is.focusedOnSearch()) {
  2804. module.focusSearch();
  2805. }
  2806. else {
  2807. module.show();
  2808. }
  2809. }
  2810. },
  2811. search: {
  2812. focus: function(event) {
  2813. activated = true;
  2814. if(module.is.multiple()) {
  2815. module.remove.activeLabel();
  2816. }
  2817. if(settings.showOnFocus || (event.type !== 'focus' && event.type !== 'focusin')) {
  2818. module.search();
  2819. }
  2820. },
  2821. blur: function(event) {
  2822. pageLostFocus = (document.activeElement === this);
  2823. if(module.is.searchSelection() && !willRefocus) {
  2824. if(!itemActivated && !pageLostFocus) {
  2825. if(settings.forceSelection) {
  2826. module.forceSelection();
  2827. } else if(!settings.allowAdditions){
  2828. module.remove.searchTerm();
  2829. }
  2830. module.hide();
  2831. }
  2832. }
  2833. willRefocus = false;
  2834. }
  2835. },
  2836. clearIcon: {
  2837. click: function(event) {
  2838. module.clear();
  2839. if(module.is.searchSelection()) {
  2840. module.remove.searchTerm();
  2841. }
  2842. module.hide();
  2843. event.stopPropagation();
  2844. }
  2845. },
  2846. icon: {
  2847. click: function(event) {
  2848. iconClicked=true;
  2849. if(module.has.search()) {
  2850. if(!module.is.active()) {
  2851. if(settings.showOnFocus){
  2852. module.focusSearch();
  2853. } else {
  2854. module.toggle();
  2855. }
  2856. } else {
  2857. module.blurSearch();
  2858. }
  2859. } else {
  2860. module.toggle();
  2861. }
  2862. }
  2863. },
  2864. text: {
  2865. focus: function(event) {
  2866. activated = true;
  2867. module.focusSearch();
  2868. }
  2869. },
  2870. input: function(event) {
  2871. if(module.is.multiple() || module.is.searchSelection()) {
  2872. module.set.filtered();
  2873. }
  2874. clearTimeout(module.timer);
  2875. module.timer = setTimeout(module.search, settings.delay.search);
  2876. },
  2877. label: {
  2878. click: function(event) {
  2879. var
  2880. $label = $(this),
  2881. $labels = $module.find(selector.label),
  2882. $activeLabels = $labels.filter('.' + className.active),
  2883. $nextActive = $label.nextAll('.' + className.active),
  2884. $prevActive = $label.prevAll('.' + className.active),
  2885. $range = ($nextActive.length > 0)
  2886. ? $label.nextUntil($nextActive).add($activeLabels).add($label)
  2887. : $label.prevUntil($prevActive).add($activeLabels).add($label)
  2888. ;
  2889. if(event.shiftKey) {
  2890. $activeLabels.removeClass(className.active);
  2891. $range.addClass(className.active);
  2892. }
  2893. else if(event.ctrlKey) {
  2894. $label.toggleClass(className.active);
  2895. }
  2896. else {
  2897. $activeLabels.removeClass(className.active);
  2898. $label.addClass(className.active);
  2899. }
  2900. settings.onLabelSelect.apply(this, $labels.filter('.' + className.active));
  2901. }
  2902. },
  2903. remove: {
  2904. click: function() {
  2905. var
  2906. $label = $(this).parent()
  2907. ;
  2908. if( $label.hasClass(className.active) ) {
  2909. // remove all selected labels
  2910. module.remove.activeLabels();
  2911. }
  2912. else {
  2913. // remove this label only
  2914. module.remove.activeLabels( $label );
  2915. }
  2916. }
  2917. },
  2918. test: {
  2919. toggle: function(event) {
  2920. var
  2921. toggleBehavior = (module.is.multiple())
  2922. ? module.show
  2923. : module.toggle
  2924. ;
  2925. if(module.is.bubbledLabelClick(event) || module.is.bubbledIconClick(event)) {
  2926. return;
  2927. }
  2928. if( module.determine.eventOnElement(event, toggleBehavior) ) {
  2929. event.preventDefault();
  2930. }
  2931. },
  2932. touch: function(event) {
  2933. module.determine.eventOnElement(event, function() {
  2934. if(event.type == 'touchstart') {
  2935. module.timer = setTimeout(function() {
  2936. module.hide();
  2937. }, settings.delay.touch);
  2938. }
  2939. else if(event.type == 'touchmove') {
  2940. clearTimeout(module.timer);
  2941. }
  2942. });
  2943. event.stopPropagation();
  2944. },
  2945. hide: function(event) {
  2946. if(module.determine.eventInModule(event, module.hide)){
  2947. if(element.id && $(event.target).attr('for') === element.id){
  2948. event.preventDefault();
  2949. }
  2950. }
  2951. }
  2952. },
  2953. class: {
  2954. mutation: function(mutations) {
  2955. mutations.forEach(function(mutation) {
  2956. if(mutation.attributeName === "class") {
  2957. module.check.disabled();
  2958. }
  2959. });
  2960. }
  2961. },
  2962. select: {
  2963. mutation: function(mutations) {
  2964. module.debug('<select> modified, recreating menu');
  2965. if(module.is.selectMutation(mutations)) {
  2966. module.disconnect.selectObserver();
  2967. module.refresh();
  2968. module.setup.select();
  2969. module.set.selected();
  2970. module.observe.select();
  2971. }
  2972. }
  2973. },
  2974. menu: {
  2975. mutation: function(mutations) {
  2976. var
  2977. mutation = mutations[0],
  2978. $addedNode = mutation.addedNodes
  2979. ? $(mutation.addedNodes[0])
  2980. : $(false),
  2981. $removedNode = mutation.removedNodes
  2982. ? $(mutation.removedNodes[0])
  2983. : $(false),
  2984. $changedNodes = $addedNode.add($removedNode),
  2985. isUserAddition = $changedNodes.is(selector.addition) || $changedNodes.closest(selector.addition).length > 0,
  2986. isMessage = $changedNodes.is(selector.message) || $changedNodes.closest(selector.message).length > 0
  2987. ;
  2988. if(isUserAddition || isMessage) {
  2989. module.debug('Updating item selector cache');
  2990. module.refreshItems();
  2991. }
  2992. else {
  2993. module.debug('Menu modified, updating selector cache');
  2994. module.refresh();
  2995. }
  2996. },
  2997. mousedown: function() {
  2998. itemActivated = true;
  2999. },
  3000. mouseup: function() {
  3001. itemActivated = false;
  3002. }
  3003. },
  3004. item: {
  3005. mouseenter: function(event) {
  3006. var
  3007. $target = $(event.target),
  3008. $item = $(this),
  3009. $subMenu = $item.children(selector.menu),
  3010. $otherMenus = $item.siblings(selector.item).children(selector.menu),
  3011. hasSubMenu = ($subMenu.length > 0),
  3012. isBubbledEvent = ($subMenu.find($target).length > 0)
  3013. ;
  3014. if( !isBubbledEvent && hasSubMenu ) {
  3015. clearTimeout(module.itemTimer);
  3016. module.itemTimer = setTimeout(function() {
  3017. module.verbose('Showing sub-menu', $subMenu);
  3018. $.each($otherMenus, function() {
  3019. module.animate.hide(false, $(this));
  3020. });
  3021. module.animate.show(false, $subMenu);
  3022. }, settings.delay.show);
  3023. event.preventDefault();
  3024. }
  3025. },
  3026. mouseleave: function(event) {
  3027. var
  3028. $subMenu = $(this).children(selector.menu)
  3029. ;
  3030. if($subMenu.length > 0) {
  3031. clearTimeout(module.itemTimer);
  3032. module.itemTimer = setTimeout(function() {
  3033. module.verbose('Hiding sub-menu', $subMenu);
  3034. module.animate.hide(false, $subMenu);
  3035. }, settings.delay.hide);
  3036. }
  3037. },
  3038. click: function (event, skipRefocus) {
  3039. var
  3040. $choice = $(this),
  3041. $target = (event)
  3042. ? $(event.target)
  3043. : $(''),
  3044. $subMenu = $choice.find(selector.menu),
  3045. text = module.get.choiceText($choice),
  3046. value = module.get.choiceValue($choice, text),
  3047. hasSubMenu = ($subMenu.length > 0),
  3048. isBubbledEvent = ($subMenu.find($target).length > 0)
  3049. ;
  3050. // prevents IE11 bug where menu receives focus even though `tabindex=-1`
  3051. if (document.activeElement.tagName.toLowerCase() !== 'input') {
  3052. $(document.activeElement).blur();
  3053. }
  3054. if(!isBubbledEvent && (!hasSubMenu || settings.allowCategorySelection)) {
  3055. if(module.is.searchSelection()) {
  3056. if(settings.allowAdditions) {
  3057. module.remove.userAddition();
  3058. }
  3059. module.remove.searchTerm();
  3060. if(!module.is.focusedOnSearch() && !(skipRefocus == true)) {
  3061. module.focusSearch(true);
  3062. }
  3063. }
  3064. if(!settings.useLabels) {
  3065. module.remove.filteredItem();
  3066. module.set.scrollPosition($choice);
  3067. }
  3068. module.determine.selectAction.call(this, text, value);
  3069. }
  3070. }
  3071. },
  3072. document: {
  3073. // label selection should occur even when element has no focus
  3074. keydown: function(event) {
  3075. var
  3076. pressedKey = event.which,
  3077. isShortcutKey = module.is.inObject(pressedKey, keys)
  3078. ;
  3079. if(isShortcutKey) {
  3080. var
  3081. $label = $module.find(selector.label),
  3082. $activeLabel = $label.filter('.' + className.active),
  3083. activeValue = $activeLabel.data(metadata.value),
  3084. labelIndex = $label.index($activeLabel),
  3085. labelCount = $label.length,
  3086. hasActiveLabel = ($activeLabel.length > 0),
  3087. hasMultipleActive = ($activeLabel.length > 1),
  3088. isFirstLabel = (labelIndex === 0),
  3089. isLastLabel = (labelIndex + 1 == labelCount),
  3090. isSearch = module.is.searchSelection(),
  3091. isFocusedOnSearch = module.is.focusedOnSearch(),
  3092. isFocused = module.is.focused(),
  3093. caretAtStart = (isFocusedOnSearch && module.get.caretPosition(false) === 0),
  3094. isSelectedSearch = (caretAtStart && module.get.caretPosition(true) !== 0),
  3095. $nextLabel
  3096. ;
  3097. if(isSearch && !hasActiveLabel && !isFocusedOnSearch) {
  3098. return;
  3099. }
  3100. if(pressedKey == keys.leftArrow) {
  3101. // activate previous label
  3102. if((isFocused || caretAtStart) && !hasActiveLabel) {
  3103. module.verbose('Selecting previous label');
  3104. $label.last().addClass(className.active);
  3105. }
  3106. else if(hasActiveLabel) {
  3107. if(!event.shiftKey) {
  3108. module.verbose('Selecting previous label');
  3109. $label.removeClass(className.active);
  3110. }
  3111. else {
  3112. module.verbose('Adding previous label to selection');
  3113. }
  3114. if(isFirstLabel && !hasMultipleActive) {
  3115. $activeLabel.addClass(className.active);
  3116. }
  3117. else {
  3118. $activeLabel.prev(selector.siblingLabel)
  3119. .addClass(className.active)
  3120. .end()
  3121. ;
  3122. }
  3123. event.preventDefault();
  3124. }
  3125. }
  3126. else if(pressedKey == keys.rightArrow) {
  3127. // activate first label
  3128. if(isFocused && !hasActiveLabel) {
  3129. $label.first().addClass(className.active);
  3130. }
  3131. // activate next label
  3132. if(hasActiveLabel) {
  3133. if(!event.shiftKey) {
  3134. module.verbose('Selecting next label');
  3135. $label.removeClass(className.active);
  3136. }
  3137. else {
  3138. module.verbose('Adding next label to selection');
  3139. }
  3140. if(isLastLabel) {
  3141. if(isSearch) {
  3142. if(!isFocusedOnSearch) {
  3143. module.focusSearch();
  3144. }
  3145. else {
  3146. $label.removeClass(className.active);
  3147. }
  3148. }
  3149. else if(hasMultipleActive) {
  3150. $activeLabel.next(selector.siblingLabel).addClass(className.active);
  3151. }
  3152. else {
  3153. $activeLabel.addClass(className.active);
  3154. }
  3155. }
  3156. else {
  3157. $activeLabel.next(selector.siblingLabel).addClass(className.active);
  3158. }
  3159. event.preventDefault();
  3160. }
  3161. }
  3162. else if(pressedKey == keys.deleteKey || pressedKey == keys.backspace) {
  3163. if(hasActiveLabel) {
  3164. module.verbose('Removing active labels');
  3165. if(isLastLabel) {
  3166. if(isSearch && !isFocusedOnSearch) {
  3167. module.focusSearch();
  3168. }
  3169. }
  3170. $activeLabel.last().next(selector.siblingLabel).addClass(className.active);
  3171. module.remove.activeLabels($activeLabel);
  3172. event.preventDefault();
  3173. }
  3174. else if(caretAtStart && !isSelectedSearch && !hasActiveLabel && pressedKey == keys.backspace) {
  3175. module.verbose('Removing last label on input backspace');
  3176. $activeLabel = $label.last().addClass(className.active);
  3177. module.remove.activeLabels($activeLabel);
  3178. }
  3179. }
  3180. else {
  3181. $activeLabel.removeClass(className.active);
  3182. }
  3183. }
  3184. }
  3185. },
  3186. keydown: function(event) {
  3187. var
  3188. pressedKey = event.which,
  3189. isShortcutKey = module.is.inObject(pressedKey, keys)
  3190. ;
  3191. if(isShortcutKey) {
  3192. var
  3193. $currentlySelected = $item.not(selector.unselectable).filter('.' + className.selected).eq(0),
  3194. $activeItem = $menu.children('.' + className.active).eq(0),
  3195. $selectedItem = ($currentlySelected.length > 0)
  3196. ? $currentlySelected
  3197. : $activeItem,
  3198. $visibleItems = ($selectedItem.length > 0)
  3199. ? $selectedItem.siblings(':not(.' + className.filtered +')').addBack()
  3200. : $menu.children(':not(.' + className.filtered +')'),
  3201. $subMenu = $selectedItem.children(selector.menu),
  3202. $parentMenu = $selectedItem.closest(selector.menu),
  3203. inVisibleMenu = ($parentMenu.hasClass(className.visible) || $parentMenu.hasClass(className.animating) || $parentMenu.parent(selector.menu).length > 0),
  3204. hasSubMenu = ($subMenu.length> 0),
  3205. hasSelectedItem = ($selectedItem.length > 0),
  3206. selectedIsSelectable = ($selectedItem.not(selector.unselectable).length > 0),
  3207. delimiterPressed = (pressedKey == keys.delimiter && settings.allowAdditions && module.is.multiple()),
  3208. isAdditionWithoutMenu = (settings.allowAdditions && settings.hideAdditions && (pressedKey == keys.enter || delimiterPressed) && selectedIsSelectable),
  3209. $nextItem,
  3210. isSubMenuItem,
  3211. newIndex
  3212. ;
  3213. // allow selection with menu closed
  3214. if(isAdditionWithoutMenu) {
  3215. module.verbose('Selecting item from keyboard shortcut', $selectedItem);
  3216. module.event.item.click.call($selectedItem, event);
  3217. if(module.is.searchSelection()) {
  3218. module.remove.searchTerm();
  3219. }
  3220. if(module.is.multiple()){
  3221. event.preventDefault();
  3222. }
  3223. }
  3224. // visible menu keyboard shortcuts
  3225. if( module.is.visible() ) {
  3226. // enter (select or open sub-menu)
  3227. if(pressedKey == keys.enter || delimiterPressed) {
  3228. if(pressedKey == keys.enter && hasSelectedItem && hasSubMenu && !settings.allowCategorySelection) {
  3229. module.verbose('Pressed enter on unselectable category, opening sub menu');
  3230. pressedKey = keys.rightArrow;
  3231. }
  3232. else if(selectedIsSelectable) {
  3233. module.verbose('Selecting item from keyboard shortcut', $selectedItem);
  3234. module.event.item.click.call($selectedItem, event);
  3235. if(module.is.searchSelection()) {
  3236. module.remove.searchTerm();
  3237. if(module.is.multiple()) {
  3238. $search.focus();
  3239. }
  3240. }
  3241. }
  3242. event.preventDefault();
  3243. }
  3244. // sub-menu actions
  3245. if(hasSelectedItem) {
  3246. if(pressedKey == keys.leftArrow) {
  3247. isSubMenuItem = ($parentMenu[0] !== $menu[0]);
  3248. if(isSubMenuItem) {
  3249. module.verbose('Left key pressed, closing sub-menu');
  3250. module.animate.hide(false, $parentMenu);
  3251. $selectedItem
  3252. .removeClass(className.selected)
  3253. ;
  3254. $parentMenu
  3255. .closest(selector.item)
  3256. .addClass(className.selected)
  3257. ;
  3258. event.preventDefault();
  3259. }
  3260. }
  3261. // right arrow (show sub-menu)
  3262. if(pressedKey == keys.rightArrow) {
  3263. if(hasSubMenu) {
  3264. module.verbose('Right key pressed, opening sub-menu');
  3265. module.animate.show(false, $subMenu);
  3266. $selectedItem
  3267. .removeClass(className.selected)
  3268. ;
  3269. $subMenu
  3270. .find(selector.item).eq(0)
  3271. .addClass(className.selected)
  3272. ;
  3273. event.preventDefault();
  3274. }
  3275. }
  3276. }
  3277. // up arrow (traverse menu up)
  3278. if(pressedKey == keys.upArrow) {
  3279. $nextItem = (hasSelectedItem && inVisibleMenu)
  3280. ? $selectedItem.prevAll(selector.item + ':not(' + selector.unselectable + ')').eq(0)
  3281. : $item.eq(0)
  3282. ;
  3283. if($visibleItems.index( $nextItem ) < 0) {
  3284. module.verbose('Up key pressed but reached top of current menu');
  3285. event.preventDefault();
  3286. return;
  3287. }
  3288. else {
  3289. module.verbose('Up key pressed, changing active item');
  3290. $selectedItem
  3291. .removeClass(className.selected)
  3292. ;
  3293. $nextItem
  3294. .addClass(className.selected)
  3295. ;
  3296. module.set.scrollPosition($nextItem);
  3297. if(settings.selectOnKeydown && module.is.single()) {
  3298. module.set.selectedItem($nextItem);
  3299. }
  3300. }
  3301. event.preventDefault();
  3302. }
  3303. // down arrow (traverse menu down)
  3304. if(pressedKey == keys.downArrow) {
  3305. $nextItem = (hasSelectedItem && inVisibleMenu)
  3306. ? $nextItem = $selectedItem.nextAll(selector.item + ':not(' + selector.unselectable + ')').eq(0)
  3307. : $item.eq(0)
  3308. ;
  3309. if($nextItem.length === 0) {
  3310. module.verbose('Down key pressed but reached bottom of current menu');
  3311. event.preventDefault();
  3312. return;
  3313. }
  3314. else {
  3315. module.verbose('Down key pressed, changing active item');
  3316. $item
  3317. .removeClass(className.selected)
  3318. ;
  3319. $nextItem
  3320. .addClass(className.selected)
  3321. ;
  3322. module.set.scrollPosition($nextItem);
  3323. if(settings.selectOnKeydown && module.is.single()) {
  3324. module.set.selectedItem($nextItem);
  3325. }
  3326. }
  3327. event.preventDefault();
  3328. }
  3329. // page down (show next page)
  3330. if(pressedKey == keys.pageUp) {
  3331. module.scrollPage('up');
  3332. event.preventDefault();
  3333. }
  3334. if(pressedKey == keys.pageDown) {
  3335. module.scrollPage('down');
  3336. event.preventDefault();
  3337. }
  3338. // escape (close menu)
  3339. if(pressedKey == keys.escape) {
  3340. module.verbose('Escape key pressed, closing dropdown');
  3341. module.hide();
  3342. }
  3343. }
  3344. else {
  3345. // delimiter key
  3346. if(delimiterPressed) {
  3347. event.preventDefault();
  3348. }
  3349. // down arrow (open menu)
  3350. if(pressedKey == keys.downArrow && !module.is.visible()) {
  3351. module.verbose('Down key pressed, showing dropdown');
  3352. module.show();
  3353. event.preventDefault();
  3354. }
  3355. }
  3356. }
  3357. else {
  3358. if( !module.has.search() ) {
  3359. module.set.selectedLetter( String.fromCharCode(pressedKey) );
  3360. }
  3361. }
  3362. }
  3363. },
  3364. trigger: {
  3365. change: function() {
  3366. var
  3367. inputElement = $input[0]
  3368. ;
  3369. if(inputElement) {
  3370. var events = document.createEvent('HTMLEvents');
  3371. module.verbose('Triggering native change event');
  3372. events.initEvent('change', true, false);
  3373. inputElement.dispatchEvent(events);
  3374. }
  3375. }
  3376. },
  3377. determine: {
  3378. selectAction: function(text, value) {
  3379. selectActionActive = true;
  3380. module.verbose('Determining action', settings.action);
  3381. if( $.isFunction( module.action[settings.action] ) ) {
  3382. module.verbose('Triggering preset action', settings.action, text, value);
  3383. module.action[ settings.action ].call(element, text, value, this);
  3384. }
  3385. else if( $.isFunction(settings.action) ) {
  3386. module.verbose('Triggering user action', settings.action, text, value);
  3387. settings.action.call(element, text, value, this);
  3388. }
  3389. else {
  3390. module.error(error.action, settings.action);
  3391. }
  3392. selectActionActive = false;
  3393. },
  3394. eventInModule: function(event, callback) {
  3395. var
  3396. $target = $(event.target),
  3397. inDocument = ($target.closest(document.documentElement).length > 0),
  3398. inModule = ($target.closest($module).length > 0)
  3399. ;
  3400. callback = $.isFunction(callback)
  3401. ? callback
  3402. : function(){}
  3403. ;
  3404. if(inDocument && !inModule) {
  3405. module.verbose('Triggering event', callback);
  3406. callback();
  3407. return true;
  3408. }
  3409. else {
  3410. module.verbose('Event occurred in dropdown, canceling callback');
  3411. return false;
  3412. }
  3413. },
  3414. eventOnElement: function(event, callback) {
  3415. var
  3416. $target = $(event.target),
  3417. $label = $target.closest(selector.siblingLabel),
  3418. inVisibleDOM = document.body.contains(event.target),
  3419. notOnLabel = ($module.find($label).length === 0 || !(module.is.multiple() && settings.useLabels)),
  3420. notInMenu = ($target.closest($menu).length === 0)
  3421. ;
  3422. callback = $.isFunction(callback)
  3423. ? callback
  3424. : function(){}
  3425. ;
  3426. if(inVisibleDOM && notOnLabel && notInMenu) {
  3427. module.verbose('Triggering event', callback);
  3428. callback();
  3429. return true;
  3430. }
  3431. else {
  3432. module.verbose('Event occurred in dropdown menu, canceling callback');
  3433. return false;
  3434. }
  3435. }
  3436. },
  3437. action: {
  3438. nothing: function() {},
  3439. activate: function(text, value, element) {
  3440. value = (value !== undefined)
  3441. ? value
  3442. : text
  3443. ;
  3444. if( module.can.activate( $(element) ) ) {
  3445. module.set.selected(value, $(element));
  3446. if(!module.is.multiple()) {
  3447. module.hideAndClear();
  3448. }
  3449. }
  3450. },
  3451. select: function(text, value, element) {
  3452. value = (value !== undefined)
  3453. ? value
  3454. : text
  3455. ;
  3456. if( module.can.activate( $(element) ) ) {
  3457. module.set.value(value, text, $(element));
  3458. if(!module.is.multiple()) {
  3459. module.hideAndClear();
  3460. }
  3461. }
  3462. },
  3463. combo: function(text, value, element) {
  3464. value = (value !== undefined)
  3465. ? value
  3466. : text
  3467. ;
  3468. module.set.selected(value, $(element));
  3469. module.hideAndClear();
  3470. },
  3471. hide: function(text, value, element) {
  3472. module.set.value(value, text, $(element));
  3473. module.hideAndClear();
  3474. }
  3475. },
  3476. get: {
  3477. id: function() {
  3478. return id;
  3479. },
  3480. defaultText: function() {
  3481. return $module.data(metadata.defaultText);
  3482. },
  3483. defaultValue: function() {
  3484. return $module.data(metadata.defaultValue);
  3485. },
  3486. placeholderText: function() {
  3487. if(settings.placeholder != 'auto' && typeof settings.placeholder == 'string') {
  3488. return settings.placeholder;
  3489. }
  3490. return $module.data(metadata.placeholderText) || '';
  3491. },
  3492. text: function() {
  3493. return settings.preserveHTML ? $text.html() : $text.text();
  3494. },
  3495. query: function() {
  3496. return String($search.val()).trim();
  3497. },
  3498. searchWidth: function(value) {
  3499. value = (value !== undefined)
  3500. ? value
  3501. : $search.val()
  3502. ;
  3503. $sizer.text(value);
  3504. // prevent rounding issues
  3505. return Math.ceil( $sizer.width() + 1);
  3506. },
  3507. selectionCount: function() {
  3508. var
  3509. values = module.get.values(),
  3510. count
  3511. ;
  3512. count = ( module.is.multiple() )
  3513. ? Array.isArray(values)
  3514. ? values.length
  3515. : 0
  3516. : (module.get.value() !== '')
  3517. ? 1
  3518. : 0
  3519. ;
  3520. return count;
  3521. },
  3522. transition: function($subMenu) {
  3523. return (settings.transition == 'auto')
  3524. ? module.is.upward($subMenu)
  3525. ? 'slide up'
  3526. : 'slide down'
  3527. : settings.transition
  3528. ;
  3529. },
  3530. userValues: function() {
  3531. var
  3532. values = module.get.values()
  3533. ;
  3534. if(!values) {
  3535. return false;
  3536. }
  3537. values = Array.isArray(values)
  3538. ? values
  3539. : [values]
  3540. ;
  3541. return $.grep(values, function(value) {
  3542. return (module.get.item(value) === false);
  3543. });
  3544. },
  3545. uniqueArray: function(array) {
  3546. return $.grep(array, function (value, index) {
  3547. return $.inArray(value, array) === index;
  3548. });
  3549. },
  3550. caretPosition: function(returnEndPos) {
  3551. var
  3552. input = $search.get(0),
  3553. range,
  3554. rangeLength
  3555. ;
  3556. if(returnEndPos && 'selectionEnd' in input){
  3557. return input.selectionEnd;
  3558. }
  3559. else if(!returnEndPos && 'selectionStart' in input) {
  3560. return input.selectionStart;
  3561. }
  3562. if (document.selection) {
  3563. input.focus();
  3564. range = document.selection.createRange();
  3565. rangeLength = range.text.length;
  3566. if(returnEndPos) {
  3567. return rangeLength;
  3568. }
  3569. range.moveStart('character', -input.value.length);
  3570. return range.text.length - rangeLength;
  3571. }
  3572. },
  3573. value: function() {
  3574. var
  3575. value = ($input.length > 0)
  3576. ? $input.val()
  3577. : $module.data(metadata.value),
  3578. isEmptyMultiselect = (Array.isArray(value) && value.length === 1 && value[0] === '')
  3579. ;
  3580. // prevents placeholder element from being selected when multiple
  3581. return (value === undefined || isEmptyMultiselect)
  3582. ? ''
  3583. : value
  3584. ;
  3585. },
  3586. values: function() {
  3587. var
  3588. value = module.get.value()
  3589. ;
  3590. if(value === '') {
  3591. return '';
  3592. }
  3593. return ( !module.has.selectInput() && module.is.multiple() )
  3594. ? (typeof value == 'string') // delimited string
  3595. ? module.escape.htmlEntities(value).split(settings.delimiter)
  3596. : ''
  3597. : value
  3598. ;
  3599. },
  3600. remoteValues: function() {
  3601. var
  3602. values = module.get.values(),
  3603. remoteValues = false
  3604. ;
  3605. if(values) {
  3606. if(typeof values == 'string') {
  3607. values = [values];
  3608. }
  3609. $.each(values, function(index, value) {
  3610. var
  3611. name = module.read.remoteData(value)
  3612. ;
  3613. module.verbose('Restoring value from session data', name, value);
  3614. if(name) {
  3615. if(!remoteValues) {
  3616. remoteValues = {};
  3617. }
  3618. remoteValues[value] = name;
  3619. }
  3620. });
  3621. }
  3622. return remoteValues;
  3623. },
  3624. choiceText: function($choice, preserveHTML) {
  3625. preserveHTML = (preserveHTML !== undefined)
  3626. ? preserveHTML
  3627. : settings.preserveHTML
  3628. ;
  3629. if($choice) {
  3630. if($choice.find(selector.menu).length > 0) {
  3631. module.verbose('Retrieving text of element with sub-menu');
  3632. $choice = $choice.clone();
  3633. $choice.find(selector.menu).remove();
  3634. $choice.find(selector.menuIcon).remove();
  3635. }
  3636. return ($choice.data(metadata.text) !== undefined)
  3637. ? $choice.data(metadata.text)
  3638. : (preserveHTML)
  3639. ? $choice.html().trim()
  3640. : $choice.text().trim()
  3641. ;
  3642. }
  3643. },
  3644. choiceValue: function($choice, choiceText) {
  3645. choiceText = choiceText || module.get.choiceText($choice);
  3646. if(!$choice) {
  3647. return false;
  3648. }
  3649. return ($choice.data(metadata.value) !== undefined)
  3650. ? String( $choice.data(metadata.value) )
  3651. : (typeof choiceText === 'string')
  3652. ? String(
  3653. settings.ignoreSearchCase
  3654. ? choiceText.toLowerCase()
  3655. : choiceText
  3656. ).trim()
  3657. : String(choiceText)
  3658. ;
  3659. },
  3660. inputEvent: function() {
  3661. var
  3662. input = $search[0]
  3663. ;
  3664. if(input) {
  3665. return (input.oninput !== undefined)
  3666. ? 'input'
  3667. : (input.onpropertychange !== undefined)
  3668. ? 'propertychange'
  3669. : 'keyup'
  3670. ;
  3671. }
  3672. return false;
  3673. },
  3674. selectValues: function() {
  3675. var
  3676. select = {},
  3677. oldGroup = [],
  3678. values = []
  3679. ;
  3680. $module
  3681. .find('option')
  3682. .each(function() {
  3683. var
  3684. $option = $(this),
  3685. name = $option.html(),
  3686. disabled = $option.attr('disabled'),
  3687. value = ( $option.attr('value') !== undefined )
  3688. ? $option.attr('value')
  3689. : name,
  3690. text = ( $option.data(metadata.text) !== undefined )
  3691. ? $option.data(metadata.text)
  3692. : name,
  3693. group = $option.parent('optgroup')
  3694. ;
  3695. if(settings.placeholder === 'auto' && value === '') {
  3696. select.placeholder = name;
  3697. }
  3698. else {
  3699. if(group.length !== oldGroup.length || group[0] !== oldGroup[0]) {
  3700. values.push({
  3701. type: 'header',
  3702. divider: settings.headerDivider,
  3703. name: group.attr('label') || ''
  3704. });
  3705. oldGroup = group;
  3706. }
  3707. values.push({
  3708. name : name,
  3709. value : value,
  3710. text : text,
  3711. disabled : disabled
  3712. });
  3713. }
  3714. })
  3715. ;
  3716. if(settings.placeholder && settings.placeholder !== 'auto') {
  3717. module.debug('Setting placeholder value to', settings.placeholder);
  3718. select.placeholder = settings.placeholder;
  3719. }
  3720. if(settings.sortSelect) {
  3721. if(settings.sortSelect === true) {
  3722. values.sort(function(a, b) {
  3723. return a.name.localeCompare(b.name);
  3724. });
  3725. } else if(settings.sortSelect === 'natural') {
  3726. values.sort(function(a, b) {
  3727. return (a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
  3728. });
  3729. } else if($.isFunction(settings.sortSelect)) {
  3730. values.sort(settings.sortSelect);
  3731. }
  3732. select[fields.values] = values;
  3733. module.debug('Retrieved and sorted values from select', select);
  3734. }
  3735. else {
  3736. select[fields.values] = values;
  3737. module.debug('Retrieved values from select', select);
  3738. }
  3739. return select;
  3740. },
  3741. activeItem: function() {
  3742. return $item.filter('.' + className.active);
  3743. },
  3744. selectedItem: function() {
  3745. var
  3746. $selectedItem = $item.not(selector.unselectable).filter('.' + className.selected)
  3747. ;
  3748. return ($selectedItem.length > 0)
  3749. ? $selectedItem
  3750. : $item.eq(0)
  3751. ;
  3752. },
  3753. itemWithAdditions: function(value) {
  3754. var
  3755. $items = module.get.item(value),
  3756. $userItems = module.create.userChoice(value),
  3757. hasUserItems = ($userItems && $userItems.length > 0)
  3758. ;
  3759. if(hasUserItems) {
  3760. $items = ($items.length > 0)
  3761. ? $items.add($userItems)
  3762. : $userItems
  3763. ;
  3764. }
  3765. return $items;
  3766. },
  3767. item: function(value, strict) {
  3768. var
  3769. $selectedItem = false,
  3770. shouldSearch,
  3771. isMultiple
  3772. ;
  3773. value = (value !== undefined)
  3774. ? value
  3775. : ( module.get.values() !== undefined)
  3776. ? module.get.values()
  3777. : module.get.text()
  3778. ;
  3779. isMultiple = (module.is.multiple() && Array.isArray(value));
  3780. shouldSearch = (isMultiple)
  3781. ? (value.length > 0)
  3782. : (value !== undefined && value !== null)
  3783. ;
  3784. strict = (value === '' || value === false || value === true)
  3785. ? true
  3786. : strict || false
  3787. ;
  3788. if(shouldSearch) {
  3789. $item
  3790. .each(function() {
  3791. var
  3792. $choice = $(this),
  3793. optionText = module.get.choiceText($choice),
  3794. optionValue = module.get.choiceValue($choice, optionText)
  3795. ;
  3796. // safe early exit
  3797. if(optionValue === null || optionValue === undefined) {
  3798. return;
  3799. }
  3800. if(isMultiple) {
  3801. if($.inArray(module.escape.htmlEntities(String(optionValue)), value.map(function(v){return String(v);})) !== -1) {
  3802. $selectedItem = ($selectedItem)
  3803. ? $selectedItem.add($choice)
  3804. : $choice
  3805. ;
  3806. }
  3807. }
  3808. else if(strict) {
  3809. module.verbose('Ambiguous dropdown value using strict type check', $choice, value);
  3810. if( optionValue === value) {
  3811. $selectedItem = $choice;
  3812. return true;
  3813. }
  3814. }
  3815. else {
  3816. if(settings.ignoreCase) {
  3817. optionValue = optionValue.toLowerCase();
  3818. value = value.toLowerCase();
  3819. }
  3820. if(module.escape.htmlEntities(String(optionValue)) === module.escape.htmlEntities(String(value))) {
  3821. module.verbose('Found select item by value', optionValue, value);
  3822. $selectedItem = $choice;
  3823. return true;
  3824. }
  3825. }
  3826. })
  3827. ;
  3828. }
  3829. return $selectedItem;
  3830. }
  3831. },
  3832. check: {
  3833. maxSelections: function(selectionCount) {
  3834. if(settings.maxSelections) {
  3835. selectionCount = (selectionCount !== undefined)
  3836. ? selectionCount
  3837. : module.get.selectionCount()
  3838. ;
  3839. if(selectionCount >= settings.maxSelections) {
  3840. module.debug('Maximum selection count reached');
  3841. if(settings.useLabels) {
  3842. $item.addClass(className.filtered);
  3843. module.add.message(message.maxSelections);
  3844. }
  3845. return true;
  3846. }
  3847. else {
  3848. module.verbose('No longer at maximum selection count');
  3849. module.remove.message();
  3850. module.remove.filteredItem();
  3851. if(module.is.searchSelection()) {
  3852. module.filterItems();
  3853. }
  3854. return false;
  3855. }
  3856. }
  3857. return true;
  3858. },
  3859. disabled: function(){
  3860. $search.attr('tabindex',module.is.disabled() ? -1 : 0);
  3861. }
  3862. },
  3863. restore: {
  3864. defaults: function(preventChangeTrigger) {
  3865. module.clear(preventChangeTrigger);
  3866. module.restore.defaultText();
  3867. module.restore.defaultValue();
  3868. },
  3869. defaultText: function() {
  3870. var
  3871. defaultText = module.get.defaultText(),
  3872. placeholderText = module.get.placeholderText
  3873. ;
  3874. if(defaultText === placeholderText) {
  3875. module.debug('Restoring default placeholder text', defaultText);
  3876. module.set.placeholderText(defaultText);
  3877. }
  3878. else {
  3879. module.debug('Restoring default text', defaultText);
  3880. module.set.text(defaultText);
  3881. }
  3882. },
  3883. placeholderText: function() {
  3884. module.set.placeholderText();
  3885. },
  3886. defaultValue: function() {
  3887. var
  3888. defaultValue = module.get.defaultValue()
  3889. ;
  3890. if(defaultValue !== undefined) {
  3891. module.debug('Restoring default value', defaultValue);
  3892. if(defaultValue !== '') {
  3893. module.set.value(defaultValue);
  3894. module.set.selected();
  3895. }
  3896. else {
  3897. module.remove.activeItem();
  3898. module.remove.selectedItem();
  3899. }
  3900. }
  3901. },
  3902. labels: function() {
  3903. if(settings.allowAdditions) {
  3904. if(!settings.useLabels) {
  3905. module.error(error.labels);
  3906. settings.useLabels = true;
  3907. }
  3908. module.debug('Restoring selected values');
  3909. module.create.userLabels();
  3910. }
  3911. module.check.maxSelections();
  3912. },
  3913. selected: function() {
  3914. module.restore.values();
  3915. if(module.is.multiple()) {
  3916. module.debug('Restoring previously selected values and labels');
  3917. module.restore.labels();
  3918. }
  3919. else {
  3920. module.debug('Restoring previously selected values');
  3921. }
  3922. },
  3923. values: function() {
  3924. // prevents callbacks from occurring on initial load
  3925. module.set.initialLoad();
  3926. if(settings.apiSettings && settings.saveRemoteData && module.get.remoteValues()) {
  3927. module.restore.remoteValues();
  3928. }
  3929. else {
  3930. module.set.selected();
  3931. }
  3932. var value = module.get.value();
  3933. if(value && value !== '' && !(Array.isArray(value) && value.length === 0)) {
  3934. $input.removeClass(className.noselection);
  3935. } else {
  3936. $input.addClass(className.noselection);
  3937. }
  3938. module.remove.initialLoad();
  3939. },
  3940. remoteValues: function() {
  3941. var
  3942. values = module.get.remoteValues()
  3943. ;
  3944. module.debug('Recreating selected from session data', values);
  3945. if(values) {
  3946. if( module.is.single() ) {
  3947. $.each(values, function(value, name) {
  3948. module.set.text(name);
  3949. });
  3950. }
  3951. else {
  3952. $.each(values, function(value, name) {
  3953. module.add.label(value, name);
  3954. });
  3955. }
  3956. }
  3957. }
  3958. },
  3959. read: {
  3960. remoteData: function(value) {
  3961. var
  3962. name
  3963. ;
  3964. if(window.Storage === undefined) {
  3965. module.error(error.noStorage);
  3966. return;
  3967. }
  3968. name = sessionStorage.getItem(value);
  3969. return (name !== undefined)
  3970. ? name
  3971. : false
  3972. ;
  3973. }
  3974. },
  3975. save: {
  3976. defaults: function() {
  3977. module.save.defaultText();
  3978. module.save.placeholderText();
  3979. module.save.defaultValue();
  3980. },
  3981. defaultValue: function() {
  3982. var
  3983. value = module.get.value()
  3984. ;
  3985. module.verbose('Saving default value as', value);
  3986. $module.data(metadata.defaultValue, value);
  3987. },
  3988. defaultText: function() {
  3989. var
  3990. text = module.get.text()
  3991. ;
  3992. module.verbose('Saving default text as', text);
  3993. $module.data(metadata.defaultText, text);
  3994. },
  3995. placeholderText: function() {
  3996. var
  3997. text
  3998. ;
  3999. if(settings.placeholder !== false && $text.hasClass(className.placeholder)) {
  4000. text = module.get.text();
  4001. module.verbose('Saving placeholder text as', text);
  4002. $module.data(metadata.placeholderText, text);
  4003. }
  4004. },
  4005. remoteData: function(name, value) {
  4006. if(window.Storage === undefined) {
  4007. module.error(error.noStorage);
  4008. return;
  4009. }
  4010. module.verbose('Saving remote data to session storage', value, name);
  4011. sessionStorage.setItem(value, name);
  4012. }
  4013. },
  4014. clear: function(preventChangeTrigger) {
  4015. if(module.is.multiple() && settings.useLabels) {
  4016. module.remove.labels();
  4017. }
  4018. else {
  4019. module.remove.activeItem();
  4020. module.remove.selectedItem();
  4021. module.remove.filteredItem();
  4022. }
  4023. module.set.placeholderText();
  4024. module.clearValue(preventChangeTrigger);
  4025. },
  4026. clearValue: function(preventChangeTrigger) {
  4027. module.set.value('', null, null, preventChangeTrigger);
  4028. },
  4029. scrollPage: function(direction, $selectedItem) {
  4030. var
  4031. $currentItem = $selectedItem || module.get.selectedItem(),
  4032. $menu = $currentItem.closest(selector.menu),
  4033. menuHeight = $menu.outerHeight(),
  4034. currentScroll = $menu.scrollTop(),
  4035. itemHeight = $item.eq(0).outerHeight(),
  4036. itemsPerPage = Math.floor(menuHeight / itemHeight),
  4037. maxScroll = $menu.prop('scrollHeight'),
  4038. newScroll = (direction == 'up')
  4039. ? currentScroll - (itemHeight * itemsPerPage)
  4040. : currentScroll + (itemHeight * itemsPerPage),
  4041. $selectableItem = $item.not(selector.unselectable),
  4042. isWithinRange,
  4043. $nextSelectedItem,
  4044. elementIndex
  4045. ;
  4046. elementIndex = (direction == 'up')
  4047. ? $selectableItem.index($currentItem) - itemsPerPage
  4048. : $selectableItem.index($currentItem) + itemsPerPage
  4049. ;
  4050. isWithinRange = (direction == 'up')
  4051. ? (elementIndex >= 0)
  4052. : (elementIndex < $selectableItem.length)
  4053. ;
  4054. $nextSelectedItem = (isWithinRange)
  4055. ? $selectableItem.eq(elementIndex)
  4056. : (direction == 'up')
  4057. ? $selectableItem.first()
  4058. : $selectableItem.last()
  4059. ;
  4060. if($nextSelectedItem.length > 0) {
  4061. module.debug('Scrolling page', direction, $nextSelectedItem);
  4062. $currentItem
  4063. .removeClass(className.selected)
  4064. ;
  4065. $nextSelectedItem
  4066. .addClass(className.selected)
  4067. ;
  4068. if(settings.selectOnKeydown && module.is.single()) {
  4069. module.set.selectedItem($nextSelectedItem);
  4070. }
  4071. $menu
  4072. .scrollTop(newScroll)
  4073. ;
  4074. }
  4075. },
  4076. set: {
  4077. filtered: function() {
  4078. var
  4079. isMultiple = module.is.multiple(),
  4080. isSearch = module.is.searchSelection(),
  4081. isSearchMultiple = (isMultiple && isSearch),
  4082. searchValue = (isSearch)
  4083. ? module.get.query()
  4084. : '',
  4085. hasSearchValue = (typeof searchValue === 'string' && searchValue.length > 0),
  4086. searchWidth = module.get.searchWidth(),
  4087. valueIsSet = searchValue !== ''
  4088. ;
  4089. if(isMultiple && hasSearchValue) {
  4090. module.verbose('Adjusting input width', searchWidth, settings.glyphWidth);
  4091. $search.css('width', searchWidth);
  4092. }
  4093. if(hasSearchValue || (isSearchMultiple && valueIsSet)) {
  4094. module.verbose('Hiding placeholder text');
  4095. $text.addClass(className.filtered);
  4096. }
  4097. else if(!isMultiple || (isSearchMultiple && !valueIsSet)) {
  4098. module.verbose('Showing placeholder text');
  4099. $text.removeClass(className.filtered);
  4100. }
  4101. },
  4102. empty: function() {
  4103. $module.addClass(className.empty);
  4104. },
  4105. loading: function() {
  4106. $module.addClass(className.loading);
  4107. },
  4108. placeholderText: function(text) {
  4109. text = text || module.get.placeholderText();
  4110. module.debug('Setting placeholder text', text);
  4111. module.set.text(text);
  4112. $text.addClass(className.placeholder);
  4113. },
  4114. tabbable: function() {
  4115. if( module.is.searchSelection() ) {
  4116. module.debug('Added tabindex to searchable dropdown');
  4117. $search
  4118. .val('')
  4119. ;
  4120. module.check.disabled();
  4121. $menu
  4122. .attr('tabindex', -1)
  4123. ;
  4124. }
  4125. else {
  4126. module.debug('Added tabindex to dropdown');
  4127. if( $module.attr('tabindex') === undefined) {
  4128. $module
  4129. .attr('tabindex', 0)
  4130. ;
  4131. $menu
  4132. .attr('tabindex', -1)
  4133. ;
  4134. }
  4135. }
  4136. },
  4137. initialLoad: function() {
  4138. module.verbose('Setting initial load');
  4139. initialLoad = true;
  4140. },
  4141. activeItem: function($item) {
  4142. if( settings.allowAdditions && $item.filter(selector.addition).length > 0 ) {
  4143. $item.addClass(className.filtered);
  4144. }
  4145. else {
  4146. $item.addClass(className.active);
  4147. }
  4148. },
  4149. partialSearch: function(text) {
  4150. var
  4151. length = module.get.query().length
  4152. ;
  4153. $search.val( text.substr(0, length));
  4154. },
  4155. scrollPosition: function($item, forceScroll) {
  4156. var
  4157. edgeTolerance = 5,
  4158. $menu,
  4159. hasActive,
  4160. offset,
  4161. itemHeight,
  4162. itemOffset,
  4163. menuOffset,
  4164. menuScroll,
  4165. menuHeight,
  4166. abovePage,
  4167. belowPage
  4168. ;
  4169. $item = $item || module.get.selectedItem();
  4170. $menu = $item.closest(selector.menu);
  4171. hasActive = ($item && $item.length > 0);
  4172. forceScroll = (forceScroll !== undefined)
  4173. ? forceScroll
  4174. : false
  4175. ;
  4176. if(module.get.activeItem().length === 0){
  4177. forceScroll = false;
  4178. }
  4179. if($item && $menu.length > 0 && hasActive) {
  4180. itemOffset = $item.position().top;
  4181. $menu.addClass(className.loading);
  4182. menuScroll = $menu.scrollTop();
  4183. menuOffset = $menu.offset().top;
  4184. itemOffset = $item.offset().top;
  4185. offset = menuScroll - menuOffset + itemOffset;
  4186. if(!forceScroll) {
  4187. menuHeight = $menu.height();
  4188. belowPage = menuScroll + menuHeight < (offset + edgeTolerance);
  4189. abovePage = ((offset - edgeTolerance) < menuScroll);
  4190. }
  4191. module.debug('Scrolling to active item', offset);
  4192. if(forceScroll || abovePage || belowPage) {
  4193. $menu.scrollTop(offset);
  4194. }
  4195. $menu.removeClass(className.loading);
  4196. }
  4197. },
  4198. text: function(text) {
  4199. if(settings.action === 'combo') {
  4200. module.debug('Changing combo button text', text, $combo);
  4201. if(settings.preserveHTML) {
  4202. $combo.html(text);
  4203. }
  4204. else {
  4205. $combo.text(text);
  4206. }
  4207. }
  4208. else if(settings.action === 'activate') {
  4209. if(text !== module.get.placeholderText()) {
  4210. $text.removeClass(className.placeholder);
  4211. }
  4212. module.debug('Changing text', text, $text);
  4213. $text
  4214. .removeClass(className.filtered)
  4215. ;
  4216. if(settings.preserveHTML) {
  4217. $text.html(text);
  4218. }
  4219. else {
  4220. $text.text(text);
  4221. }
  4222. }
  4223. },
  4224. selectedItem: function($item) {
  4225. var
  4226. value = module.get.choiceValue($item),
  4227. searchText = module.get.choiceText($item, false),
  4228. text = module.get.choiceText($item, true)
  4229. ;
  4230. module.debug('Setting user selection to item', $item);
  4231. module.remove.activeItem();
  4232. module.set.partialSearch(searchText);
  4233. module.set.activeItem($item);
  4234. module.set.selected(value, $item);
  4235. module.set.text(text);
  4236. },
  4237. selectedLetter: function(letter) {
  4238. var
  4239. $selectedItem = $item.filter('.' + className.selected),
  4240. alreadySelectedLetter = $selectedItem.length > 0 && module.has.firstLetter($selectedItem, letter),
  4241. $nextValue = false,
  4242. $nextItem
  4243. ;
  4244. // check next of same letter
  4245. if(alreadySelectedLetter) {
  4246. $nextItem = $selectedItem.nextAll($item).eq(0);
  4247. if( module.has.firstLetter($nextItem, letter) ) {
  4248. $nextValue = $nextItem;
  4249. }
  4250. }
  4251. // check all values
  4252. if(!$nextValue) {
  4253. $item
  4254. .each(function(){
  4255. if(module.has.firstLetter($(this), letter)) {
  4256. $nextValue = $(this);
  4257. return false;
  4258. }
  4259. })
  4260. ;
  4261. }
  4262. // set next value
  4263. if($nextValue) {
  4264. module.verbose('Scrolling to next value with letter', letter);
  4265. module.set.scrollPosition($nextValue);
  4266. $selectedItem.removeClass(className.selected);
  4267. $nextValue.addClass(className.selected);
  4268. if(settings.selectOnKeydown && module.is.single()) {
  4269. module.set.selectedItem($nextValue);
  4270. }
  4271. }
  4272. },
  4273. direction: function($menu) {
  4274. if(settings.direction == 'auto') {
  4275. // reset position, remove upward if it's base menu
  4276. if (!$menu) {
  4277. module.remove.upward();
  4278. } else if (module.is.upward($menu)) {
  4279. //we need make sure when make assertion openDownward for $menu, $menu does not have upward class
  4280. module.remove.upward($menu);
  4281. }
  4282. if(module.can.openDownward($menu)) {
  4283. module.remove.upward($menu);
  4284. }
  4285. else {
  4286. module.set.upward($menu);
  4287. }
  4288. if(!module.is.leftward($menu) && !module.can.openRightward($menu)) {
  4289. module.set.leftward($menu);
  4290. }
  4291. }
  4292. else if(settings.direction == 'upward') {
  4293. module.set.upward($menu);
  4294. }
  4295. },
  4296. upward: function($currentMenu) {
  4297. var $element = $currentMenu || $module;
  4298. $element.addClass(className.upward);
  4299. },
  4300. leftward: function($currentMenu) {
  4301. var $element = $currentMenu || $menu;
  4302. $element.addClass(className.leftward);
  4303. },
  4304. value: function(value, text, $selected, preventChangeTrigger) {
  4305. if(value !== undefined && value !== '' && !(Array.isArray(value) && value.length === 0)) {
  4306. $input.removeClass(className.noselection);
  4307. } else {
  4308. $input.addClass(className.noselection);
  4309. }
  4310. var
  4311. escapedValue = module.escape.value(value),
  4312. hasInput = ($input.length > 0),
  4313. currentValue = module.get.values(),
  4314. stringValue = (value !== undefined)
  4315. ? String(value)
  4316. : value,
  4317. newValue
  4318. ;
  4319. if(hasInput) {
  4320. if(!settings.allowReselection && stringValue == currentValue) {
  4321. module.verbose('Skipping value update already same value', value, currentValue);
  4322. if(!module.is.initialLoad()) {
  4323. return;
  4324. }
  4325. }
  4326. if( module.is.single() && module.has.selectInput() && module.can.extendSelect() ) {
  4327. module.debug('Adding user option', value);
  4328. module.add.optionValue(value);
  4329. }
  4330. module.debug('Updating input value', escapedValue, currentValue);
  4331. internalChange = true;
  4332. $input
  4333. .val(escapedValue)
  4334. ;
  4335. if(settings.fireOnInit === false && module.is.initialLoad()) {
  4336. module.debug('Input native change event ignored on initial load');
  4337. }
  4338. else if(preventChangeTrigger !== true) {
  4339. module.trigger.change();
  4340. }
  4341. internalChange = false;
  4342. }
  4343. else {
  4344. module.verbose('Storing value in metadata', escapedValue, $input);
  4345. if(escapedValue !== currentValue) {
  4346. $module.data(metadata.value, stringValue);
  4347. }
  4348. }
  4349. if(settings.fireOnInit === false && module.is.initialLoad()) {
  4350. module.verbose('No callback on initial load', settings.onChange);
  4351. }
  4352. else if(preventChangeTrigger !== true) {
  4353. settings.onChange.call(element, value, text, $selected);
  4354. }
  4355. },
  4356. active: function() {
  4357. $module
  4358. .addClass(className.active)
  4359. ;
  4360. },
  4361. multiple: function() {
  4362. $module.addClass(className.multiple);
  4363. },
  4364. visible: function() {
  4365. $module.addClass(className.visible);
  4366. },
  4367. exactly: function(value, $selectedItem) {
  4368. module.debug('Setting selected to exact values');
  4369. module.clear();
  4370. module.set.selected(value, $selectedItem);
  4371. },
  4372. selected: function(value, $selectedItem) {
  4373. var
  4374. isMultiple = module.is.multiple()
  4375. ;
  4376. $selectedItem = (settings.allowAdditions)
  4377. ? $selectedItem || module.get.itemWithAdditions(value)
  4378. : $selectedItem || module.get.item(value)
  4379. ;
  4380. if(!$selectedItem) {
  4381. return;
  4382. }
  4383. module.debug('Setting selected menu item to', $selectedItem);
  4384. if(module.is.multiple()) {
  4385. module.remove.searchWidth();
  4386. }
  4387. if(module.is.single()) {
  4388. module.remove.activeItem();
  4389. module.remove.selectedItem();
  4390. }
  4391. else if(settings.useLabels) {
  4392. module.remove.selectedItem();
  4393. }
  4394. // select each item
  4395. $selectedItem
  4396. .each(function() {
  4397. var
  4398. $selected = $(this),
  4399. selectedText = module.get.choiceText($selected),
  4400. selectedValue = module.get.choiceValue($selected, selectedText),
  4401. isFiltered = $selected.hasClass(className.filtered),
  4402. isActive = $selected.hasClass(className.active),
  4403. isUserValue = $selected.hasClass(className.addition),
  4404. shouldAnimate = (isMultiple && $selectedItem.length == 1)
  4405. ;
  4406. if(isMultiple) {
  4407. if(!isActive || isUserValue) {
  4408. if(settings.apiSettings && settings.saveRemoteData) {
  4409. module.save.remoteData(selectedText, selectedValue);
  4410. }
  4411. if(settings.useLabels) {
  4412. module.add.label(selectedValue, selectedText, shouldAnimate);
  4413. module.add.value(selectedValue, selectedText, $selected);
  4414. module.set.activeItem($selected);
  4415. module.filterActive();
  4416. module.select.nextAvailable($selectedItem);
  4417. }
  4418. else {
  4419. module.add.value(selectedValue, selectedText, $selected);
  4420. module.set.text(module.add.variables(message.count));
  4421. module.set.activeItem($selected);
  4422. }
  4423. }
  4424. else if(!isFiltered && (settings.useLabels || selectActionActive)) {
  4425. module.debug('Selected active value, removing label');
  4426. module.remove.selected(selectedValue);
  4427. }
  4428. }
  4429. else {
  4430. if(settings.apiSettings && settings.saveRemoteData) {
  4431. module.save.remoteData(selectedText, selectedValue);
  4432. }
  4433. module.set.text(selectedText);
  4434. module.set.value(selectedValue, selectedText, $selected);
  4435. $selected
  4436. .addClass(className.active)
  4437. .addClass(className.selected)
  4438. ;
  4439. }
  4440. })
  4441. ;
  4442. module.remove.searchTerm();
  4443. }
  4444. },
  4445. add: {
  4446. label: function(value, text, shouldAnimate) {
  4447. var
  4448. $next = module.is.searchSelection()
  4449. ? $search
  4450. : $text,
  4451. escapedValue = module.escape.value(value),
  4452. $label
  4453. ;
  4454. if(settings.ignoreCase) {
  4455. escapedValue = escapedValue.toLowerCase();
  4456. }
  4457. $label = $('<a />')
  4458. .addClass(className.label)
  4459. .attr('data-' + metadata.value, escapedValue)
  4460. .html(templates.label(escapedValue, text, settings.preserveHTML, settings.className))
  4461. ;
  4462. $label = settings.onLabelCreate.call($label, escapedValue, text);
  4463. if(module.has.label(value)) {
  4464. module.debug('User selection already exists, skipping', escapedValue);
  4465. return;
  4466. }
  4467. if(settings.label.variation) {
  4468. $label.addClass(settings.label.variation);
  4469. }
  4470. if(shouldAnimate === true) {
  4471. module.debug('Animating in label', $label);
  4472. $label
  4473. .addClass(className.hidden)
  4474. .insertBefore($next)
  4475. .transition({
  4476. animation : settings.label.transition,
  4477. debug : settings.debug,
  4478. verbose : settings.verbose,
  4479. duration : settings.label.duration
  4480. })
  4481. ;
  4482. }
  4483. else {
  4484. module.debug('Adding selection label', $label);
  4485. $label
  4486. .insertBefore($next)
  4487. ;
  4488. }
  4489. },
  4490. message: function(message) {
  4491. var
  4492. $message = $menu.children(selector.message),
  4493. html = settings.templates.message(module.add.variables(message))
  4494. ;
  4495. if($message.length > 0) {
  4496. $message
  4497. .html(html)
  4498. ;
  4499. }
  4500. else {
  4501. $message = $('<div/>')
  4502. .html(html)
  4503. .addClass(className.message)
  4504. .appendTo($menu)
  4505. ;
  4506. }
  4507. },
  4508. optionValue: function(value) {
  4509. var
  4510. escapedValue = module.escape.value(value),
  4511. $option = $input.find('option[value="' + module.escape.string(escapedValue) + '"]'),
  4512. hasOption = ($option.length > 0)
  4513. ;
  4514. if(hasOption) {
  4515. return;
  4516. }
  4517. // temporarily disconnect observer
  4518. module.disconnect.selectObserver();
  4519. if( module.is.single() ) {
  4520. module.verbose('Removing previous user addition');
  4521. $input.find('option.' + className.addition).remove();
  4522. }
  4523. $('<option/>')
  4524. .prop('value', escapedValue)
  4525. .addClass(className.addition)
  4526. .html(value)
  4527. .appendTo($input)
  4528. ;
  4529. module.verbose('Adding user addition as an <option>', value);
  4530. module.observe.select();
  4531. },
  4532. userSuggestion: function(value) {
  4533. var
  4534. $addition = $menu.children(selector.addition),
  4535. $existingItem = module.get.item(value),
  4536. alreadyHasValue = $existingItem && $existingItem.not(selector.addition).length,
  4537. hasUserSuggestion = $addition.length > 0,
  4538. html
  4539. ;
  4540. if(settings.useLabels && module.has.maxSelections()) {
  4541. return;
  4542. }
  4543. if(value === '' || alreadyHasValue) {
  4544. $addition.remove();
  4545. return;
  4546. }
  4547. if(hasUserSuggestion) {
  4548. $addition
  4549. .data(metadata.value, value)
  4550. .data(metadata.text, value)
  4551. .attr('data-' + metadata.value, value)
  4552. .attr('data-' + metadata.text, value)
  4553. .removeClass(className.filtered)
  4554. ;
  4555. if(!settings.hideAdditions) {
  4556. html = settings.templates.addition( module.add.variables(message.addResult, value) );
  4557. $addition
  4558. .html(html)
  4559. ;
  4560. }
  4561. module.verbose('Replacing user suggestion with new value', $addition);
  4562. }
  4563. else {
  4564. $addition = module.create.userChoice(value);
  4565. $addition
  4566. .prependTo($menu)
  4567. ;
  4568. module.verbose('Adding item choice to menu corresponding with user choice addition', $addition);
  4569. }
  4570. if(!settings.hideAdditions || module.is.allFiltered()) {
  4571. $addition
  4572. .addClass(className.selected)
  4573. .siblings()
  4574. .removeClass(className.selected)
  4575. ;
  4576. }
  4577. module.refreshItems();
  4578. },
  4579. variables: function(message, term) {
  4580. var
  4581. hasCount = (message.search('{count}') !== -1),
  4582. hasMaxCount = (message.search('{maxCount}') !== -1),
  4583. hasTerm = (message.search('{term}') !== -1),
  4584. count,
  4585. query
  4586. ;
  4587. module.verbose('Adding templated variables to message', message);
  4588. if(hasCount) {
  4589. count = module.get.selectionCount();
  4590. message = message.replace('{count}', count);
  4591. }
  4592. if(hasMaxCount) {
  4593. count = module.get.selectionCount();
  4594. message = message.replace('{maxCount}', settings.maxSelections);
  4595. }
  4596. if(hasTerm) {
  4597. query = term || module.get.query();
  4598. message = message.replace('{term}', query);
  4599. }
  4600. return message;
  4601. },
  4602. value: function(addedValue, addedText, $selectedItem) {
  4603. var
  4604. currentValue = module.get.values(),
  4605. newValue
  4606. ;
  4607. if(module.has.value(addedValue)) {
  4608. module.debug('Value already selected');
  4609. return;
  4610. }
  4611. if(addedValue === '') {
  4612. module.debug('Cannot select blank values from multiselect');
  4613. return;
  4614. }
  4615. // extend current array
  4616. if(Array.isArray(currentValue)) {
  4617. newValue = currentValue.concat([addedValue]);
  4618. newValue = module.get.uniqueArray(newValue);
  4619. }
  4620. else {
  4621. newValue = [addedValue];
  4622. }
  4623. // add values
  4624. if( module.has.selectInput() ) {
  4625. if(module.can.extendSelect()) {
  4626. module.debug('Adding value to select', addedValue, newValue, $input);
  4627. module.add.optionValue(addedValue);
  4628. }
  4629. }
  4630. else {
  4631. newValue = newValue.join(settings.delimiter);
  4632. module.debug('Setting hidden input to delimited value', newValue, $input);
  4633. }
  4634. if(settings.fireOnInit === false && module.is.initialLoad()) {
  4635. module.verbose('Skipping onadd callback on initial load', settings.onAdd);
  4636. }
  4637. else {
  4638. settings.onAdd.call(element, addedValue, addedText, $selectedItem);
  4639. }
  4640. module.set.value(newValue, addedText, $selectedItem);
  4641. module.check.maxSelections();
  4642. },
  4643. },
  4644. remove: {
  4645. active: function() {
  4646. $module.removeClass(className.active);
  4647. },
  4648. activeLabel: function() {
  4649. $module.find(selector.label).removeClass(className.active);
  4650. },
  4651. empty: function() {
  4652. $module.removeClass(className.empty);
  4653. },
  4654. loading: function() {
  4655. $module.removeClass(className.loading);
  4656. },
  4657. initialLoad: function() {
  4658. initialLoad = false;
  4659. },
  4660. upward: function($currentMenu) {
  4661. var $element = $currentMenu || $module;
  4662. $element.removeClass(className.upward);
  4663. },
  4664. leftward: function($currentMenu) {
  4665. var $element = $currentMenu || $menu;
  4666. $element.removeClass(className.leftward);
  4667. },
  4668. visible: function() {
  4669. $module.removeClass(className.visible);
  4670. },
  4671. activeItem: function() {
  4672. $item.removeClass(className.active);
  4673. },
  4674. filteredItem: function() {
  4675. if(settings.useLabels && module.has.maxSelections() ) {
  4676. return;
  4677. }
  4678. if(settings.useLabels && module.is.multiple()) {
  4679. $item.not('.' + className.active).removeClass(className.filtered);
  4680. }
  4681. else {
  4682. $item.removeClass(className.filtered);
  4683. }
  4684. if(settings.hideDividers) {
  4685. $divider.removeClass(className.hidden);
  4686. }
  4687. module.remove.empty();
  4688. },
  4689. optionValue: function(value) {
  4690. var
  4691. escapedValue = module.escape.value(value),
  4692. $option = $input.find('option[value="' + module.escape.string(escapedValue) + '"]'),
  4693. hasOption = ($option.length > 0)
  4694. ;
  4695. if(!hasOption || !$option.hasClass(className.addition)) {
  4696. return;
  4697. }
  4698. // temporarily disconnect observer
  4699. if(selectObserver) {
  4700. selectObserver.disconnect();
  4701. module.verbose('Temporarily disconnecting mutation observer');
  4702. }
  4703. $option.remove();
  4704. module.verbose('Removing user addition as an <option>', escapedValue);
  4705. if(selectObserver) {
  4706. selectObserver.observe($input[0], {
  4707. childList : true,
  4708. subtree : true
  4709. });
  4710. }
  4711. },
  4712. message: function() {
  4713. $menu.children(selector.message).remove();
  4714. },
  4715. searchWidth: function() {
  4716. $search.css('width', '');
  4717. },
  4718. searchTerm: function() {
  4719. module.verbose('Cleared search term');
  4720. $search.val('');
  4721. module.set.filtered();
  4722. },
  4723. userAddition: function() {
  4724. $item.filter(selector.addition).remove();
  4725. },
  4726. selected: function(value, $selectedItem) {
  4727. $selectedItem = (settings.allowAdditions)
  4728. ? $selectedItem || module.get.itemWithAdditions(value)
  4729. : $selectedItem || module.get.item(value)
  4730. ;
  4731. if(!$selectedItem) {
  4732. return false;
  4733. }
  4734. $selectedItem
  4735. .each(function() {
  4736. var
  4737. $selected = $(this),
  4738. selectedText = module.get.choiceText($selected),
  4739. selectedValue = module.get.choiceValue($selected, selectedText)
  4740. ;
  4741. if(module.is.multiple()) {
  4742. if(settings.useLabels) {
  4743. module.remove.value(selectedValue, selectedText, $selected);
  4744. module.remove.label(selectedValue);
  4745. }
  4746. else {
  4747. module.remove.value(selectedValue, selectedText, $selected);
  4748. if(module.get.selectionCount() === 0) {
  4749. module.set.placeholderText();
  4750. }
  4751. else {
  4752. module.set.text(module.add.variables(message.count));
  4753. }
  4754. }
  4755. }
  4756. else {
  4757. module.remove.value(selectedValue, selectedText, $selected);
  4758. }
  4759. $selected
  4760. .removeClass(className.filtered)
  4761. .removeClass(className.active)
  4762. ;
  4763. if(settings.useLabels) {
  4764. $selected.removeClass(className.selected);
  4765. }
  4766. })
  4767. ;
  4768. },
  4769. selectedItem: function() {
  4770. $item.removeClass(className.selected);
  4771. },
  4772. value: function(removedValue, removedText, $removedItem) {
  4773. var
  4774. values = module.get.values(),
  4775. newValue
  4776. ;
  4777. removedValue = module.escape.htmlEntities(removedValue);
  4778. if( module.has.selectInput() ) {
  4779. module.verbose('Input is <select> removing selected option', removedValue);
  4780. newValue = module.remove.arrayValue(removedValue, values);
  4781. module.remove.optionValue(removedValue);
  4782. }
  4783. else {
  4784. module.verbose('Removing from delimited values', removedValue);
  4785. newValue = module.remove.arrayValue(removedValue, values);
  4786. newValue = newValue.join(settings.delimiter);
  4787. }
  4788. if(settings.fireOnInit === false && module.is.initialLoad()) {
  4789. module.verbose('No callback on initial load', settings.onRemove);
  4790. }
  4791. else {
  4792. settings.onRemove.call(element, removedValue, removedText, $removedItem);
  4793. }
  4794. module.set.value(newValue, removedText, $removedItem);
  4795. module.check.maxSelections();
  4796. },
  4797. arrayValue: function(removedValue, values) {
  4798. if( !Array.isArray(values) ) {
  4799. values = [values];
  4800. }
  4801. values = $.grep(values, function(value){
  4802. return (removedValue != value);
  4803. });
  4804. module.verbose('Removed value from delimited string', removedValue, values);
  4805. return values;
  4806. },
  4807. label: function(value, shouldAnimate) {
  4808. var
  4809. $labels = $module.find(selector.label),
  4810. $removedLabel = $labels.filter('[data-' + metadata.value + '="' + module.escape.string(settings.ignoreCase ? value.toLowerCase() : value) +'"]')
  4811. ;
  4812. module.verbose('Removing label', $removedLabel);
  4813. $removedLabel.remove();
  4814. },
  4815. activeLabels: function($activeLabels) {
  4816. $activeLabels = $activeLabels || $module.find(selector.label).filter('.' + className.active);
  4817. module.verbose('Removing active label selections', $activeLabels);
  4818. module.remove.labels($activeLabels);
  4819. },
  4820. labels: function($labels) {
  4821. $labels = $labels || $module.find(selector.label);
  4822. module.verbose('Removing labels', $labels);
  4823. $labels
  4824. .each(function(){
  4825. var
  4826. $label = $(this),
  4827. value = $label.data(metadata.value),
  4828. stringValue = (value !== undefined)
  4829. ? String(value)
  4830. : value,
  4831. isUserValue = module.is.userValue(stringValue)
  4832. ;
  4833. if(settings.onLabelRemove.call($label, value) === false) {
  4834. module.debug('Label remove callback cancelled removal');
  4835. return;
  4836. }
  4837. module.remove.message();
  4838. if(isUserValue) {
  4839. module.remove.value(stringValue);
  4840. module.remove.label(stringValue);
  4841. }
  4842. else {
  4843. // selected will also remove label
  4844. module.remove.selected(stringValue);
  4845. }
  4846. })
  4847. ;
  4848. },
  4849. tabbable: function() {
  4850. if( module.is.searchSelection() ) {
  4851. module.debug('Searchable dropdown initialized');
  4852. $search
  4853. .removeAttr('tabindex')
  4854. ;
  4855. $menu
  4856. .removeAttr('tabindex')
  4857. ;
  4858. }
  4859. else {
  4860. module.debug('Simple selection dropdown initialized');
  4861. $module
  4862. .removeAttr('tabindex')
  4863. ;
  4864. $menu
  4865. .removeAttr('tabindex')
  4866. ;
  4867. }
  4868. },
  4869. diacritics: function(text) {
  4870. return settings.ignoreDiacritics ? text.normalize('NFD').replace(/[\u0300-\u036f]/g, '') : text;
  4871. }
  4872. },
  4873. has: {
  4874. menuSearch: function() {
  4875. return (module.has.search() && $search.closest($menu).length > 0);
  4876. },
  4877. clearItem: function() {
  4878. return ($clear.length > 0);
  4879. },
  4880. search: function() {
  4881. return ($search.length > 0);
  4882. },
  4883. sizer: function() {
  4884. return ($sizer.length > 0);
  4885. },
  4886. selectInput: function() {
  4887. return ( $input.is('select') );
  4888. },
  4889. minCharacters: function(searchTerm) {
  4890. if(settings.minCharacters && !iconClicked) {
  4891. searchTerm = (searchTerm !== undefined)
  4892. ? String(searchTerm)
  4893. : String(module.get.query())
  4894. ;
  4895. return (searchTerm.length >= settings.minCharacters);
  4896. }
  4897. iconClicked=false;
  4898. return true;
  4899. },
  4900. firstLetter: function($item, letter) {
  4901. var
  4902. text,
  4903. firstLetter
  4904. ;
  4905. if(!$item || $item.length === 0 || typeof letter !== 'string') {
  4906. return false;
  4907. }
  4908. text = module.get.choiceText($item, false);
  4909. letter = letter.toLowerCase();
  4910. firstLetter = String(text).charAt(0).toLowerCase();
  4911. return (letter == firstLetter);
  4912. },
  4913. input: function() {
  4914. return ($input.length > 0);
  4915. },
  4916. items: function() {
  4917. return ($item.length > 0);
  4918. },
  4919. menu: function() {
  4920. return ($menu.length > 0);
  4921. },
  4922. message: function() {
  4923. return ($menu.children(selector.message).length !== 0);
  4924. },
  4925. label: function(value) {
  4926. var
  4927. escapedValue = module.escape.value(value),
  4928. $labels = $module.find(selector.label)
  4929. ;
  4930. if(settings.ignoreCase) {
  4931. escapedValue = escapedValue.toLowerCase();
  4932. }
  4933. return ($labels.filter('[data-' + metadata.value + '="' + module.escape.string(escapedValue) +'"]').length > 0);
  4934. },
  4935. maxSelections: function() {
  4936. return (settings.maxSelections && module.get.selectionCount() >= settings.maxSelections);
  4937. },
  4938. allResultsFiltered: function() {
  4939. var
  4940. $normalResults = $item.not(selector.addition)
  4941. ;
  4942. return ($normalResults.filter(selector.unselectable).length === $normalResults.length);
  4943. },
  4944. userSuggestion: function() {
  4945. return ($menu.children(selector.addition).length > 0);
  4946. },
  4947. query: function() {
  4948. return (module.get.query() !== '');
  4949. },
  4950. value: function(value) {
  4951. return (settings.ignoreCase)
  4952. ? module.has.valueIgnoringCase(value)
  4953. : module.has.valueMatchingCase(value)
  4954. ;
  4955. },
  4956. valueMatchingCase: function(value) {
  4957. var
  4958. values = module.get.values(),
  4959. hasValue = Array.isArray(values)
  4960. ? values && ($.inArray(value, values) !== -1)
  4961. : (values == value)
  4962. ;
  4963. return (hasValue)
  4964. ? true
  4965. : false
  4966. ;
  4967. },
  4968. valueIgnoringCase: function(value) {
  4969. var
  4970. values = module.get.values(),
  4971. hasValue = false
  4972. ;
  4973. if(!Array.isArray(values)) {
  4974. values = [values];
  4975. }
  4976. $.each(values, function(index, existingValue) {
  4977. if(String(value).toLowerCase() == String(existingValue).toLowerCase()) {
  4978. hasValue = true;
  4979. return false;
  4980. }
  4981. });
  4982. return hasValue;
  4983. }
  4984. },
  4985. is: {
  4986. active: function() {
  4987. return $module.hasClass(className.active);
  4988. },
  4989. animatingInward: function() {
  4990. return $menu.transition('is inward');
  4991. },
  4992. animatingOutward: function() {
  4993. return $menu.transition('is outward');
  4994. },
  4995. bubbledLabelClick: function(event) {
  4996. return $(event.target).is('select, input') && $module.closest('label').length > 0;
  4997. },
  4998. bubbledIconClick: function(event) {
  4999. return $(event.target).closest($icon).length > 0;
  5000. },
  5001. alreadySetup: function() {
  5002. return ($module.is('select') && $module.parent(selector.dropdown).data(moduleNamespace) !== undefined && $module.prev().length === 0);
  5003. },
  5004. animating: function($subMenu) {
  5005. return ($subMenu)
  5006. ? $subMenu.transition && $subMenu.transition('is animating')
  5007. : $menu.transition && $menu.transition('is animating')
  5008. ;
  5009. },
  5010. leftward: function($subMenu) {
  5011. var $selectedMenu = $subMenu || $menu;
  5012. return $selectedMenu.hasClass(className.leftward);
  5013. },
  5014. clearable: function() {
  5015. return ($module.hasClass(className.clearable) || settings.clearable);
  5016. },
  5017. disabled: function() {
  5018. return $module.hasClass(className.disabled);
  5019. },
  5020. focused: function() {
  5021. return (document.activeElement === $module[0]);
  5022. },
  5023. focusedOnSearch: function() {
  5024. return (document.activeElement === $search[0]);
  5025. },
  5026. allFiltered: function() {
  5027. return( (module.is.multiple() || module.has.search()) && !(settings.hideAdditions == false && module.has.userSuggestion()) && !module.has.message() && module.has.allResultsFiltered() );
  5028. },
  5029. hidden: function($subMenu) {
  5030. return !module.is.visible($subMenu);
  5031. },
  5032. initialLoad: function() {
  5033. return initialLoad;
  5034. },
  5035. inObject: function(needle, object) {
  5036. var
  5037. found = false
  5038. ;
  5039. $.each(object, function(index, property) {
  5040. if(property == needle) {
  5041. found = true;
  5042. return true;
  5043. }
  5044. });
  5045. return found;
  5046. },
  5047. multiple: function() {
  5048. return $module.hasClass(className.multiple);
  5049. },
  5050. remote: function() {
  5051. return settings.apiSettings && module.can.useAPI();
  5052. },
  5053. single: function() {
  5054. return !module.is.multiple();
  5055. },
  5056. selectMutation: function(mutations) {
  5057. var
  5058. selectChanged = false
  5059. ;
  5060. $.each(mutations, function(index, mutation) {
  5061. if($(mutation.target).is('select') || $(mutation.addedNodes).is('select')) {
  5062. selectChanged = true;
  5063. return false;
  5064. }
  5065. });
  5066. return selectChanged;
  5067. },
  5068. search: function() {
  5069. return $module.hasClass(className.search);
  5070. },
  5071. searchSelection: function() {
  5072. return ( module.has.search() && $search.parent(selector.dropdown).length === 1 );
  5073. },
  5074. selection: function() {
  5075. return $module.hasClass(className.selection);
  5076. },
  5077. userValue: function(value) {
  5078. return ($.inArray(value, module.get.userValues()) !== -1);
  5079. },
  5080. upward: function($menu) {
  5081. var $element = $menu || $module;
  5082. return $element.hasClass(className.upward);
  5083. },
  5084. visible: function($subMenu) {
  5085. return ($subMenu)
  5086. ? $subMenu.hasClass(className.visible)
  5087. : $menu.hasClass(className.visible)
  5088. ;
  5089. },
  5090. verticallyScrollableContext: function() {
  5091. var
  5092. overflowY = ($context.get(0) !== window)
  5093. ? $context.css('overflow-y')
  5094. : false
  5095. ;
  5096. return (overflowY == 'auto' || overflowY == 'scroll');
  5097. },
  5098. horizontallyScrollableContext: function() {
  5099. var
  5100. overflowX = ($context.get(0) !== window)
  5101. ? $context.css('overflow-X')
  5102. : false
  5103. ;
  5104. return (overflowX == 'auto' || overflowX == 'scroll');
  5105. }
  5106. },
  5107. can: {
  5108. activate: function($item) {
  5109. if(settings.useLabels) {
  5110. return true;
  5111. }
  5112. if(!module.has.maxSelections()) {
  5113. return true;
  5114. }
  5115. if(module.has.maxSelections() && $item.hasClass(className.active)) {
  5116. return true;
  5117. }
  5118. return false;
  5119. },
  5120. openDownward: function($subMenu) {
  5121. var
  5122. $currentMenu = $subMenu || $menu,
  5123. canOpenDownward = true,
  5124. onScreen = {},
  5125. calculations
  5126. ;
  5127. $currentMenu
  5128. .addClass(className.loading)
  5129. ;
  5130. calculations = {
  5131. context: {
  5132. offset : ($context.get(0) === window)
  5133. ? { top: 0, left: 0}
  5134. : $context.offset(),
  5135. scrollTop : $context.scrollTop(),
  5136. height : $context.outerHeight()
  5137. },
  5138. menu : {
  5139. offset: $currentMenu.offset(),
  5140. height: $currentMenu.outerHeight()
  5141. }
  5142. };
  5143. if(module.is.verticallyScrollableContext()) {
  5144. calculations.menu.offset.top += calculations.context.scrollTop;
  5145. }
  5146. onScreen = {
  5147. above : (calculations.context.scrollTop) <= calculations.menu.offset.top - calculations.context.offset.top - calculations.menu.height,
  5148. below : (calculations.context.scrollTop + calculations.context.height) >= calculations.menu.offset.top - calculations.context.offset.top + calculations.menu.height
  5149. };
  5150. if(onScreen.below) {
  5151. module.verbose('Dropdown can fit in context downward', onScreen);
  5152. canOpenDownward = true;
  5153. }
  5154. else if(!onScreen.below && !onScreen.above) {
  5155. module.verbose('Dropdown cannot fit in either direction, favoring downward', onScreen);
  5156. canOpenDownward = true;
  5157. }
  5158. else {
  5159. module.verbose('Dropdown cannot fit below, opening upward', onScreen);
  5160. canOpenDownward = false;
  5161. }
  5162. $currentMenu.removeClass(className.loading);
  5163. return canOpenDownward;
  5164. },
  5165. openRightward: function($subMenu) {
  5166. var
  5167. $currentMenu = $subMenu || $menu,
  5168. canOpenRightward = true,
  5169. isOffscreenRight = false,
  5170. calculations
  5171. ;
  5172. $currentMenu
  5173. .addClass(className.loading)
  5174. ;
  5175. calculations = {
  5176. context: {
  5177. offset : ($context.get(0) === window)
  5178. ? { top: 0, left: 0}
  5179. : $context.offset(),
  5180. scrollLeft : $context.scrollLeft(),
  5181. width : $context.outerWidth()
  5182. },
  5183. menu: {
  5184. offset : $currentMenu.offset(),
  5185. width : $currentMenu.outerWidth()
  5186. }
  5187. };
  5188. if(module.is.horizontallyScrollableContext()) {
  5189. calculations.menu.offset.left += calculations.context.scrollLeft;
  5190. }
  5191. isOffscreenRight = (calculations.menu.offset.left - calculations.context.offset.left + calculations.menu.width >= calculations.context.scrollLeft + calculations.context.width);
  5192. if(isOffscreenRight) {
  5193. module.verbose('Dropdown cannot fit in context rightward', isOffscreenRight);
  5194. canOpenRightward = false;
  5195. }
  5196. $currentMenu.removeClass(className.loading);
  5197. return canOpenRightward;
  5198. },
  5199. click: function() {
  5200. return (hasTouch || settings.on == 'click');
  5201. },
  5202. extendSelect: function() {
  5203. return settings.allowAdditions || settings.apiSettings;
  5204. },
  5205. show: function() {
  5206. return !module.is.disabled() && (module.has.items() || module.has.message());
  5207. },
  5208. useAPI: function() {
  5209. return $.fn.api !== undefined;
  5210. }
  5211. },
  5212. animate: {
  5213. show: function(callback, $subMenu) {
  5214. var
  5215. $currentMenu = $subMenu || $menu,
  5216. start = ($subMenu)
  5217. ? function() {}
  5218. : function() {
  5219. module.hideSubMenus();
  5220. module.hideOthers();
  5221. module.set.active();
  5222. },
  5223. transition
  5224. ;
  5225. callback = $.isFunction(callback)
  5226. ? callback
  5227. : function(){}
  5228. ;
  5229. module.verbose('Doing menu show animation', $currentMenu);
  5230. module.set.direction($subMenu);
  5231. transition = module.get.transition($subMenu);
  5232. if( module.is.selection() ) {
  5233. module.set.scrollPosition(module.get.selectedItem(), true);
  5234. }
  5235. if( module.is.hidden($currentMenu) || module.is.animating($currentMenu) ) {
  5236. var displayType = $module.hasClass('column') ? 'flex' : false;
  5237. if(transition == 'none') {
  5238. start();
  5239. $currentMenu.transition({
  5240. displayType: displayType
  5241. }).transition('show');
  5242. callback.call(element);
  5243. }
  5244. else if($.fn.transition !== undefined && $module.transition('is supported')) {
  5245. $currentMenu
  5246. .transition({
  5247. animation : transition + ' in',
  5248. debug : settings.debug,
  5249. verbose : settings.verbose,
  5250. duration : settings.duration,
  5251. queue : true,
  5252. onStart : start,
  5253. displayType: displayType,
  5254. onComplete : function() {
  5255. callback.call(element);
  5256. }
  5257. })
  5258. ;
  5259. }
  5260. else {
  5261. module.error(error.noTransition, transition);
  5262. }
  5263. }
  5264. },
  5265. hide: function(callback, $subMenu) {
  5266. var
  5267. $currentMenu = $subMenu || $menu,
  5268. start = ($subMenu)
  5269. ? function() {}
  5270. : function() {
  5271. if( module.can.click() ) {
  5272. module.unbind.intent();
  5273. }
  5274. module.remove.active();
  5275. },
  5276. transition = module.get.transition($subMenu)
  5277. ;
  5278. callback = $.isFunction(callback)
  5279. ? callback
  5280. : function(){}
  5281. ;
  5282. if( module.is.visible($currentMenu) || module.is.animating($currentMenu) ) {
  5283. module.verbose('Doing menu hide animation', $currentMenu);
  5284. if(transition == 'none') {
  5285. start();
  5286. $currentMenu.transition('hide');
  5287. callback.call(element);
  5288. }
  5289. else if($.fn.transition !== undefined && $module.transition('is supported')) {
  5290. $currentMenu
  5291. .transition({
  5292. animation : transition + ' out',
  5293. duration : settings.duration,
  5294. debug : settings.debug,
  5295. verbose : settings.verbose,
  5296. queue : false,
  5297. onStart : start,
  5298. onComplete : function() {
  5299. callback.call(element);
  5300. }
  5301. })
  5302. ;
  5303. }
  5304. else {
  5305. module.error(error.transition);
  5306. }
  5307. }
  5308. }
  5309. },
  5310. hideAndClear: function() {
  5311. module.remove.searchTerm();
  5312. if( module.has.maxSelections() ) {
  5313. return;
  5314. }
  5315. if(module.has.search()) {
  5316. module.hide(function() {
  5317. module.remove.filteredItem();
  5318. });
  5319. }
  5320. else {
  5321. module.hide();
  5322. }
  5323. },
  5324. delay: {
  5325. show: function() {
  5326. module.verbose('Delaying show event to ensure user intent');
  5327. clearTimeout(module.timer);
  5328. module.timer = setTimeout(module.show, settings.delay.show);
  5329. },
  5330. hide: function() {
  5331. module.verbose('Delaying hide event to ensure user intent');
  5332. clearTimeout(module.timer);
  5333. module.timer = setTimeout(module.hide, settings.delay.hide);
  5334. }
  5335. },
  5336. escape: {
  5337. value: function(value) {
  5338. var
  5339. multipleValues = Array.isArray(value),
  5340. stringValue = (typeof value === 'string'),
  5341. isUnparsable = (!stringValue && !multipleValues),
  5342. hasQuotes = (stringValue && value.search(regExp.quote) !== -1),
  5343. values = []
  5344. ;
  5345. if(isUnparsable || !hasQuotes) {
  5346. return value;
  5347. }
  5348. module.debug('Encoding quote values for use in select', value);
  5349. if(multipleValues) {
  5350. $.each(value, function(index, value){
  5351. values.push(value.replace(regExp.quote, '&quot;'));
  5352. });
  5353. return values;
  5354. }
  5355. return value.replace(regExp.quote, '&quot;');
  5356. },
  5357. string: function(text) {
  5358. text = String(text);
  5359. return text.replace(regExp.escape, '\\$&');
  5360. },
  5361. htmlEntities: function(string) {
  5362. var
  5363. badChars = /[<>"'`]/g,
  5364. shouldEscape = /[&<>"'`]/,
  5365. escape = {
  5366. "<": "&lt;",
  5367. ">": "&gt;",
  5368. '"': "&quot;",
  5369. "'": "&#x27;",
  5370. "`": "&#x60;"
  5371. },
  5372. escapedChar = function(chr) {
  5373. return escape[chr];
  5374. }
  5375. ;
  5376. if(shouldEscape.test(string)) {
  5377. string = string.replace(/&(?![a-z0-9#]{1,6};)/, "&amp;");
  5378. return string.replace(badChars, escapedChar);
  5379. }
  5380. return string;
  5381. }
  5382. },
  5383. setting: function(name, value) {
  5384. module.debug('Changing setting', name, value);
  5385. if( $.isPlainObject(name) ) {
  5386. $.extend(true, settings, name);
  5387. }
  5388. else if(value !== undefined) {
  5389. if($.isPlainObject(settings[name])) {
  5390. $.extend(true, settings[name], value);
  5391. }
  5392. else {
  5393. settings[name] = value;
  5394. }
  5395. }
  5396. else {
  5397. return settings[name];
  5398. }
  5399. },
  5400. internal: function(name, value) {
  5401. if( $.isPlainObject(name) ) {
  5402. $.extend(true, module, name);
  5403. }
  5404. else if(value !== undefined) {
  5405. module[name] = value;
  5406. }
  5407. else {
  5408. return module[name];
  5409. }
  5410. },
  5411. debug: function() {
  5412. if(!settings.silent && settings.debug) {
  5413. if(settings.performance) {
  5414. module.performance.log(arguments);
  5415. }
  5416. else {
  5417. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  5418. module.debug.apply(console, arguments);
  5419. }
  5420. }
  5421. },
  5422. verbose: function() {
  5423. if(!settings.silent && settings.verbose && settings.debug) {
  5424. if(settings.performance) {
  5425. module.performance.log(arguments);
  5426. }
  5427. else {
  5428. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  5429. module.verbose.apply(console, arguments);
  5430. }
  5431. }
  5432. },
  5433. error: function() {
  5434. if(!settings.silent) {
  5435. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  5436. module.error.apply(console, arguments);
  5437. }
  5438. },
  5439. performance: {
  5440. log: function(message) {
  5441. var
  5442. currentTime,
  5443. executionTime,
  5444. previousTime
  5445. ;
  5446. if(settings.performance) {
  5447. currentTime = new Date().getTime();
  5448. previousTime = time || currentTime;
  5449. executionTime = currentTime - previousTime;
  5450. time = currentTime;
  5451. performance.push({
  5452. 'Name' : message[0],
  5453. 'Arguments' : [].slice.call(message, 1) || '',
  5454. 'Element' : element,
  5455. 'Execution Time' : executionTime
  5456. });
  5457. }
  5458. clearTimeout(module.performance.timer);
  5459. module.performance.timer = setTimeout(module.performance.display, 500);
  5460. },
  5461. display: function() {
  5462. var
  5463. title = settings.name + ':',
  5464. totalTime = 0
  5465. ;
  5466. time = false;
  5467. clearTimeout(module.performance.timer);
  5468. $.each(performance, function(index, data) {
  5469. totalTime += data['Execution Time'];
  5470. });
  5471. title += ' ' + totalTime + 'ms';
  5472. if(moduleSelector) {
  5473. title += ' \'' + moduleSelector + '\'';
  5474. }
  5475. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  5476. console.groupCollapsed(title);
  5477. if(console.table) {
  5478. console.table(performance);
  5479. }
  5480. else {
  5481. $.each(performance, function(index, data) {
  5482. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  5483. });
  5484. }
  5485. console.groupEnd();
  5486. }
  5487. performance = [];
  5488. }
  5489. },
  5490. invoke: function(query, passedArguments, context) {
  5491. var
  5492. object = instance,
  5493. maxDepth,
  5494. found,
  5495. response
  5496. ;
  5497. passedArguments = passedArguments || queryArguments;
  5498. context = element || context;
  5499. if(typeof query == 'string' && object !== undefined) {
  5500. query = query.split(/[\. ]/);
  5501. maxDepth = query.length - 1;
  5502. $.each(query, function(depth, value) {
  5503. var camelCaseValue = (depth != maxDepth)
  5504. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  5505. : query
  5506. ;
  5507. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  5508. object = object[camelCaseValue];
  5509. }
  5510. else if( object[camelCaseValue] !== undefined ) {
  5511. found = object[camelCaseValue];
  5512. return false;
  5513. }
  5514. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  5515. object = object[value];
  5516. }
  5517. else if( object[value] !== undefined ) {
  5518. found = object[value];
  5519. return false;
  5520. }
  5521. else {
  5522. module.error(error.method, query);
  5523. return false;
  5524. }
  5525. });
  5526. }
  5527. if ( $.isFunction( found ) ) {
  5528. response = found.apply(context, passedArguments);
  5529. }
  5530. else if(found !== undefined) {
  5531. response = found;
  5532. }
  5533. if(Array.isArray(returnedValue)) {
  5534. returnedValue.push(response);
  5535. }
  5536. else if(returnedValue !== undefined) {
  5537. returnedValue = [returnedValue, response];
  5538. }
  5539. else if(response !== undefined) {
  5540. returnedValue = response;
  5541. }
  5542. return found;
  5543. }
  5544. };
  5545. if(methodInvoked) {
  5546. if(instance === undefined) {
  5547. module.initialize();
  5548. }
  5549. module.invoke(query);
  5550. }
  5551. else {
  5552. if(instance !== undefined) {
  5553. instance.invoke('destroy');
  5554. }
  5555. module.initialize();
  5556. }
  5557. })
  5558. ;
  5559. return (returnedValue !== undefined)
  5560. ? returnedValue
  5561. : $allModules
  5562. ;
  5563. };
  5564. $.fn.dropdown.settings = {
  5565. silent : false,
  5566. debug : false,
  5567. verbose : false,
  5568. performance : true,
  5569. on : 'click', // what event should show menu action on item selection
  5570. action : 'activate', // action on item selection (nothing, activate, select, combo, hide, function(){})
  5571. values : false, // specify values to use for dropdown
  5572. clearable : false, // whether the value of the dropdown can be cleared
  5573. apiSettings : false,
  5574. selectOnKeydown : true, // Whether selection should occur automatically when keyboard shortcuts used
  5575. minCharacters : 0, // Minimum characters required to trigger API call
  5576. filterRemoteData : false, // Whether API results should be filtered after being returned for query term
  5577. saveRemoteData : true, // Whether remote name/value pairs should be stored in sessionStorage to allow remote data to be restored on page refresh
  5578. throttle : 200, // How long to wait after last user input to search remotely
  5579. context : window, // Context to use when determining if on screen
  5580. direction : 'auto', // Whether dropdown should always open in one direction
  5581. keepOnScreen : true, // Whether dropdown should check whether it is on screen before showing
  5582. match : 'both', // what to match against with search selection (both, text, or label)
  5583. fullTextSearch : false, // search anywhere in value (set to 'exact' to require exact matches)
  5584. ignoreDiacritics : false, // match results also if they contain diacritics of the same base character (for example searching for "a" will also match "á" or "â" or "à", etc...)
  5585. hideDividers : false, // Whether to hide any divider elements (specified in selector.divider) that are sibling to any items when searched (set to true will hide all dividers, set to 'empty' will hide them when they are not followed by a visible item)
  5586. placeholder : 'auto', // whether to convert blank <select> values to placeholder text
  5587. preserveHTML : true, // preserve html when selecting value
  5588. sortSelect : false, // sort selection on init
  5589. forceSelection : true, // force a choice on blur with search selection
  5590. allowAdditions : false, // whether multiple select should allow user added values
  5591. ignoreCase : false, // whether to consider case sensitivity when creating labels
  5592. ignoreSearchCase : true, // whether to consider case sensitivity when filtering items
  5593. hideAdditions : true, // whether or not to hide special message prompting a user they can enter a value
  5594. maxSelections : false, // When set to a number limits the number of selections to this count
  5595. useLabels : true, // whether multiple select should filter currently active selections from choices
  5596. delimiter : ',', // when multiselect uses normal <input> the values will be delimited with this character
  5597. showOnFocus : true, // show menu on focus
  5598. allowReselection : false, // whether current value should trigger callbacks when reselected
  5599. allowTab : true, // add tabindex to element
  5600. allowCategorySelection : false, // allow elements with sub-menus to be selected
  5601. fireOnInit : false, // Whether callbacks should fire when initializing dropdown values
  5602. transition : 'auto', // auto transition will slide down or up based on direction
  5603. duration : 200, // duration of transition
  5604. glyphWidth : 1.037, // widest glyph width in em (W is 1.037 em) used to calculate multiselect input width
  5605. headerDivider : true, // whether option headers should have an additional divider line underneath when converted from <select> <optgroup>
  5606. // label settings on multi-select
  5607. label: {
  5608. transition : 'scale',
  5609. duration : 200,
  5610. variation : false
  5611. },
  5612. // delay before event
  5613. delay : {
  5614. hide : 300,
  5615. show : 200,
  5616. search : 20,
  5617. touch : 50
  5618. },
  5619. /* Callbacks */
  5620. onChange : function(value, text, $selected){},
  5621. onAdd : function(value, text, $selected){},
  5622. onRemove : function(value, text, $selected){},
  5623. onLabelSelect : function($selectedLabels){},
  5624. onLabelCreate : function(value, text) { return $(this); },
  5625. onLabelRemove : function(value) { return true; },
  5626. onNoResults : function(searchTerm) { return true; },
  5627. onShow : function(){},
  5628. onHide : function(){},
  5629. /* Component */
  5630. name : 'Dropdown',
  5631. namespace : 'dropdown',
  5632. message: {
  5633. addResult : 'Add <b>{term}</b>',
  5634. count : '{count} selected',
  5635. maxSelections : 'Max {maxCount} selections',
  5636. noResults : 'No results found.',
  5637. serverError : 'There was an error contacting the server'
  5638. },
  5639. error : {
  5640. action : 'You called a dropdown action that was not defined',
  5641. alreadySetup : 'Once a select has been initialized behaviors must be called on the created ui dropdown',
  5642. labels : 'Allowing user additions currently requires the use of labels.',
  5643. missingMultiple : '<select> requires multiple property to be set to correctly preserve multiple values',
  5644. method : 'The method you called is not defined.',
  5645. noAPI : 'The API module is required to load resources remotely',
  5646. noStorage : 'Saving remote data requires session storage',
  5647. noTransition : 'This module requires ui transitions <https://github.com/Semantic-Org/UI-Transition>',
  5648. noNormalize : '"ignoreDiacritics" setting will be ignored. Browser does not support String().normalize(). You may consider including <https://cdn.jsdelivr.net/npm/unorm@1.4.1/lib/unorm.min.js> as a polyfill.'
  5649. },
  5650. regExp : {
  5651. escape : /[-[\]{}()*+?.,\\^$|#\s:=@]/g,
  5652. quote : /"/g
  5653. },
  5654. metadata : {
  5655. defaultText : 'defaultText',
  5656. defaultValue : 'defaultValue',
  5657. placeholderText : 'placeholder',
  5658. text : 'text',
  5659. value : 'value'
  5660. },
  5661. // property names for remote query
  5662. fields: {
  5663. remoteValues : 'results', // grouping for api results
  5664. values : 'values', // grouping for all dropdown values
  5665. disabled : 'disabled', // whether value should be disabled
  5666. name : 'name', // displayed dropdown text
  5667. value : 'value', // actual dropdown value
  5668. text : 'text', // displayed text when selected
  5669. type : 'type', // type of dropdown element
  5670. image : 'image', // optional image path
  5671. imageClass : 'imageClass', // optional individual class for image
  5672. icon : 'icon', // optional icon name
  5673. iconClass : 'iconClass', // optional individual class for icon (for example to use flag instead)
  5674. class : 'class', // optional individual class for item/header
  5675. divider : 'divider' // optional divider append for group headers
  5676. },
  5677. keys : {
  5678. backspace : 8,
  5679. delimiter : 188, // comma
  5680. deleteKey : 46,
  5681. enter : 13,
  5682. escape : 27,
  5683. pageUp : 33,
  5684. pageDown : 34,
  5685. leftArrow : 37,
  5686. upArrow : 38,
  5687. rightArrow : 39,
  5688. downArrow : 40
  5689. },
  5690. selector : {
  5691. addition : '.addition',
  5692. divider : '.divider, .header',
  5693. dropdown : '.ui.dropdown',
  5694. hidden : '.hidden',
  5695. icon : '> .dropdown.icon',
  5696. input : '> input[type="hidden"], > select',
  5697. item : '.item',
  5698. label : '> .label',
  5699. remove : '> .label > .delete.icon',
  5700. siblingLabel : '.label',
  5701. menu : '.menu',
  5702. message : '.message',
  5703. menuIcon : '.dropdown.icon',
  5704. search : 'input.search, .menu > .search > input, .menu input.search',
  5705. sizer : '> span.sizer',
  5706. text : '> .text:not(.icon)',
  5707. unselectable : '.disabled, .filtered',
  5708. clearIcon : '> .remove.icon'
  5709. },
  5710. className : {
  5711. active : 'active',
  5712. addition : 'addition',
  5713. animating : 'animating',
  5714. disabled : 'disabled',
  5715. empty : 'empty',
  5716. dropdown : 'ui dropdown',
  5717. filtered : 'filtered',
  5718. hidden : 'hidden transition',
  5719. icon : 'icon',
  5720. image : 'image',
  5721. item : 'item',
  5722. label : 'ui label',
  5723. loading : 'loading',
  5724. menu : 'menu',
  5725. message : 'message',
  5726. multiple : 'multiple',
  5727. placeholder : 'default',
  5728. sizer : 'sizer',
  5729. search : 'search',
  5730. selected : 'selected',
  5731. selection : 'selection',
  5732. upward : 'upward',
  5733. leftward : 'left',
  5734. visible : 'visible',
  5735. clearable : 'clearable',
  5736. noselection : 'noselection',
  5737. delete : 'delete',
  5738. header : 'header',
  5739. divider : 'divider',
  5740. groupIcon : '',
  5741. unfilterable : 'unfilterable'
  5742. }
  5743. };
  5744. /* Templates */
  5745. $.fn.dropdown.settings.templates = {
  5746. deQuote: function(string) {
  5747. return String(string).replace(/"/g,"");
  5748. },
  5749. escape: function(string, preserveHTML) {
  5750. if (preserveHTML){
  5751. return string;
  5752. }
  5753. var
  5754. badChars = /[<>"'`]/g,
  5755. shouldEscape = /[&<>"'`]/,
  5756. escape = {
  5757. "<": "&lt;",
  5758. ">": "&gt;",
  5759. '"': "&quot;",
  5760. "'": "&#x27;",
  5761. "`": "&#x60;"
  5762. },
  5763. escapedChar = function(chr) {
  5764. return escape[chr];
  5765. }
  5766. ;
  5767. if(shouldEscape.test(string)) {
  5768. string = string.replace(/&(?![a-z0-9#]{1,6};)/, "&amp;");
  5769. return string.replace(badChars, escapedChar);
  5770. }
  5771. return string;
  5772. },
  5773. // generates dropdown from select values
  5774. dropdown: function(select, fields, preserveHTML, className) {
  5775. var
  5776. placeholder = select.placeholder || false,
  5777. html = '',
  5778. escape = $.fn.dropdown.settings.templates.escape
  5779. ;
  5780. html += '<i class="dropdown icon"></i>';
  5781. if(placeholder) {
  5782. html += '<div class="default text">' + escape(placeholder,preserveHTML) + '</div>';
  5783. }
  5784. else {
  5785. html += '<div class="text"></div>';
  5786. }
  5787. html += '<div class="'+className.menu+'">';
  5788. html += $.fn.dropdown.settings.templates.menu(select, fields, preserveHTML,className);
  5789. html += '</div>';
  5790. return html;
  5791. },
  5792. // generates just menu from select
  5793. menu: function(response, fields, preserveHTML, className) {
  5794. var
  5795. values = response[fields.values] || [],
  5796. html = '',
  5797. escape = $.fn.dropdown.settings.templates.escape,
  5798. deQuote = $.fn.dropdown.settings.templates.deQuote
  5799. ;
  5800. $.each(values, function(index, option) {
  5801. var
  5802. itemType = (option[fields.type])
  5803. ? option[fields.type]
  5804. : 'item'
  5805. ;
  5806. if( itemType === 'item' ) {
  5807. var
  5808. maybeText = (option[fields.text])
  5809. ? ' data-text="' + deQuote(option[fields.text]) + '"'
  5810. : '',
  5811. maybeDisabled = (option[fields.disabled])
  5812. ? className.disabled+' '
  5813. : ''
  5814. ;
  5815. html += '<div class="'+ maybeDisabled + (option[fields.class] ? deQuote(option[fields.class]) : className.item)+'" data-value="' + deQuote(option[fields.value]) + '"' + maybeText + '>';
  5816. if(option[fields.image]) {
  5817. html += '<img class="'+(option[fields.imageClass] ? deQuote(option[fields.imageClass]) : className.image)+'" src="' + deQuote(option[fields.image]) + '">';
  5818. }
  5819. if(option[fields.icon]) {
  5820. html += '<i class="'+deQuote(option[fields.icon])+' '+(option[fields.iconClass] ? deQuote(option[fields.iconClass]) : className.icon)+'"></i>';
  5821. }
  5822. html += escape(option[fields.name] || '', preserveHTML);
  5823. html += '</div>';
  5824. } else if (itemType === 'header') {
  5825. var groupName = escape(option[fields.name] || '', preserveHTML),
  5826. groupIcon = option[fields.icon] ? deQuote(option[fields.icon]) : className.groupIcon
  5827. ;
  5828. if(groupName !== '' || groupIcon !== '') {
  5829. html += '<div class="' + (option[fields.class] ? deQuote(option[fields.class]) : className.header) + '">';
  5830. if (groupIcon !== '') {
  5831. html += '<i class="' + groupIcon + ' ' + (option[fields.iconClass] ? deQuote(option[fields.iconClass]) : className.icon) + '"></i>';
  5832. }
  5833. html += groupName;
  5834. html += '</div>';
  5835. }
  5836. if(option[fields.divider]){
  5837. html += '<div class="'+className.divider+'"></div>';
  5838. }
  5839. }
  5840. });
  5841. return html;
  5842. },
  5843. // generates label for multiselect
  5844. label: function(value, text, preserveHTML, className) {
  5845. var
  5846. escape = $.fn.dropdown.settings.templates.escape;
  5847. return escape(text,preserveHTML) + '<i class="'+className.delete+' icon"></i>';
  5848. },
  5849. // generates messages like "No results"
  5850. message: function(message) {
  5851. return message;
  5852. },
  5853. // generates user addition to selection menu
  5854. addition: function(choice) {
  5855. return choice;
  5856. }
  5857. };
  5858. })( jQuery, window, document );
  5859. /*!
  5860. * # Fomantic-UI - Form Validation
  5861. * http://github.com/fomantic/Fomantic-UI/
  5862. *
  5863. *
  5864. * Released under the MIT license
  5865. * http://opensource.org/licenses/MIT
  5866. *
  5867. */
  5868. ;(function ($, window, document, undefined) {
  5869. 'use strict';
  5870. $.isFunction = $.isFunction || function(obj) {
  5871. return typeof obj === "function" && typeof obj.nodeType !== "number";
  5872. };
  5873. window = (typeof window != 'undefined' && window.Math == Math)
  5874. ? window
  5875. : (typeof self != 'undefined' && self.Math == Math)
  5876. ? self
  5877. : Function('return this')()
  5878. ;
  5879. $.fn.form = function(parameters) {
  5880. var
  5881. $allModules = $(this),
  5882. moduleSelector = $allModules.selector || '',
  5883. time = new Date().getTime(),
  5884. performance = [],
  5885. query = arguments[0],
  5886. legacyParameters = arguments[1],
  5887. methodInvoked = (typeof query == 'string'),
  5888. queryArguments = [].slice.call(arguments, 1),
  5889. returnedValue
  5890. ;
  5891. $allModules
  5892. .each(function() {
  5893. var
  5894. $module = $(this),
  5895. element = this,
  5896. formErrors = [],
  5897. keyHeldDown = false,
  5898. // set at run-time
  5899. $field,
  5900. $group,
  5901. $message,
  5902. $prompt,
  5903. $submit,
  5904. $clear,
  5905. $reset,
  5906. settings,
  5907. validation,
  5908. metadata,
  5909. selector,
  5910. className,
  5911. regExp,
  5912. error,
  5913. namespace,
  5914. moduleNamespace,
  5915. eventNamespace,
  5916. submitting = false,
  5917. dirty = false,
  5918. history = ['clean', 'clean'],
  5919. instance,
  5920. module
  5921. ;
  5922. module = {
  5923. initialize: function() {
  5924. // settings grabbed at run time
  5925. module.get.settings();
  5926. if(methodInvoked) {
  5927. if(instance === undefined) {
  5928. module.instantiate();
  5929. }
  5930. module.invoke(query);
  5931. }
  5932. else {
  5933. if(instance !== undefined) {
  5934. instance.invoke('destroy');
  5935. }
  5936. module.verbose('Initializing form validation', $module, settings);
  5937. module.bindEvents();
  5938. module.set.defaults();
  5939. if (settings.autoCheckRequired) {
  5940. module.set.autoCheck();
  5941. }
  5942. module.instantiate();
  5943. }
  5944. },
  5945. instantiate: function() {
  5946. module.verbose('Storing instance of module', module);
  5947. instance = module;
  5948. $module
  5949. .data(moduleNamespace, module)
  5950. ;
  5951. },
  5952. destroy: function() {
  5953. module.verbose('Destroying previous module', instance);
  5954. module.removeEvents();
  5955. $module
  5956. .removeData(moduleNamespace)
  5957. ;
  5958. },
  5959. refresh: function() {
  5960. module.verbose('Refreshing selector cache');
  5961. $field = $module.find(selector.field);
  5962. $group = $module.find(selector.group);
  5963. $message = $module.find(selector.message);
  5964. $prompt = $module.find(selector.prompt);
  5965. $submit = $module.find(selector.submit);
  5966. $clear = $module.find(selector.clear);
  5967. $reset = $module.find(selector.reset);
  5968. },
  5969. submit: function() {
  5970. module.verbose('Submitting form', $module);
  5971. submitting = true;
  5972. $module.submit();
  5973. },
  5974. attachEvents: function(selector, action) {
  5975. action = action || 'submit';
  5976. $(selector).on('click' + eventNamespace, function(event) {
  5977. module[action]();
  5978. event.preventDefault();
  5979. });
  5980. },
  5981. bindEvents: function() {
  5982. module.verbose('Attaching form events');
  5983. $module
  5984. .on('submit' + eventNamespace, module.validate.form)
  5985. .on('blur' + eventNamespace, selector.field, module.event.field.blur)
  5986. .on('click' + eventNamespace, selector.submit, module.submit)
  5987. .on('click' + eventNamespace, selector.reset, module.reset)
  5988. .on('click' + eventNamespace, selector.clear, module.clear)
  5989. ;
  5990. if(settings.keyboardShortcuts) {
  5991. $module.on('keydown' + eventNamespace, selector.field, module.event.field.keydown);
  5992. }
  5993. $field.each(function(index, el) {
  5994. var
  5995. $input = $(el),
  5996. type = $input.prop('type'),
  5997. inputEvent = module.get.changeEvent(type, $input)
  5998. ;
  5999. $input.on(inputEvent + eventNamespace, module.event.field.change);
  6000. });
  6001. // Dirty events
  6002. if (settings.preventLeaving) {
  6003. $(window).on('beforeunload' + eventNamespace, module.event.beforeUnload);
  6004. }
  6005. $field.on('change click keyup keydown blur', function(e) {
  6006. $(this).triggerHandler(e.type + ".dirty");
  6007. });
  6008. $field.on('change.dirty click.dirty keyup.dirty keydown.dirty blur.dirty', module.determine.isDirty);
  6009. $module.on('dirty' + eventNamespace, function(e) {
  6010. settings.onDirty.call();
  6011. });
  6012. $module.on('clean' + eventNamespace, function(e) {
  6013. settings.onClean.call();
  6014. })
  6015. },
  6016. clear: function() {
  6017. $field.each(function (index, el) {
  6018. var
  6019. $field = $(el),
  6020. $element = $field.parent(),
  6021. $fieldGroup = $field.closest($group),
  6022. $prompt = $fieldGroup.find(selector.prompt),
  6023. $calendar = $field.closest(selector.uiCalendar),
  6024. defaultValue = $field.data(metadata.defaultValue) || '',
  6025. isCheckbox = $element.is(selector.uiCheckbox),
  6026. isDropdown = $element.is(selector.uiDropdown) && module.can.useElement('dropdown'),
  6027. isCalendar = ($calendar.length > 0 && module.can.useElement('calendar')),
  6028. isErrored = $fieldGroup.hasClass(className.error)
  6029. ;
  6030. if(isErrored) {
  6031. module.verbose('Resetting error on field', $fieldGroup);
  6032. $fieldGroup.removeClass(className.error);
  6033. $prompt.remove();
  6034. }
  6035. if(isDropdown) {
  6036. module.verbose('Resetting dropdown value', $element, defaultValue);
  6037. $element.dropdown('clear', true);
  6038. }
  6039. else if(isCheckbox) {
  6040. $field.prop('checked', false);
  6041. }
  6042. else if (isCalendar) {
  6043. $calendar.calendar('clear');
  6044. }
  6045. else {
  6046. module.verbose('Resetting field value', $field, defaultValue);
  6047. $field.val('');
  6048. }
  6049. });
  6050. module.remove.states();
  6051. },
  6052. reset: function() {
  6053. $field.each(function (index, el) {
  6054. var
  6055. $field = $(el),
  6056. $element = $field.parent(),
  6057. $fieldGroup = $field.closest($group),
  6058. $calendar = $field.closest(selector.uiCalendar),
  6059. $prompt = $fieldGroup.find(selector.prompt),
  6060. defaultValue = $field.data(metadata.defaultValue),
  6061. isCheckbox = $element.is(selector.uiCheckbox),
  6062. isDropdown = $element.is(selector.uiDropdown) && module.can.useElement('dropdown'),
  6063. isCalendar = ($calendar.length > 0 && module.can.useElement('calendar')),
  6064. isErrored = $fieldGroup.hasClass(className.error)
  6065. ;
  6066. if(defaultValue === undefined) {
  6067. return;
  6068. }
  6069. if(isErrored) {
  6070. module.verbose('Resetting error on field', $fieldGroup);
  6071. $fieldGroup.removeClass(className.error);
  6072. $prompt.remove();
  6073. }
  6074. if(isDropdown) {
  6075. module.verbose('Resetting dropdown value', $element, defaultValue);
  6076. $element.dropdown('restore defaults', true);
  6077. }
  6078. else if(isCheckbox) {
  6079. module.verbose('Resetting checkbox value', $element, defaultValue);
  6080. $field.prop('checked', defaultValue);
  6081. }
  6082. else if (isCalendar) {
  6083. $calendar.calendar('set date', defaultValue);
  6084. }
  6085. else {
  6086. module.verbose('Resetting field value', $field, defaultValue);
  6087. $field.val(defaultValue);
  6088. }
  6089. });
  6090. module.remove.states();
  6091. },
  6092. determine: {
  6093. isValid: function() {
  6094. var
  6095. allValid = true
  6096. ;
  6097. $.each(validation, function(fieldName, field) {
  6098. if( !( module.validate.field(field, fieldName, true) ) ) {
  6099. allValid = false;
  6100. }
  6101. });
  6102. return allValid;
  6103. },
  6104. isDirty: function(e) {
  6105. var formIsDirty = false;
  6106. $field.each(function(index, el) {
  6107. var
  6108. $el = $(el),
  6109. isCheckbox = ($el.filter(selector.checkbox).length > 0),
  6110. isDirty
  6111. ;
  6112. if (isCheckbox) {
  6113. isDirty = module.is.checkboxDirty($el);
  6114. } else {
  6115. isDirty = module.is.fieldDirty($el);
  6116. }
  6117. $el.data(settings.metadata.isDirty, isDirty);
  6118. formIsDirty |= isDirty;
  6119. });
  6120. if (formIsDirty) {
  6121. module.set.dirty();
  6122. } else {
  6123. module.set.clean();
  6124. }
  6125. if (e && e.namespace === 'dirty') {
  6126. e.stopImmediatePropagation();
  6127. e.preventDefault();
  6128. }
  6129. }
  6130. },
  6131. is: {
  6132. bracketedRule: function(rule) {
  6133. return (rule.type && rule.type.match(settings.regExp.bracket));
  6134. },
  6135. shorthandFields: function(fields) {
  6136. var
  6137. fieldKeys = Object.keys(fields),
  6138. firstRule = fields[fieldKeys[0]]
  6139. ;
  6140. return module.is.shorthandRules(firstRule);
  6141. },
  6142. // duck type rule test
  6143. shorthandRules: function(rules) {
  6144. return (typeof rules == 'string' || Array.isArray(rules));
  6145. },
  6146. empty: function($field) {
  6147. if(!$field || $field.length === 0) {
  6148. return true;
  6149. }
  6150. else if($field.is(selector.checkbox)) {
  6151. return !$field.is(':checked');
  6152. }
  6153. else {
  6154. return module.is.blank($field);
  6155. }
  6156. },
  6157. blank: function($field) {
  6158. return String($field.val()).trim() === '';
  6159. },
  6160. valid: function(field, showErrors) {
  6161. var
  6162. allValid = true
  6163. ;
  6164. if(field) {
  6165. module.verbose('Checking if field is valid', field);
  6166. return module.validate.field(validation[field], field, !!showErrors);
  6167. }
  6168. else {
  6169. module.verbose('Checking if form is valid');
  6170. $.each(validation, function(fieldName, field) {
  6171. if( !module.is.valid(fieldName, showErrors) ) {
  6172. allValid = false;
  6173. }
  6174. });
  6175. return allValid;
  6176. }
  6177. },
  6178. dirty: function() {
  6179. return dirty;
  6180. },
  6181. clean: function() {
  6182. return !dirty;
  6183. },
  6184. fieldDirty: function($el) {
  6185. var initialValue = $el.data(metadata.defaultValue);
  6186. // Explicitly check for null/undefined here as value may be `false`, so ($el.data(dataInitialValue) || '') would not work
  6187. if (initialValue == null) { initialValue = ''; }
  6188. else if(Array.isArray(initialValue)) {
  6189. initialValue = initialValue.toString();
  6190. }
  6191. var currentValue = $el.val();
  6192. if (currentValue == null) { currentValue = ''; }
  6193. // multiple select values are returned as arrays which are never equal, so do string conversion first
  6194. else if(Array.isArray(currentValue)) {
  6195. currentValue = currentValue.toString();
  6196. }
  6197. // Boolean values can be encoded as "true/false" or "True/False" depending on underlying frameworks so we need a case insensitive comparison
  6198. var boolRegex = /^(true|false)$/i;
  6199. var isBoolValue = boolRegex.test(initialValue) && boolRegex.test(currentValue);
  6200. if (isBoolValue) {
  6201. var regex = new RegExp("^" + initialValue + "$", "i");
  6202. return !regex.test(currentValue);
  6203. }
  6204. return currentValue !== initialValue;
  6205. },
  6206. checkboxDirty: function($el) {
  6207. var initialValue = $el.data(metadata.defaultValue);
  6208. var currentValue = $el.is(":checked");
  6209. return initialValue !== currentValue;
  6210. },
  6211. justDirty: function() {
  6212. return (history[0] === 'dirty');
  6213. },
  6214. justClean: function() {
  6215. return (history[0] === 'clean');
  6216. }
  6217. },
  6218. removeEvents: function() {
  6219. $module.off(eventNamespace);
  6220. $field.off(eventNamespace);
  6221. $submit.off(eventNamespace);
  6222. $field.off(eventNamespace);
  6223. },
  6224. event: {
  6225. field: {
  6226. keydown: function(event) {
  6227. var
  6228. $field = $(this),
  6229. key = event.which,
  6230. isInput = $field.is(selector.input),
  6231. isCheckbox = $field.is(selector.checkbox),
  6232. isInDropdown = ($field.closest(selector.uiDropdown).length > 0),
  6233. keyCode = {
  6234. enter : 13,
  6235. escape : 27
  6236. }
  6237. ;
  6238. if( key == keyCode.escape) {
  6239. module.verbose('Escape key pressed blurring field');
  6240. $field
  6241. .blur()
  6242. ;
  6243. }
  6244. if(!event.ctrlKey && key == keyCode.enter && isInput && !isInDropdown && !isCheckbox) {
  6245. if(!keyHeldDown) {
  6246. $field.one('keyup' + eventNamespace, module.event.field.keyup);
  6247. module.submit();
  6248. module.debug('Enter pressed on input submitting form');
  6249. }
  6250. keyHeldDown = true;
  6251. }
  6252. },
  6253. keyup: function() {
  6254. keyHeldDown = false;
  6255. },
  6256. blur: function(event) {
  6257. var
  6258. $field = $(this),
  6259. $fieldGroup = $field.closest($group),
  6260. validationRules = module.get.validation($field)
  6261. ;
  6262. if( $fieldGroup.hasClass(className.error) ) {
  6263. module.debug('Revalidating field', $field, validationRules);
  6264. if(validationRules) {
  6265. module.validate.field( validationRules );
  6266. }
  6267. }
  6268. else if(settings.on == 'blur') {
  6269. if(validationRules) {
  6270. module.validate.field( validationRules );
  6271. }
  6272. }
  6273. },
  6274. change: function(event) {
  6275. var
  6276. $field = $(this),
  6277. $fieldGroup = $field.closest($group),
  6278. validationRules = module.get.validation($field)
  6279. ;
  6280. if(validationRules && (settings.on == 'change' || ( $fieldGroup.hasClass(className.error) && settings.revalidate) )) {
  6281. clearTimeout(module.timer);
  6282. module.timer = setTimeout(function() {
  6283. module.debug('Revalidating field', $field, module.get.validation($field));
  6284. module.validate.field( validationRules );
  6285. if(!settings.inline) {
  6286. module.validate.form(false,true);
  6287. }
  6288. }, settings.delay);
  6289. }
  6290. }
  6291. },
  6292. beforeUnload: function(event) {
  6293. if (module.is.dirty() && !submitting) {
  6294. var event = event || window.event;
  6295. // For modern browsers
  6296. if (event) {
  6297. event.returnValue = settings.text.leavingMessage;
  6298. }
  6299. // For olders...
  6300. return settings.text.leavingMessage;
  6301. }
  6302. }
  6303. },
  6304. get: {
  6305. ancillaryValue: function(rule) {
  6306. if(!rule.type || (!rule.value && !module.is.bracketedRule(rule))) {
  6307. return false;
  6308. }
  6309. return (rule.value !== undefined)
  6310. ? rule.value
  6311. : rule.type.match(settings.regExp.bracket)[1] + ''
  6312. ;
  6313. },
  6314. ruleName: function(rule) {
  6315. if( module.is.bracketedRule(rule) ) {
  6316. return rule.type.replace(rule.type.match(settings.regExp.bracket)[0], '');
  6317. }
  6318. return rule.type;
  6319. },
  6320. changeEvent: function(type, $input) {
  6321. if(type == 'checkbox' || type == 'radio' || type == 'hidden' || $input.is('select')) {
  6322. return 'change';
  6323. }
  6324. else {
  6325. return module.get.inputEvent();
  6326. }
  6327. },
  6328. inputEvent: function() {
  6329. return (document.createElement('input').oninput !== undefined)
  6330. ? 'input'
  6331. : (document.createElement('input').onpropertychange !== undefined)
  6332. ? 'propertychange'
  6333. : 'keyup'
  6334. ;
  6335. },
  6336. fieldsFromShorthand: function(fields) {
  6337. var
  6338. fullFields = {}
  6339. ;
  6340. $.each(fields, function(name, rules) {
  6341. if(typeof rules == 'string') {
  6342. rules = [rules];
  6343. }
  6344. fullFields[name] = {
  6345. rules: []
  6346. };
  6347. $.each(rules, function(index, rule) {
  6348. fullFields[name].rules.push({ type: rule });
  6349. });
  6350. });
  6351. return fullFields;
  6352. },
  6353. prompt: function(rule, field) {
  6354. var
  6355. ruleName = module.get.ruleName(rule),
  6356. ancillary = module.get.ancillaryValue(rule),
  6357. $field = module.get.field(field.identifier),
  6358. value = $field.val(),
  6359. prompt = $.isFunction(rule.prompt)
  6360. ? rule.prompt(value)
  6361. : rule.prompt || settings.prompt[ruleName] || settings.text.unspecifiedRule,
  6362. requiresValue = (prompt.search('{value}') !== -1),
  6363. requiresName = (prompt.search('{name}') !== -1),
  6364. $label,
  6365. name
  6366. ;
  6367. if(requiresValue) {
  6368. prompt = prompt.replace(/\{value\}/g, $field.val());
  6369. }
  6370. if(requiresName) {
  6371. $label = $field.closest(selector.group).find('label').eq(0);
  6372. name = ($label.length == 1)
  6373. ? $label.text()
  6374. : $field.prop('placeholder') || settings.text.unspecifiedField
  6375. ;
  6376. prompt = prompt.replace(/\{name\}/g, name);
  6377. }
  6378. prompt = prompt.replace(/\{identifier\}/g, field.identifier);
  6379. prompt = prompt.replace(/\{ruleValue\}/g, ancillary);
  6380. if(!rule.prompt) {
  6381. module.verbose('Using default validation prompt for type', prompt, ruleName);
  6382. }
  6383. return prompt;
  6384. },
  6385. settings: function() {
  6386. if($.isPlainObject(parameters)) {
  6387. var
  6388. keys = Object.keys(parameters),
  6389. isLegacySettings = (keys.length > 0)
  6390. ? (parameters[keys[0]].identifier !== undefined && parameters[keys[0]].rules !== undefined)
  6391. : false
  6392. ;
  6393. if(isLegacySettings) {
  6394. // 1.x (ducktyped)
  6395. settings = $.extend(true, {}, $.fn.form.settings, legacyParameters);
  6396. validation = $.extend({}, $.fn.form.settings.defaults, parameters);
  6397. module.error(settings.error.oldSyntax, element);
  6398. module.verbose('Extending settings from legacy parameters', validation, settings);
  6399. }
  6400. else {
  6401. // 2.x
  6402. if(parameters.fields && module.is.shorthandFields(parameters.fields)) {
  6403. parameters.fields = module.get.fieldsFromShorthand(parameters.fields);
  6404. }
  6405. settings = $.extend(true, {}, $.fn.form.settings, parameters);
  6406. validation = $.extend({}, $.fn.form.settings.defaults, settings.fields);
  6407. module.verbose('Extending settings', validation, settings);
  6408. }
  6409. }
  6410. else {
  6411. settings = $.fn.form.settings;
  6412. validation = $.fn.form.settings.defaults;
  6413. module.verbose('Using default form validation', validation, settings);
  6414. }
  6415. // shorthand
  6416. namespace = settings.namespace;
  6417. metadata = settings.metadata;
  6418. selector = settings.selector;
  6419. className = settings.className;
  6420. regExp = settings.regExp;
  6421. error = settings.error;
  6422. moduleNamespace = 'module-' + namespace;
  6423. eventNamespace = '.' + namespace;
  6424. // grab instance
  6425. instance = $module.data(moduleNamespace);
  6426. // refresh selector cache
  6427. module.refresh();
  6428. },
  6429. field: function(identifier) {
  6430. module.verbose('Finding field with identifier', identifier);
  6431. identifier = module.escape.string(identifier);
  6432. var t;
  6433. if((t=$field.filter('#' + identifier)).length > 0 ) {
  6434. return t;
  6435. }
  6436. if((t=$field.filter('[name="' + identifier +'"]')).length > 0 ) {
  6437. return t;
  6438. }
  6439. if((t=$field.filter('[name="' + identifier +'[]"]')).length > 0 ) {
  6440. return t;
  6441. }
  6442. if((t=$field.filter('[data-' + metadata.validate + '="'+ identifier +'"]')).length > 0 ) {
  6443. return t;
  6444. }
  6445. return $('<input/>');
  6446. },
  6447. fields: function(fields) {
  6448. var
  6449. $fields = $()
  6450. ;
  6451. $.each(fields, function(index, name) {
  6452. $fields = $fields.add( module.get.field(name) );
  6453. });
  6454. return $fields;
  6455. },
  6456. validation: function($field) {
  6457. var
  6458. fieldValidation,
  6459. identifier
  6460. ;
  6461. if(!validation) {
  6462. return false;
  6463. }
  6464. $.each(validation, function(fieldName, field) {
  6465. identifier = field.identifier || fieldName;
  6466. $.each(module.get.field(identifier), function(index, groupField) {
  6467. if(groupField == $field[0]) {
  6468. field.identifier = identifier;
  6469. fieldValidation = field;
  6470. return false;
  6471. }
  6472. });
  6473. });
  6474. return fieldValidation || false;
  6475. },
  6476. value: function (field) {
  6477. var
  6478. fields = [],
  6479. results
  6480. ;
  6481. fields.push(field);
  6482. results = module.get.values.call(element, fields);
  6483. return results[field];
  6484. },
  6485. values: function (fields) {
  6486. var
  6487. $fields = Array.isArray(fields)
  6488. ? module.get.fields(fields)
  6489. : $field,
  6490. values = {}
  6491. ;
  6492. $fields.each(function(index, field) {
  6493. var
  6494. $field = $(field),
  6495. $calendar = $field.closest(selector.uiCalendar),
  6496. name = $field.prop('name'),
  6497. value = $field.val(),
  6498. isCheckbox = $field.is(selector.checkbox),
  6499. isRadio = $field.is(selector.radio),
  6500. isMultiple = (name.indexOf('[]') !== -1),
  6501. isCalendar = ($calendar.length > 0 && module.can.useElement('calendar')),
  6502. isChecked = (isCheckbox)
  6503. ? $field.is(':checked')
  6504. : false
  6505. ;
  6506. if(name) {
  6507. if(isMultiple) {
  6508. name = name.replace('[]', '');
  6509. if(!values[name]) {
  6510. values[name] = [];
  6511. }
  6512. if(isCheckbox) {
  6513. if(isChecked) {
  6514. values[name].push(value || true);
  6515. }
  6516. else {
  6517. values[name].push(false);
  6518. }
  6519. }
  6520. else {
  6521. values[name].push(value);
  6522. }
  6523. }
  6524. else {
  6525. if(isRadio) {
  6526. if(values[name] === undefined || values[name] === false) {
  6527. values[name] = (isChecked)
  6528. ? value || true
  6529. : false
  6530. ;
  6531. }
  6532. }
  6533. else if(isCheckbox) {
  6534. if(isChecked) {
  6535. values[name] = value || true;
  6536. }
  6537. else {
  6538. values[name] = false;
  6539. }
  6540. }
  6541. else if(isCalendar) {
  6542. var date = $calendar.calendar('get date');
  6543. if (date !== null) {
  6544. if (settings.dateHandling == 'date') {
  6545. values[name] = date;
  6546. } else if(settings.dateHandling == 'input') {
  6547. values[name] = $calendar.calendar('get input date')
  6548. } else if (settings.dateHandling == 'formatter') {
  6549. var type = $calendar.calendar('setting', 'type');
  6550. switch(type) {
  6551. case 'date':
  6552. values[name] = settings.formatter.date(date);
  6553. break;
  6554. case 'datetime':
  6555. values[name] = settings.formatter.datetime(date);
  6556. break;
  6557. case 'time':
  6558. values[name] = settings.formatter.time(date);
  6559. break;
  6560. case 'month':
  6561. values[name] = settings.formatter.month(date);
  6562. break;
  6563. case 'year':
  6564. values[name] = settings.formatter.year(date);
  6565. break;
  6566. default:
  6567. module.debug('Wrong calendar mode', $calendar, type);
  6568. values[name] = '';
  6569. }
  6570. }
  6571. } else {
  6572. values[name] = '';
  6573. }
  6574. } else {
  6575. values[name] = value;
  6576. }
  6577. }
  6578. }
  6579. });
  6580. return values;
  6581. },
  6582. dirtyFields: function() {
  6583. return $field.filter(function(index, e) {
  6584. return $(e).data(metadata.isDirty);
  6585. });
  6586. }
  6587. },
  6588. has: {
  6589. field: function(identifier) {
  6590. module.verbose('Checking for existence of a field with identifier', identifier);
  6591. identifier = module.escape.string(identifier);
  6592. if(typeof identifier !== 'string') {
  6593. module.error(error.identifier, identifier);
  6594. }
  6595. if($field.filter('#' + identifier).length > 0 ) {
  6596. return true;
  6597. }
  6598. else if( $field.filter('[name="' + identifier +'"]').length > 0 ) {
  6599. return true;
  6600. }
  6601. else if( $field.filter('[data-' + metadata.validate + '="'+ identifier +'"]').length > 0 ) {
  6602. return true;
  6603. }
  6604. return false;
  6605. }
  6606. },
  6607. can: {
  6608. useElement: function(element){
  6609. if ($.fn[element] !== undefined) {
  6610. return true;
  6611. }
  6612. module.error(error.noElement.replace('{element}',element));
  6613. return false;
  6614. }
  6615. },
  6616. escape: {
  6617. string: function(text) {
  6618. text = String(text);
  6619. return text.replace(regExp.escape, '\\$&');
  6620. }
  6621. },
  6622. add: {
  6623. // alias
  6624. rule: function(name, rules) {
  6625. module.add.field(name, rules);
  6626. },
  6627. field: function(name, rules) {
  6628. // Validation should have at least a standard format
  6629. if(validation[name] === undefined || validation[name].rules === undefined) {
  6630. validation[name] = {
  6631. rules: []
  6632. };
  6633. }
  6634. var
  6635. newValidation = {
  6636. rules: []
  6637. }
  6638. ;
  6639. if(module.is.shorthandRules(rules)) {
  6640. rules = Array.isArray(rules)
  6641. ? rules
  6642. : [rules]
  6643. ;
  6644. $.each(rules, function(_index, rule) {
  6645. newValidation.rules.push({ type: rule });
  6646. });
  6647. }
  6648. else {
  6649. newValidation.rules = rules.rules;
  6650. }
  6651. // For each new rule, check if there's not already one with the same type
  6652. $.each(newValidation.rules, function (_index, rule) {
  6653. if ($.grep(validation[name].rules, function(item){ return item.type == rule.type; }).length == 0) {
  6654. validation[name].rules.push(rule);
  6655. }
  6656. });
  6657. module.debug('Adding rules', newValidation.rules, validation);
  6658. },
  6659. fields: function(fields) {
  6660. var
  6661. newValidation
  6662. ;
  6663. if(fields && module.is.shorthandFields(fields)) {
  6664. newValidation = module.get.fieldsFromShorthand(fields);
  6665. }
  6666. else {
  6667. newValidation = fields;
  6668. }
  6669. validation = $.extend({}, validation, newValidation);
  6670. },
  6671. prompt: function(identifier, errors, internal) {
  6672. var
  6673. $field = module.get.field(identifier),
  6674. $fieldGroup = $field.closest($group),
  6675. $prompt = $fieldGroup.children(selector.prompt),
  6676. promptExists = ($prompt.length !== 0)
  6677. ;
  6678. errors = (typeof errors == 'string')
  6679. ? [errors]
  6680. : errors
  6681. ;
  6682. module.verbose('Adding field error state', identifier);
  6683. if(!internal) {
  6684. $fieldGroup
  6685. .addClass(className.error)
  6686. ;
  6687. }
  6688. if(settings.inline) {
  6689. if(!promptExists) {
  6690. $prompt = settings.templates.prompt(errors, className.label);
  6691. $prompt
  6692. .appendTo($fieldGroup)
  6693. ;
  6694. }
  6695. $prompt
  6696. .html(errors[0])
  6697. ;
  6698. if(!promptExists) {
  6699. if(settings.transition && module.can.useElement('transition') && $module.transition('is supported')) {
  6700. module.verbose('Displaying error with css transition', settings.transition);
  6701. $prompt.transition(settings.transition + ' in', settings.duration);
  6702. }
  6703. else {
  6704. module.verbose('Displaying error with fallback javascript animation');
  6705. $prompt
  6706. .fadeIn(settings.duration)
  6707. ;
  6708. }
  6709. }
  6710. else {
  6711. module.verbose('Inline errors are disabled, no inline error added', identifier);
  6712. }
  6713. }
  6714. },
  6715. errors: function(errors) {
  6716. module.debug('Adding form error messages', errors);
  6717. module.set.error();
  6718. $message
  6719. .html( settings.templates.error(errors) )
  6720. ;
  6721. }
  6722. },
  6723. remove: {
  6724. errors: function() {
  6725. module.debug('Removing form error messages');
  6726. $message.empty();
  6727. },
  6728. states: function() {
  6729. $module.removeClass(className.error).removeClass(className.success);
  6730. if(!settings.inline) {
  6731. module.remove.errors();
  6732. }
  6733. module.determine.isDirty();
  6734. },
  6735. rule: function(field, rule) {
  6736. var
  6737. rules = Array.isArray(rule)
  6738. ? rule
  6739. : [rule]
  6740. ;
  6741. if(validation[field] === undefined || !Array.isArray(validation[field].rules)) {
  6742. return;
  6743. }
  6744. if(rule === undefined) {
  6745. module.debug('Removed all rules');
  6746. validation[field].rules = [];
  6747. return;
  6748. }
  6749. $.each(validation[field].rules, function(index, rule) {
  6750. if(rule && rules.indexOf(rule.type) !== -1) {
  6751. module.debug('Removed rule', rule.type);
  6752. validation[field].rules.splice(index, 1);
  6753. }
  6754. });
  6755. },
  6756. field: function(field) {
  6757. var
  6758. fields = Array.isArray(field)
  6759. ? field
  6760. : [field]
  6761. ;
  6762. $.each(fields, function(index, field) {
  6763. module.remove.rule(field);
  6764. });
  6765. },
  6766. // alias
  6767. rules: function(field, rules) {
  6768. if(Array.isArray(field)) {
  6769. $.each(field, function(index, field) {
  6770. module.remove.rule(field, rules);
  6771. });
  6772. }
  6773. else {
  6774. module.remove.rule(field, rules);
  6775. }
  6776. },
  6777. fields: function(fields) {
  6778. module.remove.field(fields);
  6779. },
  6780. prompt: function(identifier) {
  6781. var
  6782. $field = module.get.field(identifier),
  6783. $fieldGroup = $field.closest($group),
  6784. $prompt = $fieldGroup.children(selector.prompt)
  6785. ;
  6786. $fieldGroup
  6787. .removeClass(className.error)
  6788. ;
  6789. if(settings.inline && $prompt.is(':visible')) {
  6790. module.verbose('Removing prompt for field', identifier);
  6791. if(settings.transition && module.can.useElement('transition') && $module.transition('is supported')) {
  6792. $prompt.transition(settings.transition + ' out', settings.duration, function() {
  6793. $prompt.remove();
  6794. });
  6795. }
  6796. else {
  6797. $prompt
  6798. .fadeOut(settings.duration, function(){
  6799. $prompt.remove();
  6800. })
  6801. ;
  6802. }
  6803. }
  6804. }
  6805. },
  6806. set: {
  6807. success: function() {
  6808. $module
  6809. .removeClass(className.error)
  6810. .addClass(className.success)
  6811. ;
  6812. },
  6813. defaults: function () {
  6814. $field.each(function (index, el) {
  6815. var
  6816. $el = $(el),
  6817. $parent = $el.parent(),
  6818. isCheckbox = ($el.filter(selector.checkbox).length > 0),
  6819. isDropdown = $parent.is(selector.uiDropdown) && module.can.useElement('dropdown'),
  6820. $calendar = $el.closest(selector.uiCalendar),
  6821. isCalendar = ($calendar.length > 0 && module.can.useElement('calendar')),
  6822. value = (isCheckbox)
  6823. ? $el.is(':checked')
  6824. : $el.val()
  6825. ;
  6826. if (isDropdown) {
  6827. $parent.dropdown('save defaults');
  6828. }
  6829. else if (isCalendar) {
  6830. $calendar.calendar('refresh');
  6831. }
  6832. $el.data(metadata.defaultValue, value);
  6833. $el.data(metadata.isDirty, false);
  6834. });
  6835. },
  6836. error: function() {
  6837. $module
  6838. .removeClass(className.success)
  6839. .addClass(className.error)
  6840. ;
  6841. },
  6842. value: function (field, value) {
  6843. var
  6844. fields = {}
  6845. ;
  6846. fields[field] = value;
  6847. return module.set.values.call(element, fields);
  6848. },
  6849. values: function (fields) {
  6850. if($.isEmptyObject(fields)) {
  6851. return;
  6852. }
  6853. $.each(fields, function(key, value) {
  6854. var
  6855. $field = module.get.field(key),
  6856. $element = $field.parent(),
  6857. $calendar = $field.closest(selector.uiCalendar),
  6858. isMultiple = Array.isArray(value),
  6859. isCheckbox = $element.is(selector.uiCheckbox) && module.can.useElement('checkbox'),
  6860. isDropdown = $element.is(selector.uiDropdown) && module.can.useElement('dropdown'),
  6861. isRadio = ($field.is(selector.radio) && isCheckbox),
  6862. isCalendar = ($calendar.length > 0 && module.can.useElement('calendar')),
  6863. fieldExists = ($field.length > 0),
  6864. $multipleField
  6865. ;
  6866. if(fieldExists) {
  6867. if(isMultiple && isCheckbox) {
  6868. module.verbose('Selecting multiple', value, $field);
  6869. $element.checkbox('uncheck');
  6870. $.each(value, function(index, value) {
  6871. $multipleField = $field.filter('[value="' + value + '"]');
  6872. $element = $multipleField.parent();
  6873. if($multipleField.length > 0) {
  6874. $element.checkbox('check');
  6875. }
  6876. });
  6877. }
  6878. else if(isRadio) {
  6879. module.verbose('Selecting radio value', value, $field);
  6880. $field.filter('[value="' + value + '"]')
  6881. .parent(selector.uiCheckbox)
  6882. .checkbox('check')
  6883. ;
  6884. }
  6885. else if(isCheckbox) {
  6886. module.verbose('Setting checkbox value', value, $element);
  6887. if(value === true || value === 1) {
  6888. $element.checkbox('check');
  6889. }
  6890. else {
  6891. $element.checkbox('uncheck');
  6892. }
  6893. }
  6894. else if(isDropdown) {
  6895. module.verbose('Setting dropdown value', value, $element);
  6896. $element.dropdown('set selected', value);
  6897. }
  6898. else if (isCalendar) {
  6899. $calendar.calendar('set date',value);
  6900. }
  6901. else {
  6902. module.verbose('Setting field value', value, $field);
  6903. $field.val(value);
  6904. }
  6905. }
  6906. });
  6907. },
  6908. dirty: function() {
  6909. module.verbose('Setting state dirty');
  6910. dirty = true;
  6911. history[0] = history[1];
  6912. history[1] = 'dirty';
  6913. if (module.is.justClean()) {
  6914. $module.trigger('dirty');
  6915. }
  6916. },
  6917. clean: function() {
  6918. module.verbose('Setting state clean');
  6919. dirty = false;
  6920. history[0] = history[1];
  6921. history[1] = 'clean';
  6922. if (module.is.justDirty()) {
  6923. $module.trigger('clean');
  6924. }
  6925. },
  6926. asClean: function() {
  6927. module.set.defaults();
  6928. module.set.clean();
  6929. },
  6930. asDirty: function() {
  6931. module.set.defaults();
  6932. module.set.dirty();
  6933. },
  6934. autoCheck: function() {
  6935. module.debug('Enabling auto check on required fields');
  6936. $field.each(function (_index, el) {
  6937. var
  6938. $el = $(el),
  6939. $elGroup = $(el).closest($group),
  6940. isCheckbox = ($el.filter(selector.checkbox).length > 0),
  6941. isRequired = $el.prop('required') || $elGroup.hasClass(className.required) || $elGroup.parent().hasClass(className.required),
  6942. isDisabled = $el.is(':disabled') || $elGroup.hasClass(className.disabled) || $elGroup.parent().hasClass(className.disabled),
  6943. validation = module.get.validation($el),
  6944. hasEmptyRule = validation
  6945. ? $.grep(validation.rules, function(rule) { return rule.type == "empty" }) !== 0
  6946. : false,
  6947. identifier = validation.identifier || $el.attr('id') || $el.attr('name') || $el.data(metadata.validate)
  6948. ;
  6949. if (isRequired && !isDisabled && !hasEmptyRule && identifier !== undefined) {
  6950. if (isCheckbox) {
  6951. module.verbose("Adding 'checked' rule on field", identifier);
  6952. module.add.rule(identifier, "checked");
  6953. } else {
  6954. module.verbose("Adding 'empty' rule on field", identifier);
  6955. module.add.rule(identifier, "empty");
  6956. }
  6957. }
  6958. });
  6959. }
  6960. },
  6961. validate: {
  6962. form: function(event, ignoreCallbacks) {
  6963. var values = module.get.values();
  6964. // input keydown event will fire submit repeatedly by browser default
  6965. if(keyHeldDown) {
  6966. return false;
  6967. }
  6968. // reset errors
  6969. formErrors = [];
  6970. if( module.determine.isValid() ) {
  6971. module.debug('Form has no validation errors, submitting');
  6972. module.set.success();
  6973. if(!settings.inline) {
  6974. module.remove.errors();
  6975. }
  6976. if(ignoreCallbacks !== true) {
  6977. return settings.onSuccess.call(element, event, values);
  6978. }
  6979. }
  6980. else {
  6981. module.debug('Form has errors');
  6982. submitting = false;
  6983. module.set.error();
  6984. if(!settings.inline) {
  6985. module.add.errors(formErrors);
  6986. }
  6987. // prevent ajax submit
  6988. if(event && $module.data('moduleApi') !== undefined) {
  6989. event.stopImmediatePropagation();
  6990. }
  6991. if(ignoreCallbacks !== true) {
  6992. return settings.onFailure.call(element, formErrors, values);
  6993. }
  6994. }
  6995. },
  6996. // takes a validation object and returns whether field passes validation
  6997. field: function(field, fieldName, showErrors) {
  6998. showErrors = (showErrors !== undefined)
  6999. ? showErrors
  7000. : true
  7001. ;
  7002. if(typeof field == 'string') {
  7003. module.verbose('Validating field', field);
  7004. fieldName = field;
  7005. field = validation[field];
  7006. }
  7007. var
  7008. identifier = field.identifier || fieldName,
  7009. $field = module.get.field(identifier),
  7010. $dependsField = (field.depends)
  7011. ? module.get.field(field.depends)
  7012. : false,
  7013. fieldValid = true,
  7014. fieldErrors = []
  7015. ;
  7016. if(!field.identifier) {
  7017. module.debug('Using field name as identifier', identifier);
  7018. field.identifier = identifier;
  7019. }
  7020. var isDisabled = !$field.filter(':not(:disabled)').length;
  7021. if(isDisabled) {
  7022. module.debug('Field is disabled. Skipping', identifier);
  7023. }
  7024. else if(field.optional && module.is.blank($field)){
  7025. module.debug('Field is optional and blank. Skipping', identifier);
  7026. }
  7027. else if(field.depends && module.is.empty($dependsField)) {
  7028. module.debug('Field depends on another value that is not present or empty. Skipping', $dependsField);
  7029. }
  7030. else if(field.rules !== undefined) {
  7031. if(showErrors) {
  7032. $field.closest($group).removeClass(className.error);
  7033. }
  7034. $.each(field.rules, function(index, rule) {
  7035. if( module.has.field(identifier)) {
  7036. var invalidFields = module.validate.rule(field, rule,true) || [];
  7037. if (invalidFields.length>0){
  7038. module.debug('Field is invalid', identifier, rule.type);
  7039. fieldErrors.push(module.get.prompt(rule, field));
  7040. fieldValid = false;
  7041. if(showErrors){
  7042. $(invalidFields).closest($group).addClass(className.error);
  7043. }
  7044. }
  7045. }
  7046. });
  7047. }
  7048. if(fieldValid) {
  7049. if(showErrors) {
  7050. module.remove.prompt(identifier, fieldErrors);
  7051. settings.onValid.call($field);
  7052. }
  7053. }
  7054. else {
  7055. if(showErrors) {
  7056. formErrors = formErrors.concat(fieldErrors);
  7057. module.add.prompt(identifier, fieldErrors, true);
  7058. settings.onInvalid.call($field, fieldErrors);
  7059. }
  7060. return false;
  7061. }
  7062. return true;
  7063. },
  7064. // takes validation rule and returns whether field passes rule
  7065. rule: function(field, rule, internal) {
  7066. var
  7067. $field = module.get.field(field.identifier),
  7068. ancillary = module.get.ancillaryValue(rule),
  7069. ruleName = module.get.ruleName(rule),
  7070. ruleFunction = settings.rules[ruleName],
  7071. invalidFields = [],
  7072. isCheckbox = $field.is(selector.checkbox),
  7073. isValid = function(field){
  7074. var value = (isCheckbox ? $(field).filter(':checked').val() : $(field).val());
  7075. // cast to string avoiding encoding special values
  7076. value = (value === undefined || value === '' || value === null)
  7077. ? ''
  7078. : (settings.shouldTrim) ? String(value + '').trim() : String(value + '')
  7079. ;
  7080. return ruleFunction.call(field, value, ancillary, $module);
  7081. }
  7082. ;
  7083. if( !$.isFunction(ruleFunction) ) {
  7084. module.error(error.noRule, ruleName);
  7085. return;
  7086. }
  7087. if(isCheckbox) {
  7088. if (!isValid($field)) {
  7089. invalidFields = $field;
  7090. }
  7091. } else {
  7092. $.each($field, function (index, field) {
  7093. if (!isValid(field)) {
  7094. invalidFields.push(field);
  7095. }
  7096. });
  7097. }
  7098. return internal ? invalidFields : !(invalidFields.length>0);
  7099. }
  7100. },
  7101. setting: function(name, value) {
  7102. if( $.isPlainObject(name) ) {
  7103. $.extend(true, settings, name);
  7104. }
  7105. else if(value !== undefined) {
  7106. settings[name] = value;
  7107. }
  7108. else {
  7109. return settings[name];
  7110. }
  7111. },
  7112. internal: function(name, value) {
  7113. if( $.isPlainObject(name) ) {
  7114. $.extend(true, module, name);
  7115. }
  7116. else if(value !== undefined) {
  7117. module[name] = value;
  7118. }
  7119. else {
  7120. return module[name];
  7121. }
  7122. },
  7123. debug: function() {
  7124. if(!settings.silent && settings.debug) {
  7125. if(settings.performance) {
  7126. module.performance.log(arguments);
  7127. }
  7128. else {
  7129. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  7130. module.debug.apply(console, arguments);
  7131. }
  7132. }
  7133. },
  7134. verbose: function() {
  7135. if(!settings.silent && settings.verbose && settings.debug) {
  7136. if(settings.performance) {
  7137. module.performance.log(arguments);
  7138. }
  7139. else {
  7140. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  7141. module.verbose.apply(console, arguments);
  7142. }
  7143. }
  7144. },
  7145. error: function() {
  7146. if(!settings.silent) {
  7147. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  7148. module.error.apply(console, arguments);
  7149. }
  7150. },
  7151. performance: {
  7152. log: function(message) {
  7153. var
  7154. currentTime,
  7155. executionTime,
  7156. previousTime
  7157. ;
  7158. if(settings.performance) {
  7159. currentTime = new Date().getTime();
  7160. previousTime = time || currentTime;
  7161. executionTime = currentTime - previousTime;
  7162. time = currentTime;
  7163. performance.push({
  7164. 'Name' : message[0],
  7165. 'Arguments' : [].slice.call(message, 1) || '',
  7166. 'Element' : element,
  7167. 'Execution Time' : executionTime
  7168. });
  7169. }
  7170. clearTimeout(module.performance.timer);
  7171. module.performance.timer = setTimeout(module.performance.display, 500);
  7172. },
  7173. display: function() {
  7174. var
  7175. title = settings.name + ':',
  7176. totalTime = 0
  7177. ;
  7178. time = false;
  7179. clearTimeout(module.performance.timer);
  7180. $.each(performance, function(index, data) {
  7181. totalTime += data['Execution Time'];
  7182. });
  7183. title += ' ' + totalTime + 'ms';
  7184. if(moduleSelector) {
  7185. title += ' \'' + moduleSelector + '\'';
  7186. }
  7187. if($allModules.length > 1) {
  7188. title += ' ' + '(' + $allModules.length + ')';
  7189. }
  7190. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  7191. console.groupCollapsed(title);
  7192. if(console.table) {
  7193. console.table(performance);
  7194. }
  7195. else {
  7196. $.each(performance, function(index, data) {
  7197. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  7198. });
  7199. }
  7200. console.groupEnd();
  7201. }
  7202. performance = [];
  7203. }
  7204. },
  7205. invoke: function(query, passedArguments, context) {
  7206. var
  7207. object = instance,
  7208. maxDepth,
  7209. found,
  7210. response
  7211. ;
  7212. passedArguments = passedArguments || queryArguments;
  7213. context = element || context;
  7214. if(typeof query == 'string' && object !== undefined) {
  7215. query = query.split(/[\. ]/);
  7216. maxDepth = query.length - 1;
  7217. $.each(query, function(depth, value) {
  7218. var camelCaseValue = (depth != maxDepth)
  7219. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  7220. : query
  7221. ;
  7222. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  7223. object = object[camelCaseValue];
  7224. }
  7225. else if( object[camelCaseValue] !== undefined ) {
  7226. found = object[camelCaseValue];
  7227. return false;
  7228. }
  7229. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  7230. object = object[value];
  7231. }
  7232. else if( object[value] !== undefined ) {
  7233. found = object[value];
  7234. return false;
  7235. }
  7236. else {
  7237. return false;
  7238. }
  7239. });
  7240. }
  7241. if( $.isFunction( found ) ) {
  7242. response = found.apply(context, passedArguments);
  7243. }
  7244. else if(found !== undefined) {
  7245. response = found;
  7246. }
  7247. if(Array.isArray(returnedValue)) {
  7248. returnedValue.push(response);
  7249. }
  7250. else if(returnedValue !== undefined) {
  7251. returnedValue = [returnedValue, response];
  7252. }
  7253. else if(response !== undefined) {
  7254. returnedValue = response;
  7255. }
  7256. return found;
  7257. }
  7258. };
  7259. module.initialize();
  7260. })
  7261. ;
  7262. return (returnedValue !== undefined)
  7263. ? returnedValue
  7264. : this
  7265. ;
  7266. };
  7267. $.fn.form.settings = {
  7268. name : 'Form',
  7269. namespace : 'form',
  7270. debug : false,
  7271. verbose : false,
  7272. performance : true,
  7273. fields : false,
  7274. keyboardShortcuts : true,
  7275. on : 'submit',
  7276. inline : false,
  7277. delay : 200,
  7278. revalidate : true,
  7279. shouldTrim : true,
  7280. transition : 'scale',
  7281. duration : 200,
  7282. autoCheckRequired : false,
  7283. preventLeaving : false,
  7284. dateHandling : 'date', // 'date', 'input', 'formatter'
  7285. onValid : function() {},
  7286. onInvalid : function() {},
  7287. onSuccess : function() { return true; },
  7288. onFailure : function() { return false; },
  7289. onDirty : function() {},
  7290. onClean : function() {},
  7291. metadata : {
  7292. defaultValue : 'default',
  7293. validate : 'validate',
  7294. isDirty : 'isDirty'
  7295. },
  7296. regExp: {
  7297. htmlID : /^[a-zA-Z][\w:.-]*$/g,
  7298. bracket : /\[(.*)\]/i,
  7299. decimal : /^\d+\.?\d*$/,
  7300. email : /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i,
  7301. escape : /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|:,=@]/g,
  7302. flags : /^\/(.*)\/(.*)?/,
  7303. integer : /^\-?\d+$/,
  7304. number : /^\-?\d*(\.\d+)?$/,
  7305. url : /(https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,})/i
  7306. },
  7307. text: {
  7308. unspecifiedRule : 'Please enter a valid value',
  7309. unspecifiedField : 'This field',
  7310. leavingMessage : 'There are unsaved changes on this page which will be discarded if you continue.'
  7311. },
  7312. prompt: {
  7313. empty : '{name} must have a value',
  7314. checked : '{name} must be checked',
  7315. email : '{name} must be a valid e-mail',
  7316. url : '{name} must be a valid url',
  7317. regExp : '{name} is not formatted correctly',
  7318. integer : '{name} must be an integer',
  7319. decimal : '{name} must be a decimal number',
  7320. number : '{name} must be set to a number',
  7321. is : '{name} must be "{ruleValue}"',
  7322. isExactly : '{name} must be exactly "{ruleValue}"',
  7323. not : '{name} cannot be set to "{ruleValue}"',
  7324. notExactly : '{name} cannot be set to exactly "{ruleValue}"',
  7325. contain : '{name} must contain "{ruleValue}"',
  7326. containExactly : '{name} must contain exactly "{ruleValue}"',
  7327. doesntContain : '{name} cannot contain "{ruleValue}"',
  7328. doesntContainExactly : '{name} cannot contain exactly "{ruleValue}"',
  7329. minLength : '{name} must be at least {ruleValue} characters',
  7330. length : '{name} must be at least {ruleValue} characters',
  7331. exactLength : '{name} must be exactly {ruleValue} characters',
  7332. maxLength : '{name} cannot be longer than {ruleValue} characters',
  7333. match : '{name} must match {ruleValue} field',
  7334. different : '{name} must have a different value than {ruleValue} field',
  7335. creditCard : '{name} must be a valid credit card number',
  7336. minCount : '{name} must have at least {ruleValue} choices',
  7337. exactCount : '{name} must have exactly {ruleValue} choices',
  7338. maxCount : '{name} must have {ruleValue} or less choices'
  7339. },
  7340. selector : {
  7341. checkbox : 'input[type="checkbox"], input[type="radio"]',
  7342. clear : '.clear',
  7343. field : 'input:not(.search), textarea, select',
  7344. group : '.field',
  7345. input : 'input',
  7346. message : '.error.message',
  7347. prompt : '.prompt.label',
  7348. radio : 'input[type="radio"]',
  7349. reset : '.reset:not([type="reset"])',
  7350. submit : '.submit:not([type="submit"])',
  7351. uiCheckbox : '.ui.checkbox',
  7352. uiDropdown : '.ui.dropdown',
  7353. uiCalendar : '.ui.calendar'
  7354. },
  7355. className : {
  7356. error : 'error',
  7357. label : 'ui basic red pointing prompt label',
  7358. pressed : 'down',
  7359. success : 'success',
  7360. required : 'required',
  7361. disabled : 'disabled'
  7362. },
  7363. error: {
  7364. identifier : 'You must specify a string identifier for each field',
  7365. method : 'The method you called is not defined.',
  7366. noRule : 'There is no rule matching the one you specified',
  7367. oldSyntax : 'Starting in 2.0 forms now only take a single settings object. Validation settings converted to new syntax automatically.',
  7368. noElement : 'This module requires ui {element}'
  7369. },
  7370. templates: {
  7371. // template that produces error message
  7372. error: function(errors) {
  7373. var
  7374. html = '<ul class="list">'
  7375. ;
  7376. $.each(errors, function(index, value) {
  7377. html += '<li>' + value + '</li>';
  7378. });
  7379. html += '</ul>';
  7380. return $(html);
  7381. },
  7382. // template that produces label
  7383. prompt: function(errors, labelClasses) {
  7384. return $('<div/>')
  7385. .addClass(labelClasses)
  7386. .html(errors[0])
  7387. ;
  7388. }
  7389. },
  7390. formatter: {
  7391. date: function(date) {
  7392. return Intl.DateTimeFormat('en-GB').format(date);
  7393. },
  7394. datetime: function(date) {
  7395. return Intl.DateTimeFormat('en-GB', {
  7396. year: "numeric",
  7397. month: "2-digit",
  7398. day: "2-digit",
  7399. hour: '2-digit',
  7400. minute: '2-digit',
  7401. second: '2-digit'
  7402. }).format(date);
  7403. },
  7404. time: function(date) {
  7405. return Intl.DateTimeFormat('en-GB', {
  7406. hour: '2-digit',
  7407. minute: '2-digit',
  7408. second: '2-digit'
  7409. }).format(date);
  7410. },
  7411. month: function(date) {
  7412. return Intl.DateTimeFormat('en-GB', {
  7413. month: '2-digit',
  7414. year: 'numeric'
  7415. }).format(date);
  7416. },
  7417. year: function(date) {
  7418. return Intl.DateTimeFormat('en-GB', {
  7419. year: 'numeric'
  7420. }).format(date);
  7421. }
  7422. },
  7423. rules: {
  7424. // is not empty or blank string
  7425. empty: function(value) {
  7426. return !(value === undefined || '' === value || Array.isArray(value) && value.length === 0);
  7427. },
  7428. // checkbox checked
  7429. checked: function() {
  7430. return ($(this).filter(':checked').length > 0);
  7431. },
  7432. // is most likely an email
  7433. email: function(value){
  7434. return $.fn.form.settings.regExp.email.test(value);
  7435. },
  7436. // value is most likely url
  7437. url: function(value) {
  7438. return $.fn.form.settings.regExp.url.test(value);
  7439. },
  7440. // matches specified regExp
  7441. regExp: function(value, regExp) {
  7442. if(regExp instanceof RegExp) {
  7443. return value.match(regExp);
  7444. }
  7445. var
  7446. regExpParts = regExp.match($.fn.form.settings.regExp.flags),
  7447. flags
  7448. ;
  7449. // regular expression specified as /baz/gi (flags)
  7450. if(regExpParts) {
  7451. regExp = (regExpParts.length >= 2)
  7452. ? regExpParts[1]
  7453. : regExp
  7454. ;
  7455. flags = (regExpParts.length >= 3)
  7456. ? regExpParts[2]
  7457. : ''
  7458. ;
  7459. }
  7460. return value.match( new RegExp(regExp, flags) );
  7461. },
  7462. // is valid integer or matches range
  7463. integer: function(value, range) {
  7464. var
  7465. intRegExp = $.fn.form.settings.regExp.integer,
  7466. min,
  7467. max,
  7468. parts
  7469. ;
  7470. if( !range || ['', '..'].indexOf(range) !== -1) {
  7471. // do nothing
  7472. }
  7473. else if(range.indexOf('..') == -1) {
  7474. if(intRegExp.test(range)) {
  7475. min = max = range - 0;
  7476. }
  7477. }
  7478. else {
  7479. parts = range.split('..', 2);
  7480. if(intRegExp.test(parts[0])) {
  7481. min = parts[0] - 0;
  7482. }
  7483. if(intRegExp.test(parts[1])) {
  7484. max = parts[1] - 0;
  7485. }
  7486. }
  7487. return (
  7488. intRegExp.test(value) &&
  7489. (min === undefined || value >= min) &&
  7490. (max === undefined || value <= max)
  7491. );
  7492. },
  7493. // is valid number (with decimal)
  7494. decimal: function(value) {
  7495. return $.fn.form.settings.regExp.decimal.test(value);
  7496. },
  7497. // is valid number
  7498. number: function(value) {
  7499. return $.fn.form.settings.regExp.number.test(value);
  7500. },
  7501. // is value (case insensitive)
  7502. is: function(value, text) {
  7503. text = (typeof text == 'string')
  7504. ? text.toLowerCase()
  7505. : text
  7506. ;
  7507. value = (typeof value == 'string')
  7508. ? value.toLowerCase()
  7509. : value
  7510. ;
  7511. return (value == text);
  7512. },
  7513. // is value
  7514. isExactly: function(value, text) {
  7515. return (value == text);
  7516. },
  7517. // value is not another value (case insensitive)
  7518. not: function(value, notValue) {
  7519. value = (typeof value == 'string')
  7520. ? value.toLowerCase()
  7521. : value
  7522. ;
  7523. notValue = (typeof notValue == 'string')
  7524. ? notValue.toLowerCase()
  7525. : notValue
  7526. ;
  7527. return (value != notValue);
  7528. },
  7529. // value is not another value (case sensitive)
  7530. notExactly: function(value, notValue) {
  7531. return (value != notValue);
  7532. },
  7533. // value contains text (insensitive)
  7534. contains: function(value, text) {
  7535. // escape regex characters
  7536. text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
  7537. return (value.search( new RegExp(text, 'i') ) !== -1);
  7538. },
  7539. // value contains text (case sensitive)
  7540. containsExactly: function(value, text) {
  7541. // escape regex characters
  7542. text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
  7543. return (value.search( new RegExp(text) ) !== -1);
  7544. },
  7545. // value contains text (insensitive)
  7546. doesntContain: function(value, text) {
  7547. // escape regex characters
  7548. text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
  7549. return (value.search( new RegExp(text, 'i') ) === -1);
  7550. },
  7551. // value contains text (case sensitive)
  7552. doesntContainExactly: function(value, text) {
  7553. // escape regex characters
  7554. text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
  7555. return (value.search( new RegExp(text) ) === -1);
  7556. },
  7557. // is at least string length
  7558. minLength: function(value, requiredLength) {
  7559. return (value !== undefined)
  7560. ? (value.length >= requiredLength)
  7561. : false
  7562. ;
  7563. },
  7564. // see rls notes for 2.0.6 (this is a duplicate of minLength)
  7565. length: function(value, requiredLength) {
  7566. return (value !== undefined)
  7567. ? (value.length >= requiredLength)
  7568. : false
  7569. ;
  7570. },
  7571. // is exactly length
  7572. exactLength: function(value, requiredLength) {
  7573. return (value !== undefined)
  7574. ? (value.length == requiredLength)
  7575. : false
  7576. ;
  7577. },
  7578. // is less than length
  7579. maxLength: function(value, maxLength) {
  7580. return (value !== undefined)
  7581. ? (value.length <= maxLength)
  7582. : false
  7583. ;
  7584. },
  7585. // matches another field
  7586. match: function(value, identifier, $module) {
  7587. var
  7588. matchingValue,
  7589. matchingElement
  7590. ;
  7591. if((matchingElement = $module.find('[data-validate="'+ identifier +'"]')).length > 0 ) {
  7592. matchingValue = matchingElement.val();
  7593. }
  7594. else if((matchingElement = $module.find('#' + identifier)).length > 0) {
  7595. matchingValue = matchingElement.val();
  7596. }
  7597. else if((matchingElement = $module.find('[name="' + identifier +'"]')).length > 0) {
  7598. matchingValue = matchingElement.val();
  7599. }
  7600. else if((matchingElement = $module.find('[name="' + identifier +'[]"]')).length > 0 ) {
  7601. matchingValue = matchingElement;
  7602. }
  7603. return (matchingValue !== undefined)
  7604. ? ( value.toString() == matchingValue.toString() )
  7605. : false
  7606. ;
  7607. },
  7608. // different than another field
  7609. different: function(value, identifier, $module) {
  7610. // use either id or name of field
  7611. var
  7612. matchingValue,
  7613. matchingElement
  7614. ;
  7615. if((matchingElement = $module.find('[data-validate="'+ identifier +'"]')).length > 0 ) {
  7616. matchingValue = matchingElement.val();
  7617. }
  7618. else if((matchingElement = $module.find('#' + identifier)).length > 0) {
  7619. matchingValue = matchingElement.val();
  7620. }
  7621. else if((matchingElement = $module.find('[name="' + identifier +'"]')).length > 0) {
  7622. matchingValue = matchingElement.val();
  7623. }
  7624. else if((matchingElement = $module.find('[name="' + identifier +'[]"]')).length > 0 ) {
  7625. matchingValue = matchingElement;
  7626. }
  7627. return (matchingValue !== undefined)
  7628. ? ( value.toString() !== matchingValue.toString() )
  7629. : false
  7630. ;
  7631. },
  7632. creditCard: function(cardNumber, cardTypes) {
  7633. var
  7634. cards = {
  7635. visa: {
  7636. pattern : /^4/,
  7637. length : [16]
  7638. },
  7639. amex: {
  7640. pattern : /^3[47]/,
  7641. length : [15]
  7642. },
  7643. mastercard: {
  7644. pattern : /^5[1-5]/,
  7645. length : [16]
  7646. },
  7647. discover: {
  7648. pattern : /^(6011|622(12[6-9]|1[3-9][0-9]|[2-8][0-9]{2}|9[0-1][0-9]|92[0-5]|64[4-9])|65)/,
  7649. length : [16]
  7650. },
  7651. unionPay: {
  7652. pattern : /^(62|88)/,
  7653. length : [16, 17, 18, 19]
  7654. },
  7655. jcb: {
  7656. pattern : /^35(2[89]|[3-8][0-9])/,
  7657. length : [16]
  7658. },
  7659. maestro: {
  7660. pattern : /^(5018|5020|5038|6304|6759|676[1-3])/,
  7661. length : [12, 13, 14, 15, 16, 17, 18, 19]
  7662. },
  7663. dinersClub: {
  7664. pattern : /^(30[0-5]|^36)/,
  7665. length : [14]
  7666. },
  7667. laser: {
  7668. pattern : /^(6304|670[69]|6771)/,
  7669. length : [16, 17, 18, 19]
  7670. },
  7671. visaElectron: {
  7672. pattern : /^(4026|417500|4508|4844|491(3|7))/,
  7673. length : [16]
  7674. }
  7675. },
  7676. valid = {},
  7677. validCard = false,
  7678. requiredTypes = (typeof cardTypes == 'string')
  7679. ? cardTypes.split(',')
  7680. : false,
  7681. unionPay,
  7682. validation
  7683. ;
  7684. if(typeof cardNumber !== 'string' || cardNumber.length === 0) {
  7685. return;
  7686. }
  7687. // allow dashes in card
  7688. cardNumber = cardNumber.replace(/[\-]/g, '');
  7689. // verify card types
  7690. if(requiredTypes) {
  7691. $.each(requiredTypes, function(index, type){
  7692. // verify each card type
  7693. validation = cards[type];
  7694. if(validation) {
  7695. valid = {
  7696. length : ($.inArray(cardNumber.length, validation.length) !== -1),
  7697. pattern : (cardNumber.search(validation.pattern) !== -1)
  7698. };
  7699. if(valid.length && valid.pattern) {
  7700. validCard = true;
  7701. }
  7702. }
  7703. });
  7704. if(!validCard) {
  7705. return false;
  7706. }
  7707. }
  7708. // skip luhn for UnionPay
  7709. unionPay = {
  7710. number : ($.inArray(cardNumber.length, cards.unionPay.length) !== -1),
  7711. pattern : (cardNumber.search(cards.unionPay.pattern) !== -1)
  7712. };
  7713. if(unionPay.number && unionPay.pattern) {
  7714. return true;
  7715. }
  7716. // verify luhn, adapted from <https://gist.github.com/2134376>
  7717. var
  7718. length = cardNumber.length,
  7719. multiple = 0,
  7720. producedValue = [
  7721. [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
  7722. [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
  7723. ],
  7724. sum = 0
  7725. ;
  7726. while (length--) {
  7727. sum += producedValue[multiple][parseInt(cardNumber.charAt(length), 10)];
  7728. multiple ^= 1;
  7729. }
  7730. return (sum % 10 === 0 && sum > 0);
  7731. },
  7732. minCount: function(value, minCount) {
  7733. if(minCount == 0) {
  7734. return true;
  7735. }
  7736. if(minCount == 1) {
  7737. return (value !== '');
  7738. }
  7739. return (value.split(',').length >= minCount);
  7740. },
  7741. exactCount: function(value, exactCount) {
  7742. if(exactCount == 0) {
  7743. return (value === '');
  7744. }
  7745. if(exactCount == 1) {
  7746. return (value !== '' && value.search(',') === -1);
  7747. }
  7748. return (value.split(',').length == exactCount);
  7749. },
  7750. maxCount: function(value, maxCount) {
  7751. if(maxCount == 0) {
  7752. return false;
  7753. }
  7754. if(maxCount == 1) {
  7755. return (value.search(',') === -1);
  7756. }
  7757. return (value.split(',').length <= maxCount);
  7758. }
  7759. }
  7760. };
  7761. })( jQuery, window, document );
  7762. /*!
  7763. * # Fomantic-UI - Modal
  7764. * http://github.com/fomantic/Fomantic-UI/
  7765. *
  7766. *
  7767. * Released under the MIT license
  7768. * http://opensource.org/licenses/MIT
  7769. *
  7770. */
  7771. ;(function ($, window, document, undefined) {
  7772. 'use strict';
  7773. $.isFunction = $.isFunction || function(obj) {
  7774. return typeof obj === "function" && typeof obj.nodeType !== "number";
  7775. };
  7776. window = (typeof window != 'undefined' && window.Math == Math)
  7777. ? window
  7778. : (typeof self != 'undefined' && self.Math == Math)
  7779. ? self
  7780. : Function('return this')()
  7781. ;
  7782. $.fn.modal = function(parameters) {
  7783. var
  7784. $allModules = $(this),
  7785. $window = $(window),
  7786. $document = $(document),
  7787. $body = $('body'),
  7788. moduleSelector = $allModules.selector || '',
  7789. time = new Date().getTime(),
  7790. performance = [],
  7791. query = arguments[0],
  7792. methodInvoked = (typeof query == 'string'),
  7793. queryArguments = [].slice.call(arguments, 1),
  7794. requestAnimationFrame = window.requestAnimationFrame
  7795. || window.mozRequestAnimationFrame
  7796. || window.webkitRequestAnimationFrame
  7797. || window.msRequestAnimationFrame
  7798. || function(callback) { setTimeout(callback, 0); },
  7799. returnedValue
  7800. ;
  7801. $allModules
  7802. .each(function() {
  7803. var
  7804. settings = ( $.isPlainObject(parameters) )
  7805. ? $.extend(true, {}, $.fn.modal.settings, parameters)
  7806. : $.extend({}, $.fn.modal.settings),
  7807. selector = settings.selector,
  7808. className = settings.className,
  7809. namespace = settings.namespace,
  7810. error = settings.error,
  7811. eventNamespace = '.' + namespace,
  7812. moduleNamespace = 'module-' + namespace,
  7813. $module = $(this),
  7814. $context = $(settings.context),
  7815. $close = $module.find(selector.close),
  7816. $allModals,
  7817. $otherModals,
  7818. $focusedElement,
  7819. $dimmable,
  7820. $dimmer,
  7821. element = this,
  7822. instance = $module.data(moduleNamespace),
  7823. ignoreRepeatedEvents = false,
  7824. initialMouseDownInModal,
  7825. initialMouseDownInScrollbar,
  7826. initialBodyMargin = '',
  7827. tempBodyMargin = '',
  7828. elementEventNamespace,
  7829. id,
  7830. observer,
  7831. module
  7832. ;
  7833. module = {
  7834. initialize: function() {
  7835. module.cache = {};
  7836. module.verbose('Initializing dimmer', $context);
  7837. module.create.id();
  7838. module.create.dimmer();
  7839. if ( settings.allowMultiple ) {
  7840. module.create.innerDimmer();
  7841. }
  7842. if (!settings.centered){
  7843. $module.addClass('top aligned');
  7844. }
  7845. module.refreshModals();
  7846. module.bind.events();
  7847. if(settings.observeChanges) {
  7848. module.observeChanges();
  7849. }
  7850. module.instantiate();
  7851. },
  7852. instantiate: function() {
  7853. module.verbose('Storing instance of modal');
  7854. instance = module;
  7855. $module
  7856. .data(moduleNamespace, instance)
  7857. ;
  7858. },
  7859. create: {
  7860. dimmer: function() {
  7861. var
  7862. defaultSettings = {
  7863. debug : settings.debug,
  7864. dimmerName : 'modals'
  7865. },
  7866. dimmerSettings = $.extend(true, defaultSettings, settings.dimmerSettings)
  7867. ;
  7868. if($.fn.dimmer === undefined) {
  7869. module.error(error.dimmer);
  7870. return;
  7871. }
  7872. module.debug('Creating dimmer');
  7873. $dimmable = $context.dimmer(dimmerSettings);
  7874. if(settings.detachable) {
  7875. module.verbose('Modal is detachable, moving content into dimmer');
  7876. $dimmable.dimmer('add content', $module);
  7877. }
  7878. else {
  7879. module.set.undetached();
  7880. }
  7881. $dimmer = $dimmable.dimmer('get dimmer');
  7882. },
  7883. id: function() {
  7884. id = (Math.random().toString(16) + '000000000').substr(2, 8);
  7885. elementEventNamespace = '.' + id;
  7886. module.verbose('Creating unique id for element', id);
  7887. },
  7888. innerDimmer: function() {
  7889. if ( $module.find(selector.dimmer).length == 0 ) {
  7890. $module.prepend('<div class="ui inverted dimmer"></div>');
  7891. }
  7892. }
  7893. },
  7894. destroy: function() {
  7895. if (observer) {
  7896. observer.disconnect();
  7897. }
  7898. module.verbose('Destroying previous modal');
  7899. $module
  7900. .removeData(moduleNamespace)
  7901. .off(eventNamespace)
  7902. ;
  7903. $window.off(elementEventNamespace);
  7904. $dimmer.off(elementEventNamespace);
  7905. $close.off(eventNamespace);
  7906. $context.dimmer('destroy');
  7907. },
  7908. observeChanges: function() {
  7909. if('MutationObserver' in window) {
  7910. observer = new MutationObserver(function(mutations) {
  7911. module.debug('DOM tree modified, refreshing');
  7912. module.refresh();
  7913. });
  7914. observer.observe(element, {
  7915. childList : true,
  7916. subtree : true
  7917. });
  7918. module.debug('Setting up mutation observer', observer);
  7919. }
  7920. },
  7921. refresh: function() {
  7922. module.remove.scrolling();
  7923. module.cacheSizes();
  7924. if(!module.can.useFlex()) {
  7925. module.set.modalOffset();
  7926. }
  7927. module.set.screenHeight();
  7928. module.set.type();
  7929. },
  7930. refreshModals: function() {
  7931. $otherModals = $module.siblings(selector.modal);
  7932. $allModals = $otherModals.add($module);
  7933. },
  7934. attachEvents: function(selector, event) {
  7935. var
  7936. $toggle = $(selector)
  7937. ;
  7938. event = $.isFunction(module[event])
  7939. ? module[event]
  7940. : module.toggle
  7941. ;
  7942. if($toggle.length > 0) {
  7943. module.debug('Attaching modal events to element', selector, event);
  7944. $toggle
  7945. .off(eventNamespace)
  7946. .on('click' + eventNamespace, event)
  7947. ;
  7948. }
  7949. else {
  7950. module.error(error.notFound, selector);
  7951. }
  7952. },
  7953. bind: {
  7954. events: function() {
  7955. module.verbose('Attaching events');
  7956. $module
  7957. .on('click' + eventNamespace, selector.close, module.event.close)
  7958. .on('click' + eventNamespace, selector.approve, module.event.approve)
  7959. .on('click' + eventNamespace, selector.deny, module.event.deny)
  7960. ;
  7961. $window
  7962. .on('resize' + elementEventNamespace, module.event.resize)
  7963. ;
  7964. },
  7965. scrollLock: function() {
  7966. // touch events default to passive, due to changes in chrome to optimize mobile perf
  7967. $dimmable.get(0).addEventListener('touchmove', module.event.preventScroll, { passive: false });
  7968. }
  7969. },
  7970. unbind: {
  7971. scrollLock: function() {
  7972. $dimmable.get(0).removeEventListener('touchmove', module.event.preventScroll, { passive: false });
  7973. }
  7974. },
  7975. get: {
  7976. id: function() {
  7977. return (Math.random().toString(16) + '000000000').substr(2, 8);
  7978. }
  7979. },
  7980. event: {
  7981. approve: function() {
  7982. if(ignoreRepeatedEvents || settings.onApprove.call(element, $(this)) === false) {
  7983. module.verbose('Approve callback returned false cancelling hide');
  7984. return;
  7985. }
  7986. ignoreRepeatedEvents = true;
  7987. module.hide(function() {
  7988. ignoreRepeatedEvents = false;
  7989. });
  7990. },
  7991. preventScroll: function(event) {
  7992. if(event.target.className.indexOf('dimmer') !== -1) {
  7993. event.preventDefault();
  7994. }
  7995. },
  7996. deny: function() {
  7997. if(ignoreRepeatedEvents || settings.onDeny.call(element, $(this)) === false) {
  7998. module.verbose('Deny callback returned false cancelling hide');
  7999. return;
  8000. }
  8001. ignoreRepeatedEvents = true;
  8002. module.hide(function() {
  8003. ignoreRepeatedEvents = false;
  8004. });
  8005. },
  8006. close: function() {
  8007. module.hide();
  8008. },
  8009. mousedown: function(event) {
  8010. var
  8011. $target = $(event.target),
  8012. isRtl = module.is.rtl();
  8013. ;
  8014. initialMouseDownInModal = ($target.closest(selector.modal).length > 0);
  8015. if(initialMouseDownInModal) {
  8016. module.verbose('Mouse down event registered inside the modal');
  8017. }
  8018. initialMouseDownInScrollbar = module.is.scrolling() && ((!isRtl && $(window).outerWidth() - settings.scrollbarWidth <= event.clientX) || (isRtl && settings.scrollbarWidth >= event.clientX));
  8019. if(initialMouseDownInScrollbar) {
  8020. module.verbose('Mouse down event registered inside the scrollbar');
  8021. }
  8022. },
  8023. mouseup: function(event) {
  8024. if(!settings.closable) {
  8025. module.verbose('Dimmer clicked but closable setting is disabled');
  8026. return;
  8027. }
  8028. if(initialMouseDownInModal) {
  8029. module.debug('Dimmer clicked but mouse down was initially registered inside the modal');
  8030. return;
  8031. }
  8032. if(initialMouseDownInScrollbar){
  8033. module.debug('Dimmer clicked but mouse down was initially registered inside the scrollbar');
  8034. return;
  8035. }
  8036. var
  8037. $target = $(event.target),
  8038. isInModal = ($target.closest(selector.modal).length > 0),
  8039. isInDOM = $.contains(document.documentElement, event.target)
  8040. ;
  8041. if(!isInModal && isInDOM && module.is.active() && $module.hasClass(className.front) ) {
  8042. module.debug('Dimmer clicked, hiding all modals');
  8043. if(settings.allowMultiple) {
  8044. if(!module.hideAll()) {
  8045. return;
  8046. }
  8047. }
  8048. else if(!module.hide()){
  8049. return;
  8050. }
  8051. module.remove.clickaway();
  8052. }
  8053. },
  8054. debounce: function(method, delay) {
  8055. clearTimeout(module.timer);
  8056. module.timer = setTimeout(method, delay);
  8057. },
  8058. keyboard: function(event) {
  8059. var
  8060. keyCode = event.which,
  8061. escapeKey = 27
  8062. ;
  8063. if(keyCode == escapeKey) {
  8064. if(settings.closable) {
  8065. module.debug('Escape key pressed hiding modal');
  8066. if ( $module.hasClass(className.front) ) {
  8067. module.hide();
  8068. }
  8069. }
  8070. else {
  8071. module.debug('Escape key pressed, but closable is set to false');
  8072. }
  8073. event.preventDefault();
  8074. }
  8075. },
  8076. resize: function() {
  8077. if( $dimmable.dimmer('is active') && ( module.is.animating() || module.is.active() ) ) {
  8078. requestAnimationFrame(module.refresh);
  8079. }
  8080. }
  8081. },
  8082. toggle: function() {
  8083. if( module.is.active() || module.is.animating() ) {
  8084. module.hide();
  8085. }
  8086. else {
  8087. module.show();
  8088. }
  8089. },
  8090. show: function(callback) {
  8091. callback = $.isFunction(callback)
  8092. ? callback
  8093. : function(){}
  8094. ;
  8095. module.refreshModals();
  8096. module.set.dimmerSettings();
  8097. module.set.dimmerStyles();
  8098. module.showModal(callback);
  8099. },
  8100. hide: function(callback) {
  8101. callback = $.isFunction(callback)
  8102. ? callback
  8103. : function(){}
  8104. ;
  8105. module.refreshModals();
  8106. return module.hideModal(callback);
  8107. },
  8108. showModal: function(callback) {
  8109. callback = $.isFunction(callback)
  8110. ? callback
  8111. : function(){}
  8112. ;
  8113. if( module.is.animating() || !module.is.active() ) {
  8114. module.showDimmer();
  8115. module.cacheSizes();
  8116. module.set.bodyMargin();
  8117. if(module.can.useFlex()) {
  8118. module.remove.legacy();
  8119. }
  8120. else {
  8121. module.set.legacy();
  8122. module.set.modalOffset();
  8123. module.debug('Using non-flex legacy modal positioning.');
  8124. }
  8125. module.set.screenHeight();
  8126. module.set.type();
  8127. module.set.clickaway();
  8128. if( !settings.allowMultiple && module.others.active() ) {
  8129. module.hideOthers(module.showModal);
  8130. }
  8131. else {
  8132. ignoreRepeatedEvents = false;
  8133. if( settings.allowMultiple ) {
  8134. if ( module.others.active() ) {
  8135. $otherModals.filter('.' + className.active).find(selector.dimmer).addClass('active');
  8136. }
  8137. if ( settings.detachable ) {
  8138. $module.detach().appendTo($dimmer);
  8139. }
  8140. }
  8141. settings.onShow.call(element);
  8142. if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
  8143. module.debug('Showing modal with css animations');
  8144. $module
  8145. .transition({
  8146. debug : settings.debug,
  8147. animation : settings.transition + ' in',
  8148. queue : settings.queue,
  8149. duration : settings.duration,
  8150. useFailSafe : true,
  8151. onComplete : function() {
  8152. settings.onVisible.apply(element);
  8153. if(settings.keyboardShortcuts) {
  8154. module.add.keyboardShortcuts();
  8155. }
  8156. module.save.focus();
  8157. module.set.active();
  8158. if(settings.autofocus) {
  8159. module.set.autofocus();
  8160. }
  8161. callback();
  8162. }
  8163. })
  8164. ;
  8165. }
  8166. else {
  8167. module.error(error.noTransition);
  8168. }
  8169. }
  8170. }
  8171. else {
  8172. module.debug('Modal is already visible');
  8173. }
  8174. },
  8175. hideModal: function(callback, keepDimmed, hideOthersToo) {
  8176. var
  8177. $previousModal = $otherModals.filter('.' + className.active).last()
  8178. ;
  8179. callback = $.isFunction(callback)
  8180. ? callback
  8181. : function(){}
  8182. ;
  8183. module.debug('Hiding modal');
  8184. if(settings.onHide.call(element, $(this)) === false) {
  8185. module.verbose('Hide callback returned false cancelling hide');
  8186. ignoreRepeatedEvents = false;
  8187. return false;
  8188. }
  8189. if( module.is.animating() || module.is.active() ) {
  8190. if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
  8191. module.remove.active();
  8192. $module
  8193. .transition({
  8194. debug : settings.debug,
  8195. animation : settings.transition + ' out',
  8196. queue : settings.queue,
  8197. duration : settings.duration,
  8198. useFailSafe : true,
  8199. onStart : function() {
  8200. if(!module.others.active() && !module.others.animating() && !keepDimmed) {
  8201. module.hideDimmer();
  8202. }
  8203. if( settings.keyboardShortcuts && !module.others.active() ) {
  8204. module.remove.keyboardShortcuts();
  8205. }
  8206. },
  8207. onComplete : function() {
  8208. module.unbind.scrollLock();
  8209. if ( settings.allowMultiple ) {
  8210. $previousModal.addClass(className.front);
  8211. $module.removeClass(className.front);
  8212. if ( hideOthersToo ) {
  8213. $allModals.find(selector.dimmer).removeClass('active');
  8214. }
  8215. else {
  8216. $previousModal.find(selector.dimmer).removeClass('active');
  8217. }
  8218. }
  8219. settings.onHidden.call(element);
  8220. module.remove.dimmerStyles();
  8221. module.restore.focus();
  8222. callback();
  8223. }
  8224. })
  8225. ;
  8226. }
  8227. else {
  8228. module.error(error.noTransition);
  8229. }
  8230. }
  8231. },
  8232. showDimmer: function() {
  8233. if($dimmable.dimmer('is animating') || !$dimmable.dimmer('is active') ) {
  8234. module.save.bodyMargin();
  8235. module.debug('Showing dimmer');
  8236. $dimmable.dimmer('show');
  8237. }
  8238. else {
  8239. module.debug('Dimmer already visible');
  8240. }
  8241. },
  8242. hideDimmer: function() {
  8243. if( $dimmable.dimmer('is animating') || ($dimmable.dimmer('is active')) ) {
  8244. module.unbind.scrollLock();
  8245. $dimmable.dimmer('hide', function() {
  8246. module.restore.bodyMargin();
  8247. module.remove.clickaway();
  8248. module.remove.screenHeight();
  8249. });
  8250. }
  8251. else {
  8252. module.debug('Dimmer is not visible cannot hide');
  8253. return;
  8254. }
  8255. },
  8256. hideAll: function(callback) {
  8257. var
  8258. $visibleModals = $allModals.filter('.' + className.active + ', .' + className.animating)
  8259. ;
  8260. callback = $.isFunction(callback)
  8261. ? callback
  8262. : function(){}
  8263. ;
  8264. if( $visibleModals.length > 0 ) {
  8265. module.debug('Hiding all visible modals');
  8266. var hideOk = true;
  8267. //check in reverse order trying to hide most top displayed modal first
  8268. $($visibleModals.get().reverse()).each(function(index,element){
  8269. if(hideOk){
  8270. hideOk = $(element).modal('hide modal', callback, false, true);
  8271. }
  8272. });
  8273. if(hideOk) {
  8274. module.hideDimmer();
  8275. }
  8276. return hideOk;
  8277. }
  8278. },
  8279. hideOthers: function(callback) {
  8280. var
  8281. $visibleModals = $otherModals.filter('.' + className.active + ', .' + className.animating)
  8282. ;
  8283. callback = $.isFunction(callback)
  8284. ? callback
  8285. : function(){}
  8286. ;
  8287. if( $visibleModals.length > 0 ) {
  8288. module.debug('Hiding other modals', $otherModals);
  8289. $visibleModals
  8290. .modal('hide modal', callback, true)
  8291. ;
  8292. }
  8293. },
  8294. others: {
  8295. active: function() {
  8296. return ($otherModals.filter('.' + className.active).length > 0);
  8297. },
  8298. animating: function() {
  8299. return ($otherModals.filter('.' + className.animating).length > 0);
  8300. }
  8301. },
  8302. add: {
  8303. keyboardShortcuts: function() {
  8304. module.verbose('Adding keyboard shortcuts');
  8305. $document
  8306. .on('keyup' + eventNamespace, module.event.keyboard)
  8307. ;
  8308. }
  8309. },
  8310. save: {
  8311. focus: function() {
  8312. var
  8313. $activeElement = $(document.activeElement),
  8314. inCurrentModal = $activeElement.closest($module).length > 0
  8315. ;
  8316. if(!inCurrentModal) {
  8317. $focusedElement = $(document.activeElement).blur();
  8318. }
  8319. },
  8320. bodyMargin: function() {
  8321. initialBodyMargin = $body.css('margin-'+(module.can.leftBodyScrollbar() ? 'left':'right'));
  8322. var bodyMarginRightPixel = parseInt(initialBodyMargin.replace(/[^\d.]/g, '')),
  8323. bodyScrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
  8324. tempBodyMargin = bodyMarginRightPixel + bodyScrollbarWidth;
  8325. }
  8326. },
  8327. restore: {
  8328. focus: function() {
  8329. if($focusedElement && $focusedElement.length > 0 && settings.restoreFocus) {
  8330. $focusedElement.focus();
  8331. }
  8332. },
  8333. bodyMargin: function() {
  8334. var position = module.can.leftBodyScrollbar() ? 'left':'right';
  8335. $body.css('margin-'+position, initialBodyMargin);
  8336. $body.find(selector.bodyFixed.replace('right',position)).css('padding-'+position, initialBodyMargin);
  8337. }
  8338. },
  8339. remove: {
  8340. active: function() {
  8341. $module.removeClass(className.active);
  8342. },
  8343. legacy: function() {
  8344. $module.removeClass(className.legacy);
  8345. },
  8346. clickaway: function() {
  8347. if (!settings.detachable) {
  8348. $module
  8349. .off('mousedown' + elementEventNamespace)
  8350. ;
  8351. }
  8352. $dimmer
  8353. .off('mousedown' + elementEventNamespace)
  8354. ;
  8355. $dimmer
  8356. .off('mouseup' + elementEventNamespace)
  8357. ;
  8358. },
  8359. dimmerStyles: function() {
  8360. $dimmer.removeClass(className.inverted);
  8361. $dimmable.removeClass(className.blurring);
  8362. },
  8363. bodyStyle: function() {
  8364. if($body.attr('style') === '') {
  8365. module.verbose('Removing style attribute');
  8366. $body.removeAttr('style');
  8367. }
  8368. },
  8369. screenHeight: function() {
  8370. module.debug('Removing page height');
  8371. $body
  8372. .css('height', '')
  8373. ;
  8374. },
  8375. keyboardShortcuts: function() {
  8376. module.verbose('Removing keyboard shortcuts');
  8377. $document
  8378. .off('keyup' + eventNamespace)
  8379. ;
  8380. },
  8381. scrolling: function() {
  8382. $dimmable.removeClass(className.scrolling);
  8383. $module.removeClass(className.scrolling);
  8384. }
  8385. },
  8386. cacheSizes: function() {
  8387. $module.addClass(className.loading);
  8388. var
  8389. scrollHeight = $module.prop('scrollHeight'),
  8390. modalWidth = $module.outerWidth(),
  8391. modalHeight = $module.outerHeight()
  8392. ;
  8393. if(module.cache.pageHeight === undefined || modalHeight !== 0) {
  8394. $.extend(module.cache, {
  8395. pageHeight : $(document).outerHeight(),
  8396. width : modalWidth,
  8397. height : modalHeight + settings.offset,
  8398. scrollHeight : scrollHeight + settings.offset,
  8399. contextHeight : (settings.context == 'body')
  8400. ? $(window).height()
  8401. : $dimmable.height(),
  8402. });
  8403. module.cache.topOffset = -(module.cache.height / 2);
  8404. }
  8405. $module.removeClass(className.loading);
  8406. module.debug('Caching modal and container sizes', module.cache);
  8407. },
  8408. can: {
  8409. leftBodyScrollbar: function(){
  8410. if(module.cache.leftBodyScrollbar === undefined) {
  8411. module.cache.leftBodyScrollbar = module.is.rtl() && ((module.is.iframe && !module.is.firefox()) || module.is.safari() || module.is.edge() || module.is.ie());
  8412. }
  8413. return module.cache.leftBodyScrollbar;
  8414. },
  8415. useFlex: function() {
  8416. if (settings.useFlex === 'auto') {
  8417. return settings.detachable && !module.is.ie();
  8418. }
  8419. if(settings.useFlex && module.is.ie()) {
  8420. module.debug('useFlex true is not supported in IE');
  8421. } else if(settings.useFlex && !settings.detachable) {
  8422. module.debug('useFlex true in combination with detachable false is not supported');
  8423. }
  8424. return settings.useFlex;
  8425. },
  8426. fit: function() {
  8427. var
  8428. contextHeight = module.cache.contextHeight,
  8429. verticalCenter = module.cache.contextHeight / 2,
  8430. topOffset = module.cache.topOffset,
  8431. scrollHeight = module.cache.scrollHeight,
  8432. height = module.cache.height,
  8433. paddingHeight = settings.padding,
  8434. startPosition = (verticalCenter + topOffset)
  8435. ;
  8436. return (scrollHeight > height)
  8437. ? (startPosition + scrollHeight + paddingHeight < contextHeight)
  8438. : (height + (paddingHeight * 2) < contextHeight)
  8439. ;
  8440. }
  8441. },
  8442. is: {
  8443. active: function() {
  8444. return $module.hasClass(className.active);
  8445. },
  8446. ie: function() {
  8447. if(module.cache.isIE === undefined) {
  8448. var
  8449. isIE11 = (!(window.ActiveXObject) && 'ActiveXObject' in window),
  8450. isIE = ('ActiveXObject' in window)
  8451. ;
  8452. module.cache.isIE = (isIE11 || isIE);
  8453. }
  8454. return module.cache.isIE;
  8455. },
  8456. animating: function() {
  8457. return $module.transition('is supported')
  8458. ? $module.transition('is animating')
  8459. : $module.is(':visible')
  8460. ;
  8461. },
  8462. scrolling: function() {
  8463. return $dimmable.hasClass(className.scrolling);
  8464. },
  8465. modernBrowser: function() {
  8466. // appName for IE11 reports 'Netscape' can no longer use
  8467. return !(window.ActiveXObject || 'ActiveXObject' in window);
  8468. },
  8469. rtl: function() {
  8470. if(module.cache.isRTL === undefined) {
  8471. module.cache.isRTL = $body.attr('dir') === 'rtl' || $body.css('direction') === 'rtl';
  8472. }
  8473. return module.cache.isRTL;
  8474. },
  8475. safari: function() {
  8476. if(module.cache.isSafari === undefined) {
  8477. module.cache.isSafari = /constructor/i.test(window.HTMLElement) || !!window.ApplePaySession;
  8478. }
  8479. return module.cache.isSafari;
  8480. },
  8481. edge: function(){
  8482. if(module.cache.isEdge === undefined) {
  8483. module.cache.isEdge = !!window.setImmediate && !module.is.ie();
  8484. }
  8485. return module.cache.isEdge;
  8486. },
  8487. firefox: function(){
  8488. if(module.cache.isFirefox === undefined) {
  8489. module.cache.isFirefox = !!window.InstallTrigger;
  8490. }
  8491. return module.cache.isFirefox;
  8492. },
  8493. iframe: function() {
  8494. return !(self === top);
  8495. }
  8496. },
  8497. set: {
  8498. autofocus: function() {
  8499. var
  8500. $inputs = $module.find('[tabindex], :input').filter(':visible').filter(function() {
  8501. return $(this).closest('.disabled').length === 0;
  8502. }),
  8503. $autofocus = $inputs.filter('[autofocus]'),
  8504. $input = ($autofocus.length > 0)
  8505. ? $autofocus.first()
  8506. : $inputs.first()
  8507. ;
  8508. if($input.length > 0) {
  8509. $input.focus();
  8510. }
  8511. },
  8512. bodyMargin: function() {
  8513. var position = module.can.leftBodyScrollbar() ? 'left':'right';
  8514. if(settings.detachable || module.can.fit()) {
  8515. $body.css('margin-'+position, tempBodyMargin + 'px');
  8516. }
  8517. $body.find(selector.bodyFixed.replace('right',position)).css('padding-'+position, tempBodyMargin + 'px');
  8518. },
  8519. clickaway: function() {
  8520. if (!settings.detachable) {
  8521. $module
  8522. .on('mousedown' + elementEventNamespace, module.event.mousedown)
  8523. ;
  8524. }
  8525. $dimmer
  8526. .on('mousedown' + elementEventNamespace, module.event.mousedown)
  8527. ;
  8528. $dimmer
  8529. .on('mouseup' + elementEventNamespace, module.event.mouseup)
  8530. ;
  8531. },
  8532. dimmerSettings: function() {
  8533. if($.fn.dimmer === undefined) {
  8534. module.error(error.dimmer);
  8535. return;
  8536. }
  8537. var
  8538. defaultSettings = {
  8539. debug : settings.debug,
  8540. dimmerName : 'modals',
  8541. closable : 'auto',
  8542. useFlex : module.can.useFlex(),
  8543. duration : {
  8544. show : settings.duration,
  8545. hide : settings.duration
  8546. }
  8547. },
  8548. dimmerSettings = $.extend(true, defaultSettings, settings.dimmerSettings)
  8549. ;
  8550. if(settings.inverted) {
  8551. dimmerSettings.variation = (dimmerSettings.variation !== undefined)
  8552. ? dimmerSettings.variation + ' inverted'
  8553. : 'inverted'
  8554. ;
  8555. }
  8556. $context.dimmer('setting', dimmerSettings);
  8557. },
  8558. dimmerStyles: function() {
  8559. if(settings.inverted) {
  8560. $dimmer.addClass(className.inverted);
  8561. }
  8562. else {
  8563. $dimmer.removeClass(className.inverted);
  8564. }
  8565. if(settings.blurring) {
  8566. $dimmable.addClass(className.blurring);
  8567. }
  8568. else {
  8569. $dimmable.removeClass(className.blurring);
  8570. }
  8571. },
  8572. modalOffset: function() {
  8573. if (!settings.detachable) {
  8574. var canFit = module.can.fit();
  8575. $module
  8576. .css({
  8577. top: (!$module.hasClass('aligned') && canFit)
  8578. ? $(document).scrollTop() + (module.cache.contextHeight - module.cache.height) / 2
  8579. : !canFit || $module.hasClass('top')
  8580. ? $(document).scrollTop() + settings.padding
  8581. : $(document).scrollTop() + (module.cache.contextHeight - module.cache.height - settings.padding),
  8582. marginLeft: -(module.cache.width / 2)
  8583. })
  8584. ;
  8585. } else {
  8586. $module
  8587. .css({
  8588. marginTop: (!$module.hasClass('aligned') && module.can.fit())
  8589. ? -(module.cache.height / 2)
  8590. : settings.padding / 2,
  8591. marginLeft: -(module.cache.width / 2)
  8592. })
  8593. ;
  8594. }
  8595. module.verbose('Setting modal offset for legacy mode');
  8596. },
  8597. screenHeight: function() {
  8598. if( module.can.fit() ) {
  8599. $body.css('height', '');
  8600. }
  8601. else if(!$module.hasClass('bottom')) {
  8602. module.debug('Modal is taller than page content, resizing page height');
  8603. $body
  8604. .css('height', module.cache.height + (settings.padding * 2) )
  8605. ;
  8606. }
  8607. },
  8608. active: function() {
  8609. $module.addClass(className.active + ' ' + className.front);
  8610. $otherModals.filter('.' + className.active).removeClass(className.front);
  8611. },
  8612. scrolling: function() {
  8613. $dimmable.addClass(className.scrolling);
  8614. $module.addClass(className.scrolling);
  8615. module.unbind.scrollLock();
  8616. },
  8617. legacy: function() {
  8618. $module.addClass(className.legacy);
  8619. },
  8620. type: function() {
  8621. if(module.can.fit()) {
  8622. module.verbose('Modal fits on screen');
  8623. if(!module.others.active() && !module.others.animating()) {
  8624. module.remove.scrolling();
  8625. module.bind.scrollLock();
  8626. }
  8627. }
  8628. else if (!$module.hasClass('bottom')){
  8629. module.verbose('Modal cannot fit on screen setting to scrolling');
  8630. module.set.scrolling();
  8631. } else {
  8632. module.verbose('Bottom aligned modal not fitting on screen is unsupported for scrolling');
  8633. }
  8634. },
  8635. undetached: function() {
  8636. $dimmable.addClass(className.undetached);
  8637. }
  8638. },
  8639. setting: function(name, value) {
  8640. module.debug('Changing setting', name, value);
  8641. if( $.isPlainObject(name) ) {
  8642. $.extend(true, settings, name);
  8643. }
  8644. else if(value !== undefined) {
  8645. if($.isPlainObject(settings[name])) {
  8646. $.extend(true, settings[name], value);
  8647. }
  8648. else {
  8649. settings[name] = value;
  8650. }
  8651. }
  8652. else {
  8653. return settings[name];
  8654. }
  8655. },
  8656. internal: function(name, value) {
  8657. if( $.isPlainObject(name) ) {
  8658. $.extend(true, module, name);
  8659. }
  8660. else if(value !== undefined) {
  8661. module[name] = value;
  8662. }
  8663. else {
  8664. return module[name];
  8665. }
  8666. },
  8667. debug: function() {
  8668. if(!settings.silent && settings.debug) {
  8669. if(settings.performance) {
  8670. module.performance.log(arguments);
  8671. }
  8672. else {
  8673. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  8674. module.debug.apply(console, arguments);
  8675. }
  8676. }
  8677. },
  8678. verbose: function() {
  8679. if(!settings.silent && settings.verbose && settings.debug) {
  8680. if(settings.performance) {
  8681. module.performance.log(arguments);
  8682. }
  8683. else {
  8684. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  8685. module.verbose.apply(console, arguments);
  8686. }
  8687. }
  8688. },
  8689. error: function() {
  8690. if(!settings.silent) {
  8691. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  8692. module.error.apply(console, arguments);
  8693. }
  8694. },
  8695. performance: {
  8696. log: function(message) {
  8697. var
  8698. currentTime,
  8699. executionTime,
  8700. previousTime
  8701. ;
  8702. if(settings.performance) {
  8703. currentTime = new Date().getTime();
  8704. previousTime = time || currentTime;
  8705. executionTime = currentTime - previousTime;
  8706. time = currentTime;
  8707. performance.push({
  8708. 'Name' : message[0],
  8709. 'Arguments' : [].slice.call(message, 1) || '',
  8710. 'Element' : element,
  8711. 'Execution Time' : executionTime
  8712. });
  8713. }
  8714. clearTimeout(module.performance.timer);
  8715. module.performance.timer = setTimeout(module.performance.display, 500);
  8716. },
  8717. display: function() {
  8718. var
  8719. title = settings.name + ':',
  8720. totalTime = 0
  8721. ;
  8722. time = false;
  8723. clearTimeout(module.performance.timer);
  8724. $.each(performance, function(index, data) {
  8725. totalTime += data['Execution Time'];
  8726. });
  8727. title += ' ' + totalTime + 'ms';
  8728. if(moduleSelector) {
  8729. title += ' \'' + moduleSelector + '\'';
  8730. }
  8731. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  8732. console.groupCollapsed(title);
  8733. if(console.table) {
  8734. console.table(performance);
  8735. }
  8736. else {
  8737. $.each(performance, function(index, data) {
  8738. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  8739. });
  8740. }
  8741. console.groupEnd();
  8742. }
  8743. performance = [];
  8744. }
  8745. },
  8746. invoke: function(query, passedArguments, context) {
  8747. var
  8748. object = instance,
  8749. maxDepth,
  8750. found,
  8751. response
  8752. ;
  8753. passedArguments = passedArguments || queryArguments;
  8754. context = element || context;
  8755. if(typeof query == 'string' && object !== undefined) {
  8756. query = query.split(/[\. ]/);
  8757. maxDepth = query.length - 1;
  8758. $.each(query, function(depth, value) {
  8759. var camelCaseValue = (depth != maxDepth)
  8760. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  8761. : query
  8762. ;
  8763. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  8764. object = object[camelCaseValue];
  8765. }
  8766. else if( object[camelCaseValue] !== undefined ) {
  8767. found = object[camelCaseValue];
  8768. return false;
  8769. }
  8770. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  8771. object = object[value];
  8772. }
  8773. else if( object[value] !== undefined ) {
  8774. found = object[value];
  8775. return false;
  8776. }
  8777. else {
  8778. return false;
  8779. }
  8780. });
  8781. }
  8782. if ( $.isFunction( found ) ) {
  8783. response = found.apply(context, passedArguments);
  8784. }
  8785. else if(found !== undefined) {
  8786. response = found;
  8787. }
  8788. if(Array.isArray(returnedValue)) {
  8789. returnedValue.push(response);
  8790. }
  8791. else if(returnedValue !== undefined) {
  8792. returnedValue = [returnedValue, response];
  8793. }
  8794. else if(response !== undefined) {
  8795. returnedValue = response;
  8796. }
  8797. return found;
  8798. }
  8799. };
  8800. if(methodInvoked) {
  8801. if(instance === undefined) {
  8802. module.initialize();
  8803. }
  8804. module.invoke(query);
  8805. }
  8806. else {
  8807. if(instance !== undefined) {
  8808. instance.invoke('destroy');
  8809. }
  8810. module.initialize();
  8811. }
  8812. })
  8813. ;
  8814. return (returnedValue !== undefined)
  8815. ? returnedValue
  8816. : this
  8817. ;
  8818. };
  8819. $.fn.modal.settings = {
  8820. name : 'Modal',
  8821. namespace : 'modal',
  8822. useFlex : 'auto',
  8823. offset : 0,
  8824. silent : false,
  8825. debug : false,
  8826. verbose : false,
  8827. performance : true,
  8828. observeChanges : false,
  8829. allowMultiple : false,
  8830. detachable : true,
  8831. closable : true,
  8832. autofocus : true,
  8833. restoreFocus : true,
  8834. inverted : false,
  8835. blurring : false,
  8836. centered : true,
  8837. dimmerSettings : {
  8838. closable : false,
  8839. useCSS : true
  8840. },
  8841. // whether to use keyboard shortcuts
  8842. keyboardShortcuts: true,
  8843. context : 'body',
  8844. queue : false,
  8845. duration : 500,
  8846. transition : 'scale',
  8847. // padding with edge of page
  8848. padding : 50,
  8849. scrollbarWidth: 10,
  8850. // called before show animation
  8851. onShow : function(){},
  8852. // called after show animation
  8853. onVisible : function(){},
  8854. // called before hide animation
  8855. onHide : function(){ return true; },
  8856. // called after hide animation
  8857. onHidden : function(){},
  8858. // called after approve selector match
  8859. onApprove : function(){ return true; },
  8860. // called after deny selector match
  8861. onDeny : function(){ return true; },
  8862. selector : {
  8863. close : '> .close',
  8864. approve : '.actions .positive, .actions .approve, .actions .ok',
  8865. deny : '.actions .negative, .actions .deny, .actions .cancel',
  8866. modal : '.ui.modal',
  8867. dimmer : '> .ui.dimmer',
  8868. bodyFixed: '> .ui.fixed.menu, > .ui.right.toast-container, > .ui.right.sidebar'
  8869. },
  8870. error : {
  8871. dimmer : 'UI Dimmer, a required component is not included in this page',
  8872. method : 'The method you called is not defined.',
  8873. notFound : 'The element you specified could not be found'
  8874. },
  8875. className : {
  8876. active : 'active',
  8877. animating : 'animating',
  8878. blurring : 'blurring',
  8879. inverted : 'inverted',
  8880. legacy : 'legacy',
  8881. loading : 'loading',
  8882. scrolling : 'scrolling',
  8883. undetached : 'undetached',
  8884. front : 'front'
  8885. }
  8886. };
  8887. })( jQuery, window, document );
  8888. /*!
  8889. * # Fomantic-UI - Search
  8890. * http://github.com/fomantic/Fomantic-UI/
  8891. *
  8892. *
  8893. * Released under the MIT license
  8894. * http://opensource.org/licenses/MIT
  8895. *
  8896. */
  8897. ;(function ($, window, document, undefined) {
  8898. 'use strict';
  8899. $.isFunction = $.isFunction || function(obj) {
  8900. return typeof obj === "function" && typeof obj.nodeType !== "number";
  8901. };
  8902. window = (typeof window != 'undefined' && window.Math == Math)
  8903. ? window
  8904. : (typeof self != 'undefined' && self.Math == Math)
  8905. ? self
  8906. : Function('return this')()
  8907. ;
  8908. $.fn.search = function(parameters) {
  8909. var
  8910. $allModules = $(this),
  8911. moduleSelector = $allModules.selector || '',
  8912. time = new Date().getTime(),
  8913. performance = [],
  8914. query = arguments[0],
  8915. methodInvoked = (typeof query == 'string'),
  8916. queryArguments = [].slice.call(arguments, 1),
  8917. returnedValue
  8918. ;
  8919. $(this)
  8920. .each(function() {
  8921. var
  8922. settings = ( $.isPlainObject(parameters) )
  8923. ? $.extend(true, {}, $.fn.search.settings, parameters)
  8924. : $.extend({}, $.fn.search.settings),
  8925. className = settings.className,
  8926. metadata = settings.metadata,
  8927. regExp = settings.regExp,
  8928. fields = settings.fields,
  8929. selector = settings.selector,
  8930. error = settings.error,
  8931. namespace = settings.namespace,
  8932. eventNamespace = '.' + namespace,
  8933. moduleNamespace = namespace + '-module',
  8934. $module = $(this),
  8935. $prompt = $module.find(selector.prompt),
  8936. $searchButton = $module.find(selector.searchButton),
  8937. $results = $module.find(selector.results),
  8938. $result = $module.find(selector.result),
  8939. $category = $module.find(selector.category),
  8940. element = this,
  8941. instance = $module.data(moduleNamespace),
  8942. disabledBubbled = false,
  8943. resultsDismissed = false,
  8944. module
  8945. ;
  8946. module = {
  8947. initialize: function() {
  8948. module.verbose('Initializing module');
  8949. module.get.settings();
  8950. module.determine.searchFields();
  8951. module.bind.events();
  8952. module.set.type();
  8953. module.create.results();
  8954. module.instantiate();
  8955. },
  8956. instantiate: function() {
  8957. module.verbose('Storing instance of module', module);
  8958. instance = module;
  8959. $module
  8960. .data(moduleNamespace, module)
  8961. ;
  8962. },
  8963. destroy: function() {
  8964. module.verbose('Destroying instance');
  8965. $module
  8966. .off(eventNamespace)
  8967. .removeData(moduleNamespace)
  8968. ;
  8969. },
  8970. refresh: function() {
  8971. module.debug('Refreshing selector cache');
  8972. $prompt = $module.find(selector.prompt);
  8973. $searchButton = $module.find(selector.searchButton);
  8974. $category = $module.find(selector.category);
  8975. $results = $module.find(selector.results);
  8976. $result = $module.find(selector.result);
  8977. },
  8978. refreshResults: function() {
  8979. $results = $module.find(selector.results);
  8980. $result = $module.find(selector.result);
  8981. },
  8982. bind: {
  8983. events: function() {
  8984. module.verbose('Binding events to search');
  8985. if(settings.automatic) {
  8986. $module
  8987. .on(module.get.inputEvent() + eventNamespace, selector.prompt, module.event.input)
  8988. ;
  8989. $prompt
  8990. .attr('autocomplete', 'off')
  8991. ;
  8992. }
  8993. $module
  8994. // prompt
  8995. .on('focus' + eventNamespace, selector.prompt, module.event.focus)
  8996. .on('blur' + eventNamespace, selector.prompt, module.event.blur)
  8997. .on('keydown' + eventNamespace, selector.prompt, module.handleKeyboard)
  8998. // search button
  8999. .on('click' + eventNamespace, selector.searchButton, module.query)
  9000. // results
  9001. .on('mousedown' + eventNamespace, selector.results, module.event.result.mousedown)
  9002. .on('mouseup' + eventNamespace, selector.results, module.event.result.mouseup)
  9003. .on('click' + eventNamespace, selector.result, module.event.result.click)
  9004. ;
  9005. }
  9006. },
  9007. determine: {
  9008. searchFields: function() {
  9009. // this makes sure $.extend does not add specified search fields to default fields
  9010. // this is the only setting which should not extend defaults
  9011. if(parameters && parameters.searchFields !== undefined) {
  9012. settings.searchFields = parameters.searchFields;
  9013. }
  9014. }
  9015. },
  9016. event: {
  9017. input: function() {
  9018. if(settings.searchDelay) {
  9019. clearTimeout(module.timer);
  9020. module.timer = setTimeout(function() {
  9021. if(module.is.focused()) {
  9022. module.query();
  9023. }
  9024. }, settings.searchDelay);
  9025. }
  9026. else {
  9027. module.query();
  9028. }
  9029. },
  9030. focus: function() {
  9031. module.set.focus();
  9032. if(settings.searchOnFocus && module.has.minimumCharacters() ) {
  9033. module.query(function() {
  9034. if(module.can.show() ) {
  9035. module.showResults();
  9036. }
  9037. });
  9038. }
  9039. },
  9040. blur: function(event) {
  9041. var
  9042. pageLostFocus = (document.activeElement === this),
  9043. callback = function() {
  9044. module.cancel.query();
  9045. module.remove.focus();
  9046. module.timer = setTimeout(module.hideResults, settings.hideDelay);
  9047. }
  9048. ;
  9049. if(pageLostFocus) {
  9050. return;
  9051. }
  9052. resultsDismissed = false;
  9053. if(module.resultsClicked) {
  9054. module.debug('Determining if user action caused search to close');
  9055. $module
  9056. .one('click.close' + eventNamespace, selector.results, function(event) {
  9057. if(module.is.inMessage(event) || disabledBubbled) {
  9058. $prompt.focus();
  9059. return;
  9060. }
  9061. disabledBubbled = false;
  9062. if( !module.is.animating() && !module.is.hidden()) {
  9063. callback();
  9064. }
  9065. })
  9066. ;
  9067. }
  9068. else {
  9069. module.debug('Input blurred without user action, closing results');
  9070. callback();
  9071. }
  9072. },
  9073. result: {
  9074. mousedown: function() {
  9075. module.resultsClicked = true;
  9076. },
  9077. mouseup: function() {
  9078. module.resultsClicked = false;
  9079. },
  9080. click: function(event) {
  9081. module.debug('Search result selected');
  9082. var
  9083. $result = $(this),
  9084. $title = $result.find(selector.title).eq(0),
  9085. $link = $result.is('a[href]')
  9086. ? $result
  9087. : $result.find('a[href]').eq(0),
  9088. href = $link.attr('href') || false,
  9089. target = $link.attr('target') || false,
  9090. // title is used for result lookup
  9091. value = ($title.length > 0)
  9092. ? $title.text()
  9093. : false,
  9094. results = module.get.results(),
  9095. result = $result.data(metadata.result) || module.get.result(value, results)
  9096. ;
  9097. if(value) {
  9098. module.set.value(value);
  9099. }
  9100. if( $.isFunction(settings.onSelect) ) {
  9101. if(settings.onSelect.call(element, result, results) === false) {
  9102. module.debug('Custom onSelect callback cancelled default select action');
  9103. disabledBubbled = true;
  9104. return;
  9105. }
  9106. }
  9107. module.hideResults();
  9108. if(href) {
  9109. event.preventDefault();
  9110. module.verbose('Opening search link found in result', $link);
  9111. if(target == '_blank' || event.ctrlKey) {
  9112. window.open(href);
  9113. }
  9114. else {
  9115. window.location.href = (href);
  9116. }
  9117. }
  9118. }
  9119. }
  9120. },
  9121. ensureVisible: function ensureVisible($el) {
  9122. var elTop, elBottom, resultsScrollTop, resultsHeight;
  9123. elTop = $el.position().top;
  9124. elBottom = elTop + $el.outerHeight(true);
  9125. resultsScrollTop = $results.scrollTop();
  9126. resultsHeight = $results.height()
  9127. parseInt($results.css('paddingTop'), 0) +
  9128. parseInt($results.css('paddingBottom'), 0);
  9129. if (elTop < 0) {
  9130. $results.scrollTop(resultsScrollTop + elTop);
  9131. }
  9132. else if (resultsHeight < elBottom) {
  9133. $results.scrollTop(resultsScrollTop + (elBottom - resultsHeight));
  9134. }
  9135. },
  9136. handleKeyboard: function(event) {
  9137. var
  9138. // force selector refresh
  9139. $result = $module.find(selector.result),
  9140. $category = $module.find(selector.category),
  9141. $activeResult = $result.filter('.' + className.active),
  9142. currentIndex = $result.index( $activeResult ),
  9143. resultSize = $result.length,
  9144. hasActiveResult = $activeResult.length > 0,
  9145. keyCode = event.which,
  9146. keys = {
  9147. backspace : 8,
  9148. enter : 13,
  9149. escape : 27,
  9150. upArrow : 38,
  9151. downArrow : 40
  9152. },
  9153. newIndex
  9154. ;
  9155. // search shortcuts
  9156. if(keyCode == keys.escape) {
  9157. module.verbose('Escape key pressed, blurring search field');
  9158. module.hideResults();
  9159. resultsDismissed = true;
  9160. }
  9161. if( module.is.visible() ) {
  9162. if(keyCode == keys.enter) {
  9163. module.verbose('Enter key pressed, selecting active result');
  9164. if( $result.filter('.' + className.active).length > 0 ) {
  9165. module.event.result.click.call($result.filter('.' + className.active), event);
  9166. event.preventDefault();
  9167. return false;
  9168. }
  9169. }
  9170. else if(keyCode == keys.upArrow && hasActiveResult) {
  9171. module.verbose('Up key pressed, changing active result');
  9172. newIndex = (currentIndex - 1 < 0)
  9173. ? currentIndex
  9174. : currentIndex - 1
  9175. ;
  9176. $category
  9177. .removeClass(className.active)
  9178. ;
  9179. $result
  9180. .removeClass(className.active)
  9181. .eq(newIndex)
  9182. .addClass(className.active)
  9183. .closest($category)
  9184. .addClass(className.active)
  9185. ;
  9186. module.ensureVisible($result.eq(newIndex));
  9187. event.preventDefault();
  9188. }
  9189. else if(keyCode == keys.downArrow) {
  9190. module.verbose('Down key pressed, changing active result');
  9191. newIndex = (currentIndex + 1 >= resultSize)
  9192. ? currentIndex
  9193. : currentIndex + 1
  9194. ;
  9195. $category
  9196. .removeClass(className.active)
  9197. ;
  9198. $result
  9199. .removeClass(className.active)
  9200. .eq(newIndex)
  9201. .addClass(className.active)
  9202. .closest($category)
  9203. .addClass(className.active)
  9204. ;
  9205. module.ensureVisible($result.eq(newIndex));
  9206. event.preventDefault();
  9207. }
  9208. }
  9209. else {
  9210. // query shortcuts
  9211. if(keyCode == keys.enter) {
  9212. module.verbose('Enter key pressed, executing query');
  9213. module.query();
  9214. module.set.buttonPressed();
  9215. $prompt.one('keyup', module.remove.buttonFocus);
  9216. }
  9217. }
  9218. },
  9219. setup: {
  9220. api: function(searchTerm, callback) {
  9221. var
  9222. apiSettings = {
  9223. debug : settings.debug,
  9224. on : false,
  9225. cache : settings.cache,
  9226. action : 'search',
  9227. urlData : {
  9228. query : searchTerm
  9229. },
  9230. onSuccess : function(response) {
  9231. module.parse.response.call(element, response, searchTerm);
  9232. callback();
  9233. },
  9234. onFailure : function() {
  9235. module.displayMessage(error.serverError);
  9236. callback();
  9237. },
  9238. onAbort : function(response) {
  9239. },
  9240. onError : module.error
  9241. }
  9242. ;
  9243. $.extend(true, apiSettings, settings.apiSettings);
  9244. module.verbose('Setting up API request', apiSettings);
  9245. $module.api(apiSettings);
  9246. }
  9247. },
  9248. can: {
  9249. useAPI: function() {
  9250. return $.fn.api !== undefined;
  9251. },
  9252. show: function() {
  9253. return module.is.focused() && !module.is.visible() && !module.is.empty();
  9254. },
  9255. transition: function() {
  9256. return settings.transition && $.fn.transition !== undefined && $module.transition('is supported');
  9257. }
  9258. },
  9259. is: {
  9260. animating: function() {
  9261. return $results.hasClass(className.animating);
  9262. },
  9263. hidden: function() {
  9264. return $results.hasClass(className.hidden);
  9265. },
  9266. inMessage: function(event) {
  9267. if(!event.target) {
  9268. return;
  9269. }
  9270. var
  9271. $target = $(event.target),
  9272. isInDOM = $.contains(document.documentElement, event.target)
  9273. ;
  9274. return (isInDOM && $target.closest(selector.message).length > 0);
  9275. },
  9276. empty: function() {
  9277. return ($results.html() === '');
  9278. },
  9279. visible: function() {
  9280. return ($results.filter(':visible').length > 0);
  9281. },
  9282. focused: function() {
  9283. return ($prompt.filter(':focus').length > 0);
  9284. }
  9285. },
  9286. get: {
  9287. settings: function() {
  9288. if($.isPlainObject(parameters) && parameters.searchFullText) {
  9289. settings.fullTextSearch = parameters.searchFullText;
  9290. module.error(settings.error.oldSearchSyntax, element);
  9291. }
  9292. if (settings.ignoreDiacritics && !String.prototype.normalize) {
  9293. settings.ignoreDiacritics = false;
  9294. module.error(error.noNormalize, element);
  9295. }
  9296. },
  9297. inputEvent: function() {
  9298. var
  9299. prompt = $prompt[0],
  9300. inputEvent = (prompt !== undefined && prompt.oninput !== undefined)
  9301. ? 'input'
  9302. : (prompt !== undefined && prompt.onpropertychange !== undefined)
  9303. ? 'propertychange'
  9304. : 'keyup'
  9305. ;
  9306. return inputEvent;
  9307. },
  9308. value: function() {
  9309. return $prompt.val();
  9310. },
  9311. results: function() {
  9312. var
  9313. results = $module.data(metadata.results)
  9314. ;
  9315. return results;
  9316. },
  9317. result: function(value, results) {
  9318. var
  9319. result = false
  9320. ;
  9321. value = (value !== undefined)
  9322. ? value
  9323. : module.get.value()
  9324. ;
  9325. results = (results !== undefined)
  9326. ? results
  9327. : module.get.results()
  9328. ;
  9329. if(settings.type === 'category') {
  9330. module.debug('Finding result that matches', value);
  9331. $.each(results, function(index, category) {
  9332. if(Array.isArray(category.results)) {
  9333. result = module.search.object(value, category.results)[0];
  9334. // don't continue searching if a result is found
  9335. if(result) {
  9336. return false;
  9337. }
  9338. }
  9339. });
  9340. }
  9341. else {
  9342. module.debug('Finding result in results object', value);
  9343. result = module.search.object(value, results)[0];
  9344. }
  9345. return result || false;
  9346. },
  9347. },
  9348. select: {
  9349. firstResult: function() {
  9350. module.verbose('Selecting first result');
  9351. $result.first().addClass(className.active);
  9352. }
  9353. },
  9354. set: {
  9355. focus: function() {
  9356. $module.addClass(className.focus);
  9357. },
  9358. loading: function() {
  9359. $module.addClass(className.loading);
  9360. },
  9361. value: function(value) {
  9362. module.verbose('Setting search input value', value);
  9363. $prompt
  9364. .val(value)
  9365. ;
  9366. },
  9367. type: function(type) {
  9368. type = type || settings.type;
  9369. if(settings.type == 'category') {
  9370. $module.addClass(settings.type);
  9371. }
  9372. },
  9373. buttonPressed: function() {
  9374. $searchButton.addClass(className.pressed);
  9375. }
  9376. },
  9377. remove: {
  9378. loading: function() {
  9379. $module.removeClass(className.loading);
  9380. },
  9381. focus: function() {
  9382. $module.removeClass(className.focus);
  9383. },
  9384. buttonPressed: function() {
  9385. $searchButton.removeClass(className.pressed);
  9386. },
  9387. diacritics: function(text) {
  9388. return settings.ignoreDiacritics ? text.normalize('NFD').replace(/[\u0300-\u036f]/g, '') : text;
  9389. }
  9390. },
  9391. query: function(callback) {
  9392. callback = $.isFunction(callback)
  9393. ? callback
  9394. : function(){}
  9395. ;
  9396. var
  9397. searchTerm = module.get.value(),
  9398. cache = module.read.cache(searchTerm)
  9399. ;
  9400. callback = callback || function() {};
  9401. if( module.has.minimumCharacters() ) {
  9402. if(cache) {
  9403. module.debug('Reading result from cache', searchTerm);
  9404. module.save.results(cache.results);
  9405. module.addResults(cache.html);
  9406. module.inject.id(cache.results);
  9407. callback();
  9408. }
  9409. else {
  9410. module.debug('Querying for', searchTerm);
  9411. if($.isPlainObject(settings.source) || Array.isArray(settings.source)) {
  9412. module.search.local(searchTerm);
  9413. callback();
  9414. }
  9415. else if( module.can.useAPI() ) {
  9416. module.search.remote(searchTerm, callback);
  9417. }
  9418. else {
  9419. module.error(error.source);
  9420. callback();
  9421. }
  9422. }
  9423. settings.onSearchQuery.call(element, searchTerm);
  9424. }
  9425. else {
  9426. module.hideResults();
  9427. }
  9428. },
  9429. search: {
  9430. local: function(searchTerm) {
  9431. var
  9432. results = module.search.object(searchTerm, settings.source),
  9433. searchHTML
  9434. ;
  9435. module.set.loading();
  9436. module.save.results(results);
  9437. module.debug('Returned full local search results', results);
  9438. if(settings.maxResults > 0) {
  9439. module.debug('Using specified max results', results);
  9440. results = results.slice(0, settings.maxResults);
  9441. }
  9442. if(settings.type == 'category') {
  9443. results = module.create.categoryResults(results);
  9444. }
  9445. searchHTML = module.generateResults({
  9446. results: results
  9447. });
  9448. module.remove.loading();
  9449. module.addResults(searchHTML);
  9450. module.inject.id(results);
  9451. module.write.cache(searchTerm, {
  9452. html : searchHTML,
  9453. results : results
  9454. });
  9455. },
  9456. remote: function(searchTerm, callback) {
  9457. callback = $.isFunction(callback)
  9458. ? callback
  9459. : function(){}
  9460. ;
  9461. if($module.api('is loading')) {
  9462. $module.api('abort');
  9463. }
  9464. module.setup.api(searchTerm, callback);
  9465. $module
  9466. .api('query')
  9467. ;
  9468. },
  9469. object: function(searchTerm, source, searchFields) {
  9470. searchTerm = module.remove.diacritics(String(searchTerm));
  9471. var
  9472. results = [],
  9473. exactResults = [],
  9474. fuzzyResults = [],
  9475. searchExp = searchTerm.replace(regExp.escape, '\\$&'),
  9476. matchRegExp = new RegExp(regExp.beginsWith + searchExp, 'i'),
  9477. // avoid duplicates when pushing results
  9478. addResult = function(array, result) {
  9479. var
  9480. notResult = ($.inArray(result, results) == -1),
  9481. notFuzzyResult = ($.inArray(result, fuzzyResults) == -1),
  9482. notExactResults = ($.inArray(result, exactResults) == -1)
  9483. ;
  9484. if(notResult && notFuzzyResult && notExactResults) {
  9485. array.push(result);
  9486. }
  9487. }
  9488. ;
  9489. source = source || settings.source;
  9490. searchFields = (searchFields !== undefined)
  9491. ? searchFields
  9492. : settings.searchFields
  9493. ;
  9494. // search fields should be array to loop correctly
  9495. if(!Array.isArray(searchFields)) {
  9496. searchFields = [searchFields];
  9497. }
  9498. // exit conditions if no source
  9499. if(source === undefined || source === false) {
  9500. module.error(error.source);
  9501. return [];
  9502. }
  9503. // iterate through search fields looking for matches
  9504. $.each(searchFields, function(index, field) {
  9505. $.each(source, function(label, content) {
  9506. var
  9507. fieldExists = (typeof content[field] == 'string') || (typeof content[field] == 'number')
  9508. ;
  9509. if(fieldExists) {
  9510. var text;
  9511. if (typeof content[field] === 'string'){
  9512. text = module.remove.diacritics(content[field]);
  9513. } else {
  9514. text = content[field].toString();
  9515. }
  9516. if( text.search(matchRegExp) !== -1) {
  9517. // content starts with value (first in results)
  9518. addResult(results, content);
  9519. }
  9520. else if(settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, text) ) {
  9521. // content fuzzy matches (last in results)
  9522. addResult(exactResults, content);
  9523. }
  9524. else if(settings.fullTextSearch == true && module.fuzzySearch(searchTerm, text) ) {
  9525. // content fuzzy matches (last in results)
  9526. addResult(fuzzyResults, content);
  9527. }
  9528. }
  9529. });
  9530. });
  9531. $.merge(exactResults, fuzzyResults);
  9532. $.merge(results, exactResults);
  9533. return results;
  9534. }
  9535. },
  9536. exactSearch: function (query, term) {
  9537. query = query.toLowerCase();
  9538. term = term.toLowerCase();
  9539. return term.indexOf(query) > -1;
  9540. },
  9541. fuzzySearch: function(query, term) {
  9542. var
  9543. termLength = term.length,
  9544. queryLength = query.length
  9545. ;
  9546. if(typeof query !== 'string') {
  9547. return false;
  9548. }
  9549. query = query.toLowerCase();
  9550. term = term.toLowerCase();
  9551. if(queryLength > termLength) {
  9552. return false;
  9553. }
  9554. if(queryLength === termLength) {
  9555. return (query === term);
  9556. }
  9557. search: for (var characterIndex = 0, nextCharacterIndex = 0; characterIndex < queryLength; characterIndex++) {
  9558. var
  9559. queryCharacter = query.charCodeAt(characterIndex)
  9560. ;
  9561. while(nextCharacterIndex < termLength) {
  9562. if(term.charCodeAt(nextCharacterIndex++) === queryCharacter) {
  9563. continue search;
  9564. }
  9565. }
  9566. return false;
  9567. }
  9568. return true;
  9569. },
  9570. parse: {
  9571. response: function(response, searchTerm) {
  9572. if(Array.isArray(response)){
  9573. var o={};
  9574. o[fields.results]=response;
  9575. response = o;
  9576. }
  9577. var
  9578. searchHTML = module.generateResults(response)
  9579. ;
  9580. module.verbose('Parsing server response', response);
  9581. if(response !== undefined) {
  9582. if(searchTerm !== undefined && response[fields.results] !== undefined) {
  9583. module.addResults(searchHTML);
  9584. module.inject.id(response[fields.results]);
  9585. module.write.cache(searchTerm, {
  9586. html : searchHTML,
  9587. results : response[fields.results]
  9588. });
  9589. module.save.results(response[fields.results]);
  9590. }
  9591. }
  9592. }
  9593. },
  9594. cancel: {
  9595. query: function() {
  9596. if( module.can.useAPI() ) {
  9597. $module.api('abort');
  9598. }
  9599. }
  9600. },
  9601. has: {
  9602. minimumCharacters: function() {
  9603. var
  9604. searchTerm = module.get.value(),
  9605. numCharacters = searchTerm.length
  9606. ;
  9607. return (numCharacters >= settings.minCharacters);
  9608. },
  9609. results: function() {
  9610. if($results.length === 0) {
  9611. return false;
  9612. }
  9613. var
  9614. html = $results.html()
  9615. ;
  9616. return html != '';
  9617. }
  9618. },
  9619. clear: {
  9620. cache: function(value) {
  9621. var
  9622. cache = $module.data(metadata.cache)
  9623. ;
  9624. if(!value) {
  9625. module.debug('Clearing cache', value);
  9626. $module.removeData(metadata.cache);
  9627. }
  9628. else if(value && cache && cache[value]) {
  9629. module.debug('Removing value from cache', value);
  9630. delete cache[value];
  9631. $module.data(metadata.cache, cache);
  9632. }
  9633. }
  9634. },
  9635. read: {
  9636. cache: function(name) {
  9637. var
  9638. cache = $module.data(metadata.cache)
  9639. ;
  9640. if(settings.cache) {
  9641. module.verbose('Checking cache for generated html for query', name);
  9642. return (typeof cache == 'object') && (cache[name] !== undefined)
  9643. ? cache[name]
  9644. : false
  9645. ;
  9646. }
  9647. return false;
  9648. }
  9649. },
  9650. create: {
  9651. categoryResults: function(results) {
  9652. var
  9653. categoryResults = {}
  9654. ;
  9655. $.each(results, function(index, result) {
  9656. if(!result.category) {
  9657. return;
  9658. }
  9659. if(categoryResults[result.category] === undefined) {
  9660. module.verbose('Creating new category of results', result.category);
  9661. categoryResults[result.category] = {
  9662. name : result.category,
  9663. results : [result]
  9664. };
  9665. }
  9666. else {
  9667. categoryResults[result.category].results.push(result);
  9668. }
  9669. });
  9670. return categoryResults;
  9671. },
  9672. id: function(resultIndex, categoryIndex) {
  9673. var
  9674. resultID = (resultIndex + 1), // not zero indexed
  9675. letterID,
  9676. id
  9677. ;
  9678. if(categoryIndex !== undefined) {
  9679. // start char code for "A"
  9680. letterID = String.fromCharCode(97 + categoryIndex);
  9681. id = letterID + resultID;
  9682. module.verbose('Creating category result id', id);
  9683. }
  9684. else {
  9685. id = resultID;
  9686. module.verbose('Creating result id', id);
  9687. }
  9688. return id;
  9689. },
  9690. results: function() {
  9691. if($results.length === 0) {
  9692. $results = $('<div />')
  9693. .addClass(className.results)
  9694. .appendTo($module)
  9695. ;
  9696. }
  9697. }
  9698. },
  9699. inject: {
  9700. result: function(result, resultIndex, categoryIndex) {
  9701. module.verbose('Injecting result into results');
  9702. var
  9703. $selectedResult = (categoryIndex !== undefined)
  9704. ? $results
  9705. .children().eq(categoryIndex)
  9706. .children(selector.results)
  9707. .first()
  9708. .children(selector.result)
  9709. .eq(resultIndex)
  9710. : $results
  9711. .children(selector.result).eq(resultIndex)
  9712. ;
  9713. module.verbose('Injecting results metadata', $selectedResult);
  9714. $selectedResult
  9715. .data(metadata.result, result)
  9716. ;
  9717. },
  9718. id: function(results) {
  9719. module.debug('Injecting unique ids into results');
  9720. var
  9721. // since results may be object, we must use counters
  9722. categoryIndex = 0,
  9723. resultIndex = 0
  9724. ;
  9725. if(settings.type === 'category') {
  9726. // iterate through each category result
  9727. $.each(results, function(index, category) {
  9728. if(category.results.length > 0){
  9729. resultIndex = 0;
  9730. $.each(category.results, function(index, result) {
  9731. if(result.id === undefined) {
  9732. result.id = module.create.id(resultIndex, categoryIndex);
  9733. }
  9734. module.inject.result(result, resultIndex, categoryIndex);
  9735. resultIndex++;
  9736. });
  9737. categoryIndex++;
  9738. }
  9739. });
  9740. }
  9741. else {
  9742. // top level
  9743. $.each(results, function(index, result) {
  9744. if(result.id === undefined) {
  9745. result.id = module.create.id(resultIndex);
  9746. }
  9747. module.inject.result(result, resultIndex);
  9748. resultIndex++;
  9749. });
  9750. }
  9751. return results;
  9752. }
  9753. },
  9754. save: {
  9755. results: function(results) {
  9756. module.verbose('Saving current search results to metadata', results);
  9757. $module.data(metadata.results, results);
  9758. }
  9759. },
  9760. write: {
  9761. cache: function(name, value) {
  9762. var
  9763. cache = ($module.data(metadata.cache) !== undefined)
  9764. ? $module.data(metadata.cache)
  9765. : {}
  9766. ;
  9767. if(settings.cache) {
  9768. module.verbose('Writing generated html to cache', name, value);
  9769. cache[name] = value;
  9770. $module
  9771. .data(metadata.cache, cache)
  9772. ;
  9773. }
  9774. }
  9775. },
  9776. addResults: function(html) {
  9777. if( $.isFunction(settings.onResultsAdd) ) {
  9778. if( settings.onResultsAdd.call($results, html) === false ) {
  9779. module.debug('onResultsAdd callback cancelled default action');
  9780. return false;
  9781. }
  9782. }
  9783. if(html) {
  9784. $results
  9785. .html(html)
  9786. ;
  9787. module.refreshResults();
  9788. if(settings.selectFirstResult) {
  9789. module.select.firstResult();
  9790. }
  9791. module.showResults();
  9792. }
  9793. else {
  9794. module.hideResults(function() {
  9795. $results.empty();
  9796. });
  9797. }
  9798. },
  9799. showResults: function(callback) {
  9800. callback = $.isFunction(callback)
  9801. ? callback
  9802. : function(){}
  9803. ;
  9804. if(resultsDismissed) {
  9805. return;
  9806. }
  9807. if(!module.is.visible() && module.has.results()) {
  9808. if( module.can.transition() ) {
  9809. module.debug('Showing results with css animations');
  9810. $results
  9811. .transition({
  9812. animation : settings.transition + ' in',
  9813. debug : settings.debug,
  9814. verbose : settings.verbose,
  9815. duration : settings.duration,
  9816. onShow : function() {
  9817. var $firstResult = $module.find(selector.result).eq(0);
  9818. if($firstResult.length > 0) {
  9819. module.ensureVisible($firstResult);
  9820. }
  9821. },
  9822. onComplete : function() {
  9823. callback();
  9824. },
  9825. queue : true
  9826. })
  9827. ;
  9828. }
  9829. else {
  9830. module.debug('Showing results with javascript');
  9831. $results
  9832. .stop()
  9833. .fadeIn(settings.duration, settings.easing)
  9834. ;
  9835. }
  9836. settings.onResultsOpen.call($results);
  9837. }
  9838. },
  9839. hideResults: function(callback) {
  9840. callback = $.isFunction(callback)
  9841. ? callback
  9842. : function(){}
  9843. ;
  9844. if( module.is.visible() ) {
  9845. if( module.can.transition() ) {
  9846. module.debug('Hiding results with css animations');
  9847. $results
  9848. .transition({
  9849. animation : settings.transition + ' out',
  9850. debug : settings.debug,
  9851. verbose : settings.verbose,
  9852. duration : settings.duration,
  9853. onComplete : function() {
  9854. callback();
  9855. },
  9856. queue : true
  9857. })
  9858. ;
  9859. }
  9860. else {
  9861. module.debug('Hiding results with javascript');
  9862. $results
  9863. .stop()
  9864. .fadeOut(settings.duration, settings.easing)
  9865. ;
  9866. }
  9867. settings.onResultsClose.call($results);
  9868. }
  9869. },
  9870. generateResults: function(response) {
  9871. module.debug('Generating html from response', response);
  9872. var
  9873. template = settings.templates[settings.type],
  9874. isProperObject = ($.isPlainObject(response[fields.results]) && !$.isEmptyObject(response[fields.results])),
  9875. isProperArray = (Array.isArray(response[fields.results]) && response[fields.results].length > 0),
  9876. html = ''
  9877. ;
  9878. if(isProperObject || isProperArray ) {
  9879. if(settings.maxResults > 0) {
  9880. if(isProperObject) {
  9881. if(settings.type == 'standard') {
  9882. module.error(error.maxResults);
  9883. }
  9884. }
  9885. else {
  9886. response[fields.results] = response[fields.results].slice(0, settings.maxResults);
  9887. }
  9888. }
  9889. if($.isFunction(template)) {
  9890. html = template(response, fields, settings.preserveHTML);
  9891. }
  9892. else {
  9893. module.error(error.noTemplate, false);
  9894. }
  9895. }
  9896. else if(settings.showNoResults) {
  9897. html = module.displayMessage(error.noResults, 'empty', error.noResultsHeader);
  9898. }
  9899. settings.onResults.call(element, response);
  9900. return html;
  9901. },
  9902. displayMessage: function(text, type, header) {
  9903. type = type || 'standard';
  9904. module.debug('Displaying message', text, type, header);
  9905. module.addResults( settings.templates.message(text, type, header) );
  9906. return settings.templates.message(text, type, header);
  9907. },
  9908. setting: function(name, value) {
  9909. if( $.isPlainObject(name) ) {
  9910. $.extend(true, settings, name);
  9911. }
  9912. else if(value !== undefined) {
  9913. settings[name] = value;
  9914. }
  9915. else {
  9916. return settings[name];
  9917. }
  9918. },
  9919. internal: function(name, value) {
  9920. if( $.isPlainObject(name) ) {
  9921. $.extend(true, module, name);
  9922. }
  9923. else if(value !== undefined) {
  9924. module[name] = value;
  9925. }
  9926. else {
  9927. return module[name];
  9928. }
  9929. },
  9930. debug: function() {
  9931. if(!settings.silent && settings.debug) {
  9932. if(settings.performance) {
  9933. module.performance.log(arguments);
  9934. }
  9935. else {
  9936. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  9937. module.debug.apply(console, arguments);
  9938. }
  9939. }
  9940. },
  9941. verbose: function() {
  9942. if(!settings.silent && settings.verbose && settings.debug) {
  9943. if(settings.performance) {
  9944. module.performance.log(arguments);
  9945. }
  9946. else {
  9947. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  9948. module.verbose.apply(console, arguments);
  9949. }
  9950. }
  9951. },
  9952. error: function() {
  9953. if(!settings.silent) {
  9954. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  9955. module.error.apply(console, arguments);
  9956. }
  9957. },
  9958. performance: {
  9959. log: function(message) {
  9960. var
  9961. currentTime,
  9962. executionTime,
  9963. previousTime
  9964. ;
  9965. if(settings.performance) {
  9966. currentTime = new Date().getTime();
  9967. previousTime = time || currentTime;
  9968. executionTime = currentTime - previousTime;
  9969. time = currentTime;
  9970. performance.push({
  9971. 'Name' : message[0],
  9972. 'Arguments' : [].slice.call(message, 1) || '',
  9973. 'Element' : element,
  9974. 'Execution Time' : executionTime
  9975. });
  9976. }
  9977. clearTimeout(module.performance.timer);
  9978. module.performance.timer = setTimeout(module.performance.display, 500);
  9979. },
  9980. display: function() {
  9981. var
  9982. title = settings.name + ':',
  9983. totalTime = 0
  9984. ;
  9985. time = false;
  9986. clearTimeout(module.performance.timer);
  9987. $.each(performance, function(index, data) {
  9988. totalTime += data['Execution Time'];
  9989. });
  9990. title += ' ' + totalTime + 'ms';
  9991. if(moduleSelector) {
  9992. title += ' \'' + moduleSelector + '\'';
  9993. }
  9994. if($allModules.length > 1) {
  9995. title += ' ' + '(' + $allModules.length + ')';
  9996. }
  9997. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  9998. console.groupCollapsed(title);
  9999. if(console.table) {
  10000. console.table(performance);
  10001. }
  10002. else {
  10003. $.each(performance, function(index, data) {
  10004. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  10005. });
  10006. }
  10007. console.groupEnd();
  10008. }
  10009. performance = [];
  10010. }
  10011. },
  10012. invoke: function(query, passedArguments, context) {
  10013. var
  10014. object = instance,
  10015. maxDepth,
  10016. found,
  10017. response
  10018. ;
  10019. passedArguments = passedArguments || queryArguments;
  10020. context = element || context;
  10021. if(typeof query == 'string' && object !== undefined) {
  10022. query = query.split(/[\. ]/);
  10023. maxDepth = query.length - 1;
  10024. $.each(query, function(depth, value) {
  10025. var camelCaseValue = (depth != maxDepth)
  10026. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  10027. : query
  10028. ;
  10029. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  10030. object = object[camelCaseValue];
  10031. }
  10032. else if( object[camelCaseValue] !== undefined ) {
  10033. found = object[camelCaseValue];
  10034. return false;
  10035. }
  10036. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  10037. object = object[value];
  10038. }
  10039. else if( object[value] !== undefined ) {
  10040. found = object[value];
  10041. return false;
  10042. }
  10043. else {
  10044. return false;
  10045. }
  10046. });
  10047. }
  10048. if( $.isFunction( found ) ) {
  10049. response = found.apply(context, passedArguments);
  10050. }
  10051. else if(found !== undefined) {
  10052. response = found;
  10053. }
  10054. if(Array.isArray(returnedValue)) {
  10055. returnedValue.push(response);
  10056. }
  10057. else if(returnedValue !== undefined) {
  10058. returnedValue = [returnedValue, response];
  10059. }
  10060. else if(response !== undefined) {
  10061. returnedValue = response;
  10062. }
  10063. return found;
  10064. }
  10065. };
  10066. if(methodInvoked) {
  10067. if(instance === undefined) {
  10068. module.initialize();
  10069. }
  10070. module.invoke(query);
  10071. }
  10072. else {
  10073. if(instance !== undefined) {
  10074. instance.invoke('destroy');
  10075. }
  10076. module.initialize();
  10077. }
  10078. })
  10079. ;
  10080. return (returnedValue !== undefined)
  10081. ? returnedValue
  10082. : this
  10083. ;
  10084. };
  10085. $.fn.search.settings = {
  10086. name : 'Search',
  10087. namespace : 'search',
  10088. silent : false,
  10089. debug : false,
  10090. verbose : false,
  10091. performance : true,
  10092. // template to use (specified in settings.templates)
  10093. type : 'standard',
  10094. // minimum characters required to search
  10095. minCharacters : 1,
  10096. // whether to select first result after searching automatically
  10097. selectFirstResult : false,
  10098. // API config
  10099. apiSettings : false,
  10100. // object to search
  10101. source : false,
  10102. // Whether search should query current term on focus
  10103. searchOnFocus : true,
  10104. // fields to search
  10105. searchFields : [
  10106. 'id',
  10107. 'title',
  10108. 'description'
  10109. ],
  10110. // field to display in standard results template
  10111. displayField : '',
  10112. // search anywhere in value (set to 'exact' to require exact matches
  10113. fullTextSearch : 'exact',
  10114. // match results also if they contain diacritics of the same base character (for example searching for "a" will also match "á" or "â" or "à", etc...)
  10115. ignoreDiacritics : false,
  10116. // whether to add events to prompt automatically
  10117. automatic : true,
  10118. // delay before hiding menu after blur
  10119. hideDelay : 0,
  10120. // delay before searching
  10121. searchDelay : 200,
  10122. // maximum results returned from search
  10123. maxResults : 7,
  10124. // whether to store lookups in local cache
  10125. cache : true,
  10126. // whether no results errors should be shown
  10127. showNoResults : true,
  10128. // preserve possible html of resultset values
  10129. preserveHTML : true,
  10130. // transition settings
  10131. transition : 'scale',
  10132. duration : 200,
  10133. easing : 'easeOutExpo',
  10134. // callbacks
  10135. onSelect : false,
  10136. onResultsAdd : false,
  10137. onSearchQuery : function(query){},
  10138. onResults : function(response){},
  10139. onResultsOpen : function(){},
  10140. onResultsClose : function(){},
  10141. className: {
  10142. animating : 'animating',
  10143. active : 'active',
  10144. empty : 'empty',
  10145. focus : 'focus',
  10146. hidden : 'hidden',
  10147. loading : 'loading',
  10148. results : 'results',
  10149. pressed : 'down'
  10150. },
  10151. error : {
  10152. source : 'Cannot search. No source used, and Semantic API module was not included',
  10153. noResultsHeader : 'No Results',
  10154. noResults : 'Your search returned no results',
  10155. logging : 'Error in debug logging, exiting.',
  10156. noEndpoint : 'No search endpoint was specified',
  10157. noTemplate : 'A valid template name was not specified.',
  10158. oldSearchSyntax : 'searchFullText setting has been renamed fullTextSearch for consistency, please adjust your settings.',
  10159. serverError : 'There was an issue querying the server.',
  10160. maxResults : 'Results must be an array to use maxResults setting',
  10161. method : 'The method you called is not defined.',
  10162. noNormalize : '"ignoreDiacritics" setting will be ignored. Browser does not support String().normalize(). You may consider including <https://cdn.jsdelivr.net/npm/unorm@1.4.1/lib/unorm.min.js> as a polyfill.'
  10163. },
  10164. metadata: {
  10165. cache : 'cache',
  10166. results : 'results',
  10167. result : 'result'
  10168. },
  10169. regExp: {
  10170. escape : /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,
  10171. beginsWith : '(?:\s|^)'
  10172. },
  10173. // maps api response attributes to internal representation
  10174. fields: {
  10175. categories : 'results', // array of categories (category view)
  10176. categoryName : 'name', // name of category (category view)
  10177. categoryResults : 'results', // array of results (category view)
  10178. description : 'description', // result description
  10179. image : 'image', // result image
  10180. price : 'price', // result price
  10181. results : 'results', // array of results (standard)
  10182. title : 'title', // result title
  10183. url : 'url', // result url
  10184. action : 'action', // "view more" object name
  10185. actionText : 'text', // "view more" text
  10186. actionURL : 'url' // "view more" url
  10187. },
  10188. selector : {
  10189. prompt : '.prompt',
  10190. searchButton : '.search.button',
  10191. results : '.results',
  10192. message : '.results > .message',
  10193. category : '.category',
  10194. result : '.result',
  10195. title : '.title, .name'
  10196. },
  10197. templates: {
  10198. escape: function(string, preserveHTML) {
  10199. if (preserveHTML){
  10200. return string;
  10201. }
  10202. var
  10203. badChars = /[<>"'`]/g,
  10204. shouldEscape = /[&<>"'`]/,
  10205. escape = {
  10206. "<": "&lt;",
  10207. ">": "&gt;",
  10208. '"': "&quot;",
  10209. "'": "&#x27;",
  10210. "`": "&#x60;"
  10211. },
  10212. escapedChar = function(chr) {
  10213. return escape[chr];
  10214. }
  10215. ;
  10216. if(shouldEscape.test(string)) {
  10217. string = string.replace(/&(?![a-z0-9#]{1,6};)/, "&amp;");
  10218. return string.replace(badChars, escapedChar);
  10219. }
  10220. return string;
  10221. },
  10222. message: function(message, type, header) {
  10223. var
  10224. html = ''
  10225. ;
  10226. if(message !== undefined && type !== undefined) {
  10227. html += ''
  10228. + '<div class="message ' + type + '">'
  10229. ;
  10230. if(header) {
  10231. html += ''
  10232. + '<div class="header">' + header + '</div>'
  10233. ;
  10234. }
  10235. html += ' <div class="description">' + message + '</div>';
  10236. html += '</div>';
  10237. }
  10238. return html;
  10239. },
  10240. category: function(response, fields, preserveHTML) {
  10241. var
  10242. html = '',
  10243. escape = $.fn.search.settings.templates.escape
  10244. ;
  10245. if(response[fields.categoryResults] !== undefined) {
  10246. // each category
  10247. $.each(response[fields.categoryResults], function(index, category) {
  10248. if(category[fields.results] !== undefined && category.results.length > 0) {
  10249. html += '<div class="category">';
  10250. if(category[fields.categoryName] !== undefined) {
  10251. html += '<div class="name">' + escape(category[fields.categoryName], preserveHTML) + '</div>';
  10252. }
  10253. // each item inside category
  10254. html += '<div class="results">';
  10255. $.each(category.results, function(index, result) {
  10256. if(result[fields.url]) {
  10257. html += '<a class="result" href="' + result[fields.url].replace(/"/g,"") + '">';
  10258. }
  10259. else {
  10260. html += '<a class="result">';
  10261. }
  10262. if(result[fields.image] !== undefined) {
  10263. html += ''
  10264. + '<div class="image">'
  10265. + ' <img src="' + result[fields.image].replace(/"/g,"") + '">'
  10266. + '</div>'
  10267. ;
  10268. }
  10269. html += '<div class="content">';
  10270. if(result[fields.price] !== undefined) {
  10271. html += '<div class="price">' + escape(result[fields.price], preserveHTML) + '</div>';
  10272. }
  10273. if(result[fields.title] !== undefined) {
  10274. html += '<div class="title">' + escape(result[fields.title], preserveHTML) + '</div>';
  10275. }
  10276. if(result[fields.description] !== undefined) {
  10277. html += '<div class="description">' + escape(result[fields.description], preserveHTML) + '</div>';
  10278. }
  10279. html += ''
  10280. + '</div>'
  10281. ;
  10282. html += '</a>';
  10283. });
  10284. html += '</div>';
  10285. html += ''
  10286. + '</div>'
  10287. ;
  10288. }
  10289. });
  10290. if(response[fields.action]) {
  10291. if(fields.actionURL === false) {
  10292. html += ''
  10293. + '<div class="action">'
  10294. + escape(response[fields.action][fields.actionText], preserveHTML)
  10295. + '</div>';
  10296. } else {
  10297. html += ''
  10298. + '<a href="' + response[fields.action][fields.actionURL].replace(/"/g,"") + '" class="action">'
  10299. + escape(response[fields.action][fields.actionText], preserveHTML)
  10300. + '</a>';
  10301. }
  10302. }
  10303. return html;
  10304. }
  10305. return false;
  10306. },
  10307. standard: function(response, fields, preserveHTML) {
  10308. var
  10309. html = '',
  10310. escape = $.fn.search.settings.templates.escape
  10311. ;
  10312. if(response[fields.results] !== undefined) {
  10313. // each result
  10314. $.each(response[fields.results], function(index, result) {
  10315. if(result[fields.url]) {
  10316. html += '<a class="result" href="' + result[fields.url].replace(/"/g,"") + '">';
  10317. }
  10318. else {
  10319. html += '<a class="result">';
  10320. }
  10321. if(result[fields.image] !== undefined) {
  10322. html += ''
  10323. + '<div class="image">'
  10324. + ' <img src="' + result[fields.image].replace(/"/g,"") + '">'
  10325. + '</div>'
  10326. ;
  10327. }
  10328. html += '<div class="content">';
  10329. if(result[fields.price] !== undefined) {
  10330. html += '<div class="price">' + escape(result[fields.price], preserveHTML) + '</div>';
  10331. }
  10332. if(result[fields.title] !== undefined) {
  10333. html += '<div class="title">' + escape(result[fields.title], preserveHTML) + '</div>';
  10334. }
  10335. if(result[fields.description] !== undefined) {
  10336. html += '<div class="description">' + escape(result[fields.description], preserveHTML) + '</div>';
  10337. }
  10338. html += ''
  10339. + '</div>'
  10340. ;
  10341. html += '</a>';
  10342. });
  10343. if(response[fields.action]) {
  10344. if(fields.actionURL === false) {
  10345. html += ''
  10346. + '<div class="action">'
  10347. + escape(response[fields.action][fields.actionText], preserveHTML)
  10348. + '</div>';
  10349. } else {
  10350. html += ''
  10351. + '<a href="' + response[fields.action][fields.actionURL].replace(/"/g,"") + '" class="action">'
  10352. + escape(response[fields.action][fields.actionText], preserveHTML)
  10353. + '</a>';
  10354. }
  10355. }
  10356. return html;
  10357. }
  10358. return false;
  10359. }
  10360. }
  10361. };
  10362. })( jQuery, window, document );
  10363. /*!
  10364. * # Fomantic-UI - Tab
  10365. * http://github.com/fomantic/Fomantic-UI/
  10366. *
  10367. *
  10368. * Released under the MIT license
  10369. * http://opensource.org/licenses/MIT
  10370. *
  10371. */
  10372. ;(function ($, window, document, undefined) {
  10373. 'use strict';
  10374. $.isWindow = $.isWindow || function(obj) {
  10375. return obj != null && obj === obj.window;
  10376. };
  10377. $.isFunction = $.isFunction || function(obj) {
  10378. return typeof obj === "function" && typeof obj.nodeType !== "number";
  10379. };
  10380. window = (typeof window != 'undefined' && window.Math == Math)
  10381. ? window
  10382. : (typeof self != 'undefined' && self.Math == Math)
  10383. ? self
  10384. : Function('return this')()
  10385. ;
  10386. $.fn.tab = function(parameters) {
  10387. var
  10388. // use window context if none specified
  10389. $allModules = $.isFunction(this)
  10390. ? $(window)
  10391. : $(this),
  10392. moduleSelector = $allModules.selector || '',
  10393. time = new Date().getTime(),
  10394. performance = [],
  10395. query = arguments[0],
  10396. methodInvoked = (typeof query == 'string'),
  10397. queryArguments = [].slice.call(arguments, 1),
  10398. initializedHistory = false,
  10399. returnedValue
  10400. ;
  10401. $allModules
  10402. .each(function() {
  10403. var
  10404. settings = ( $.isPlainObject(parameters) )
  10405. ? $.extend(true, {}, $.fn.tab.settings, parameters)
  10406. : $.extend({}, $.fn.tab.settings),
  10407. className = settings.className,
  10408. metadata = settings.metadata,
  10409. selector = settings.selector,
  10410. error = settings.error,
  10411. regExp = settings.regExp,
  10412. eventNamespace = '.' + settings.namespace,
  10413. moduleNamespace = 'module-' + settings.namespace,
  10414. $module = $(this),
  10415. $context,
  10416. $tabs,
  10417. cache = {},
  10418. firstLoad = true,
  10419. recursionDepth = 0,
  10420. element = this,
  10421. instance = $module.data(moduleNamespace),
  10422. activeTabPath,
  10423. parameterArray,
  10424. module,
  10425. historyEvent
  10426. ;
  10427. module = {
  10428. initialize: function() {
  10429. module.debug('Initializing tab menu item', $module);
  10430. module.fix.callbacks();
  10431. module.determineTabs();
  10432. module.debug('Determining tabs', settings.context, $tabs);
  10433. // set up automatic routing
  10434. if(settings.auto) {
  10435. module.set.auto();
  10436. }
  10437. module.bind.events();
  10438. if(settings.history && !initializedHistory) {
  10439. module.initializeHistory();
  10440. initializedHistory = true;
  10441. }
  10442. if(settings.autoTabActivation && instance === undefined && module.determine.activeTab() == null) {
  10443. module.debug('No active tab detected, setting first tab active', module.get.initialPath());
  10444. module.changeTab(settings.autoTabActivation === true ? module.get.initialPath() : settings.autoTabActivation);
  10445. };
  10446. module.instantiate();
  10447. },
  10448. instantiate: function () {
  10449. module.verbose('Storing instance of module', module);
  10450. instance = module;
  10451. $module
  10452. .data(moduleNamespace, module)
  10453. ;
  10454. },
  10455. destroy: function() {
  10456. module.debug('Destroying tabs', $module);
  10457. $module
  10458. .removeData(moduleNamespace)
  10459. .off(eventNamespace)
  10460. ;
  10461. },
  10462. bind: {
  10463. events: function() {
  10464. // if using $.tab don't add events
  10465. if( !$.isWindow( element ) ) {
  10466. module.debug('Attaching tab activation events to element', $module);
  10467. $module
  10468. .on('click' + eventNamespace, module.event.click)
  10469. ;
  10470. }
  10471. }
  10472. },
  10473. determineTabs: function() {
  10474. var
  10475. $reference
  10476. ;
  10477. // determine tab context
  10478. if(settings.context === 'parent') {
  10479. if($module.closest(selector.ui).length > 0) {
  10480. $reference = $module.closest(selector.ui);
  10481. module.verbose('Using closest UI element as parent', $reference);
  10482. }
  10483. else {
  10484. $reference = $module;
  10485. }
  10486. $context = $reference.parent();
  10487. module.verbose('Determined parent element for creating context', $context);
  10488. }
  10489. else if(settings.context) {
  10490. $context = $(settings.context);
  10491. module.verbose('Using selector for tab context', settings.context, $context);
  10492. }
  10493. else {
  10494. $context = $('body');
  10495. }
  10496. // find tabs
  10497. if(settings.childrenOnly) {
  10498. $tabs = $context.children(selector.tabs);
  10499. module.debug('Searching tab context children for tabs', $context, $tabs);
  10500. }
  10501. else {
  10502. $tabs = $context.find(selector.tabs);
  10503. module.debug('Searching tab context for tabs', $context, $tabs);
  10504. }
  10505. },
  10506. fix: {
  10507. callbacks: function() {
  10508. if( $.isPlainObject(parameters) && (parameters.onTabLoad || parameters.onTabInit) ) {
  10509. if(parameters.onTabLoad) {
  10510. parameters.onLoad = parameters.onTabLoad;
  10511. delete parameters.onTabLoad;
  10512. module.error(error.legacyLoad, parameters.onLoad);
  10513. }
  10514. if(parameters.onTabInit) {
  10515. parameters.onFirstLoad = parameters.onTabInit;
  10516. delete parameters.onTabInit;
  10517. module.error(error.legacyInit, parameters.onFirstLoad);
  10518. }
  10519. settings = $.extend(true, {}, $.fn.tab.settings, parameters);
  10520. }
  10521. }
  10522. },
  10523. initializeHistory: function() {
  10524. module.debug('Initializing page state');
  10525. if( $.address === undefined ) {
  10526. module.error(error.state);
  10527. return false;
  10528. }
  10529. else {
  10530. if(settings.historyType == 'state') {
  10531. module.debug('Using HTML5 to manage state');
  10532. if(settings.path !== false) {
  10533. $.address
  10534. .history(true)
  10535. .state(settings.path)
  10536. ;
  10537. }
  10538. else {
  10539. module.error(error.path);
  10540. return false;
  10541. }
  10542. }
  10543. $.address
  10544. .bind('change', module.event.history.change)
  10545. ;
  10546. }
  10547. },
  10548. event: {
  10549. click: function(event) {
  10550. var
  10551. tabPath = $(this).data(metadata.tab)
  10552. ;
  10553. if(tabPath !== undefined) {
  10554. if(settings.history) {
  10555. module.verbose('Updating page state', event);
  10556. $.address.value(tabPath);
  10557. }
  10558. else {
  10559. module.verbose('Changing tab', event);
  10560. module.changeTab(tabPath);
  10561. }
  10562. event.preventDefault();
  10563. }
  10564. else {
  10565. module.debug('No tab specified');
  10566. }
  10567. },
  10568. history: {
  10569. change: function(event) {
  10570. var
  10571. tabPath = event.pathNames.join('/') || module.get.initialPath(),
  10572. pageTitle = settings.templates.determineTitle(tabPath) || false
  10573. ;
  10574. module.performance.display();
  10575. module.debug('History change event', tabPath, event);
  10576. historyEvent = event;
  10577. if(tabPath !== undefined) {
  10578. module.changeTab(tabPath);
  10579. }
  10580. if(pageTitle) {
  10581. $.address.title(pageTitle);
  10582. }
  10583. }
  10584. }
  10585. },
  10586. refresh: function() {
  10587. if(activeTabPath) {
  10588. module.debug('Refreshing tab', activeTabPath);
  10589. module.changeTab(activeTabPath);
  10590. }
  10591. },
  10592. cache: {
  10593. read: function(cacheKey) {
  10594. return (cacheKey !== undefined)
  10595. ? cache[cacheKey]
  10596. : false
  10597. ;
  10598. },
  10599. add: function(cacheKey, content) {
  10600. cacheKey = cacheKey || activeTabPath;
  10601. module.debug('Adding cached content for', cacheKey);
  10602. cache[cacheKey] = content;
  10603. },
  10604. remove: function(cacheKey) {
  10605. cacheKey = cacheKey || activeTabPath;
  10606. module.debug('Removing cached content for', cacheKey);
  10607. delete cache[cacheKey];
  10608. }
  10609. },
  10610. escape: {
  10611. string: function(text) {
  10612. text = String(text);
  10613. return text.replace(regExp.escape, '\\$&');
  10614. }
  10615. },
  10616. set: {
  10617. auto: function() {
  10618. var
  10619. url = (typeof settings.path == 'string')
  10620. ? settings.path.replace(/\/$/, '') + '/{$tab}'
  10621. : '/{$tab}'
  10622. ;
  10623. module.verbose('Setting up automatic tab retrieval from server', url);
  10624. if($.isPlainObject(settings.apiSettings)) {
  10625. settings.apiSettings.url = url;
  10626. }
  10627. else {
  10628. settings.apiSettings = {
  10629. url: url
  10630. };
  10631. }
  10632. },
  10633. loading: function(tabPath) {
  10634. var
  10635. $tab = module.get.tabElement(tabPath),
  10636. isLoading = $tab.hasClass(className.loading)
  10637. ;
  10638. if(!isLoading) {
  10639. module.verbose('Setting loading state for', $tab);
  10640. $tab
  10641. .addClass(className.loading)
  10642. .siblings($tabs)
  10643. .removeClass(className.active + ' ' + className.loading)
  10644. ;
  10645. if($tab.length > 0) {
  10646. settings.onRequest.call($tab[0], tabPath);
  10647. }
  10648. }
  10649. },
  10650. state: function(state) {
  10651. $.address.value(state);
  10652. }
  10653. },
  10654. changeTab: function(tabPath) {
  10655. var
  10656. pushStateAvailable = (window.history && window.history.pushState),
  10657. shouldIgnoreLoad = (pushStateAvailable && settings.ignoreFirstLoad && firstLoad),
  10658. remoteContent = (settings.auto || $.isPlainObject(settings.apiSettings) ),
  10659. // only add default path if not remote content
  10660. pathArray = (remoteContent && !shouldIgnoreLoad)
  10661. ? module.utilities.pathToArray(tabPath)
  10662. : module.get.defaultPathArray(tabPath)
  10663. ;
  10664. tabPath = module.utilities.arrayToPath(pathArray);
  10665. $.each(pathArray, function(index, tab) {
  10666. var
  10667. currentPathArray = pathArray.slice(0, index + 1),
  10668. currentPath = module.utilities.arrayToPath(currentPathArray),
  10669. isTab = module.is.tab(currentPath),
  10670. isLastIndex = (index + 1 == pathArray.length),
  10671. $tab = module.get.tabElement(currentPath),
  10672. $anchor,
  10673. nextPathArray,
  10674. nextPath,
  10675. isLastTab
  10676. ;
  10677. module.verbose('Looking for tab', tab);
  10678. if(isTab) {
  10679. module.verbose('Tab was found', tab);
  10680. // scope up
  10681. activeTabPath = currentPath;
  10682. parameterArray = module.utilities.filterArray(pathArray, currentPathArray);
  10683. if(isLastIndex) {
  10684. isLastTab = true;
  10685. }
  10686. else {
  10687. nextPathArray = pathArray.slice(0, index + 2);
  10688. nextPath = module.utilities.arrayToPath(nextPathArray);
  10689. isLastTab = ( !module.is.tab(nextPath) );
  10690. if(isLastTab) {
  10691. module.verbose('Tab parameters found', nextPathArray);
  10692. }
  10693. }
  10694. if(isLastTab && remoteContent) {
  10695. if(!shouldIgnoreLoad) {
  10696. module.activate.navigation(currentPath);
  10697. module.fetch.content(currentPath, tabPath);
  10698. }
  10699. else {
  10700. module.debug('Ignoring remote content on first tab load', currentPath);
  10701. firstLoad = false;
  10702. module.cache.add(tabPath, $tab.html());
  10703. module.activate.all(currentPath);
  10704. settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent);
  10705. settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent);
  10706. }
  10707. return false;
  10708. }
  10709. else {
  10710. module.debug('Opened local tab', currentPath);
  10711. module.activate.all(currentPath);
  10712. if( !module.cache.read(currentPath) ) {
  10713. module.cache.add(currentPath, true);
  10714. module.debug('First time tab loaded calling tab init');
  10715. settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent);
  10716. }
  10717. settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent);
  10718. }
  10719. }
  10720. else if(tabPath.search('/') == -1 && tabPath !== '') {
  10721. // look for in page anchor
  10722. tabPath = module.escape.string(tabPath);
  10723. $anchor = $('#' + tabPath + ', a[name="' + tabPath + '"]');
  10724. currentPath = $anchor.closest('[data-tab]').data(metadata.tab);
  10725. $tab = module.get.tabElement(currentPath);
  10726. // if anchor exists use parent tab
  10727. if($anchor && $anchor.length > 0 && currentPath) {
  10728. module.debug('Anchor link used, opening parent tab', $tab, $anchor);
  10729. if( !$tab.hasClass(className.active) ) {
  10730. setTimeout(function() {
  10731. module.scrollTo($anchor);
  10732. }, 0);
  10733. }
  10734. module.activate.all(currentPath);
  10735. if( !module.cache.read(currentPath) ) {
  10736. module.cache.add(currentPath, true);
  10737. module.debug('First time tab loaded calling tab init');
  10738. settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent);
  10739. }
  10740. settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent);
  10741. return false;
  10742. }
  10743. }
  10744. else {
  10745. module.error(error.missingTab, $module, $context, currentPath);
  10746. return false;
  10747. }
  10748. });
  10749. },
  10750. scrollTo: function($element) {
  10751. var
  10752. scrollOffset = ($element && $element.length > 0)
  10753. ? $element.offset().top
  10754. : false
  10755. ;
  10756. if(scrollOffset !== false) {
  10757. module.debug('Forcing scroll to an in-page link in a hidden tab', scrollOffset, $element);
  10758. $(document).scrollTop(scrollOffset);
  10759. }
  10760. },
  10761. update: {
  10762. content: function(tabPath, html, evaluateScripts) {
  10763. var
  10764. $tab = module.get.tabElement(tabPath),
  10765. tab = $tab[0]
  10766. ;
  10767. evaluateScripts = (evaluateScripts !== undefined)
  10768. ? evaluateScripts
  10769. : settings.evaluateScripts
  10770. ;
  10771. if(typeof settings.cacheType == 'string' && settings.cacheType.toLowerCase() == 'dom' && typeof html !== 'string') {
  10772. $tab
  10773. .empty()
  10774. .append($(html).clone(true))
  10775. ;
  10776. }
  10777. else {
  10778. if(evaluateScripts) {
  10779. module.debug('Updating HTML and evaluating inline scripts', tabPath, html);
  10780. $tab.html(html);
  10781. }
  10782. else {
  10783. module.debug('Updating HTML', tabPath, html);
  10784. tab.innerHTML = html;
  10785. }
  10786. }
  10787. }
  10788. },
  10789. fetch: {
  10790. content: function(tabPath, fullTabPath) {
  10791. var
  10792. $tab = module.get.tabElement(tabPath),
  10793. apiSettings = {
  10794. dataType : 'html',
  10795. encodeParameters : false,
  10796. on : 'now',
  10797. cache : settings.alwaysRefresh,
  10798. headers : {
  10799. 'X-Remote': true
  10800. },
  10801. onSuccess : function(response) {
  10802. if(settings.cacheType == 'response') {
  10803. module.cache.add(fullTabPath, response);
  10804. }
  10805. module.update.content(tabPath, response);
  10806. if(tabPath == activeTabPath) {
  10807. module.debug('Content loaded', tabPath);
  10808. module.activate.tab(tabPath);
  10809. }
  10810. else {
  10811. module.debug('Content loaded in background', tabPath);
  10812. }
  10813. settings.onFirstLoad.call($tab[0], tabPath, parameterArray, historyEvent);
  10814. settings.onLoad.call($tab[0], tabPath, parameterArray, historyEvent);
  10815. if(settings.loadOnce) {
  10816. module.cache.add(fullTabPath, true);
  10817. }
  10818. else if(typeof settings.cacheType == 'string' && settings.cacheType.toLowerCase() == 'dom' && $tab.children().length > 0) {
  10819. setTimeout(function() {
  10820. var
  10821. $clone = $tab.children().clone(true)
  10822. ;
  10823. $clone = $clone.not('script');
  10824. module.cache.add(fullTabPath, $clone);
  10825. }, 0);
  10826. }
  10827. else {
  10828. module.cache.add(fullTabPath, $tab.html());
  10829. }
  10830. },
  10831. urlData: {
  10832. tab: fullTabPath
  10833. }
  10834. },
  10835. request = $tab.api('get request') || false,
  10836. existingRequest = ( request && request.state() === 'pending' ),
  10837. requestSettings,
  10838. cachedContent
  10839. ;
  10840. fullTabPath = fullTabPath || tabPath;
  10841. cachedContent = module.cache.read(fullTabPath);
  10842. if(settings.cache && cachedContent) {
  10843. module.activate.tab(tabPath);
  10844. module.debug('Adding cached content', fullTabPath);
  10845. if(!settings.loadOnce) {
  10846. if(settings.evaluateScripts == 'once') {
  10847. module.update.content(tabPath, cachedContent, false);
  10848. }
  10849. else {
  10850. module.update.content(tabPath, cachedContent);
  10851. }
  10852. }
  10853. settings.onLoad.call($tab[0], tabPath, parameterArray, historyEvent);
  10854. }
  10855. else if(existingRequest) {
  10856. module.set.loading(tabPath);
  10857. module.debug('Content is already loading', fullTabPath);
  10858. }
  10859. else if($.api !== undefined) {
  10860. requestSettings = $.extend(true, {}, settings.apiSettings, apiSettings);
  10861. module.debug('Retrieving remote content', fullTabPath, requestSettings);
  10862. module.set.loading(tabPath);
  10863. $tab.api(requestSettings);
  10864. }
  10865. else {
  10866. module.error(error.api);
  10867. }
  10868. }
  10869. },
  10870. activate: {
  10871. all: function(tabPath) {
  10872. module.activate.tab(tabPath);
  10873. module.activate.navigation(tabPath);
  10874. },
  10875. tab: function(tabPath) {
  10876. var
  10877. $tab = module.get.tabElement(tabPath),
  10878. $deactiveTabs = (settings.deactivate == 'siblings')
  10879. ? $tab.siblings($tabs)
  10880. : $tabs.not($tab),
  10881. isActive = $tab.hasClass(className.active)
  10882. ;
  10883. module.verbose('Showing tab content for', $tab);
  10884. if(!isActive) {
  10885. $tab
  10886. .addClass(className.active)
  10887. ;
  10888. $deactiveTabs
  10889. .removeClass(className.active + ' ' + className.loading)
  10890. ;
  10891. if($tab.length > 0) {
  10892. settings.onVisible.call($tab[0], tabPath);
  10893. }
  10894. }
  10895. },
  10896. navigation: function(tabPath) {
  10897. var
  10898. $navigation = module.get.navElement(tabPath),
  10899. $deactiveNavigation = (settings.deactivate == 'siblings')
  10900. ? $navigation.siblings($allModules)
  10901. : $allModules.not($navigation),
  10902. isActive = $navigation.hasClass(className.active)
  10903. ;
  10904. module.verbose('Activating tab navigation for', $navigation, tabPath);
  10905. if(!isActive) {
  10906. $navigation
  10907. .addClass(className.active)
  10908. ;
  10909. $deactiveNavigation
  10910. .removeClass(className.active + ' ' + className.loading)
  10911. ;
  10912. }
  10913. }
  10914. },
  10915. deactivate: {
  10916. all: function() {
  10917. module.deactivate.navigation();
  10918. module.deactivate.tabs();
  10919. },
  10920. navigation: function() {
  10921. $allModules
  10922. .removeClass(className.active)
  10923. ;
  10924. },
  10925. tabs: function() {
  10926. $tabs
  10927. .removeClass(className.active + ' ' + className.loading)
  10928. ;
  10929. }
  10930. },
  10931. is: {
  10932. tab: function(tabName) {
  10933. return (tabName !== undefined)
  10934. ? ( module.get.tabElement(tabName).length > 0 )
  10935. : false
  10936. ;
  10937. }
  10938. },
  10939. get: {
  10940. initialPath: function() {
  10941. return $allModules.eq(0).data(metadata.tab) || $tabs.eq(0).data(metadata.tab);
  10942. },
  10943. path: function() {
  10944. return $.address.value();
  10945. },
  10946. // adds default tabs to tab path
  10947. defaultPathArray: function(tabPath) {
  10948. return module.utilities.pathToArray( module.get.defaultPath(tabPath) );
  10949. },
  10950. defaultPath: function(tabPath) {
  10951. var
  10952. $defaultNav = $allModules.filter('[data-' + metadata.tab + '^="' + module.escape.string(tabPath) + '/"]').eq(0),
  10953. defaultTab = $defaultNav.data(metadata.tab) || false
  10954. ;
  10955. if( defaultTab ) {
  10956. module.debug('Found default tab', defaultTab);
  10957. if(recursionDepth < settings.maxDepth) {
  10958. recursionDepth++;
  10959. return module.get.defaultPath(defaultTab);
  10960. }
  10961. module.error(error.recursion);
  10962. }
  10963. else {
  10964. module.debug('No default tabs found for', tabPath, $tabs);
  10965. }
  10966. recursionDepth = 0;
  10967. return tabPath;
  10968. },
  10969. navElement: function(tabPath) {
  10970. tabPath = tabPath || activeTabPath;
  10971. return $allModules.filter('[data-' + metadata.tab + '="' + module.escape.string(tabPath) + '"]');
  10972. },
  10973. tabElement: function(tabPath) {
  10974. var
  10975. $fullPathTab,
  10976. $simplePathTab,
  10977. tabPathArray,
  10978. lastTab
  10979. ;
  10980. tabPath = tabPath || activeTabPath;
  10981. tabPathArray = module.utilities.pathToArray(tabPath);
  10982. lastTab = module.utilities.last(tabPathArray);
  10983. $fullPathTab = $tabs.filter('[data-' + metadata.tab + '="' + module.escape.string(tabPath) + '"]');
  10984. $simplePathTab = $tabs.filter('[data-' + metadata.tab + '="' + module.escape.string(lastTab) + '"]');
  10985. return ($fullPathTab.length > 0)
  10986. ? $fullPathTab
  10987. : $simplePathTab
  10988. ;
  10989. },
  10990. tab: function() {
  10991. return activeTabPath;
  10992. }
  10993. },
  10994. determine: {
  10995. activeTab: function() {
  10996. var activeTab = null;
  10997. $tabs.each(function(_index, tab) {
  10998. var $tab = $(tab);
  10999. if( $tab.hasClass(className.active) ) {
  11000. var
  11001. tabPath = $(this).data(metadata.tab),
  11002. $anchor = $allModules.filter('[data-' + metadata.tab + '="' + module.escape.string(tabPath) + '"]')
  11003. ;
  11004. if( $anchor.hasClass(className.active) ) {
  11005. activeTab = tabPath;
  11006. }
  11007. }
  11008. });
  11009. return activeTab;
  11010. }
  11011. },
  11012. utilities: {
  11013. filterArray: function(keepArray, removeArray) {
  11014. return $.grep(keepArray, function(keepValue) {
  11015. return ( $.inArray(keepValue, removeArray) == -1);
  11016. });
  11017. },
  11018. last: function(array) {
  11019. return Array.isArray(array)
  11020. ? array[ array.length - 1]
  11021. : false
  11022. ;
  11023. },
  11024. pathToArray: function(pathName) {
  11025. if(pathName === undefined) {
  11026. pathName = activeTabPath;
  11027. }
  11028. return typeof pathName == 'string'
  11029. ? pathName.split('/')
  11030. : [pathName]
  11031. ;
  11032. },
  11033. arrayToPath: function(pathArray) {
  11034. return Array.isArray(pathArray)
  11035. ? pathArray.join('/')
  11036. : false
  11037. ;
  11038. }
  11039. },
  11040. setting: function(name, value) {
  11041. module.debug('Changing setting', name, value);
  11042. if( $.isPlainObject(name) ) {
  11043. $.extend(true, settings, name);
  11044. }
  11045. else if(value !== undefined) {
  11046. if($.isPlainObject(settings[name])) {
  11047. $.extend(true, settings[name], value);
  11048. }
  11049. else {
  11050. settings[name] = value;
  11051. }
  11052. }
  11053. else {
  11054. return settings[name];
  11055. }
  11056. },
  11057. internal: function(name, value) {
  11058. if( $.isPlainObject(name) ) {
  11059. $.extend(true, module, name);
  11060. }
  11061. else if(value !== undefined) {
  11062. module[name] = value;
  11063. }
  11064. else {
  11065. return module[name];
  11066. }
  11067. },
  11068. debug: function() {
  11069. if(!settings.silent && settings.debug) {
  11070. if(settings.performance) {
  11071. module.performance.log(arguments);
  11072. }
  11073. else {
  11074. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  11075. module.debug.apply(console, arguments);
  11076. }
  11077. }
  11078. },
  11079. verbose: function() {
  11080. if(!settings.silent && settings.verbose && settings.debug) {
  11081. if(settings.performance) {
  11082. module.performance.log(arguments);
  11083. }
  11084. else {
  11085. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  11086. module.verbose.apply(console, arguments);
  11087. }
  11088. }
  11089. },
  11090. error: function() {
  11091. if(!settings.silent) {
  11092. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  11093. module.error.apply(console, arguments);
  11094. }
  11095. },
  11096. performance: {
  11097. log: function(message) {
  11098. var
  11099. currentTime,
  11100. executionTime,
  11101. previousTime
  11102. ;
  11103. if(settings.performance) {
  11104. currentTime = new Date().getTime();
  11105. previousTime = time || currentTime;
  11106. executionTime = currentTime - previousTime;
  11107. time = currentTime;
  11108. performance.push({
  11109. 'Name' : message[0],
  11110. 'Arguments' : [].slice.call(message, 1) || '',
  11111. 'Element' : element,
  11112. 'Execution Time' : executionTime
  11113. });
  11114. }
  11115. clearTimeout(module.performance.timer);
  11116. module.performance.timer = setTimeout(module.performance.display, 500);
  11117. },
  11118. display: function() {
  11119. var
  11120. title = settings.name + ':',
  11121. totalTime = 0
  11122. ;
  11123. time = false;
  11124. clearTimeout(module.performance.timer);
  11125. $.each(performance, function(index, data) {
  11126. totalTime += data['Execution Time'];
  11127. });
  11128. title += ' ' + totalTime + 'ms';
  11129. if(moduleSelector) {
  11130. title += ' \'' + moduleSelector + '\'';
  11131. }
  11132. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  11133. console.groupCollapsed(title);
  11134. if(console.table) {
  11135. console.table(performance);
  11136. }
  11137. else {
  11138. $.each(performance, function(index, data) {
  11139. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  11140. });
  11141. }
  11142. console.groupEnd();
  11143. }
  11144. performance = [];
  11145. }
  11146. },
  11147. invoke: function(query, passedArguments, context) {
  11148. var
  11149. object = instance,
  11150. maxDepth,
  11151. found,
  11152. response
  11153. ;
  11154. passedArguments = passedArguments || queryArguments;
  11155. context = element || context;
  11156. if(typeof query == 'string' && object !== undefined) {
  11157. query = query.split(/[\. ]/);
  11158. maxDepth = query.length - 1;
  11159. $.each(query, function(depth, value) {
  11160. var camelCaseValue = (depth != maxDepth)
  11161. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  11162. : query
  11163. ;
  11164. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  11165. object = object[camelCaseValue];
  11166. }
  11167. else if( object[camelCaseValue] !== undefined ) {
  11168. found = object[camelCaseValue];
  11169. return false;
  11170. }
  11171. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  11172. object = object[value];
  11173. }
  11174. else if( object[value] !== undefined ) {
  11175. found = object[value];
  11176. return false;
  11177. }
  11178. else {
  11179. module.error(error.method, query);
  11180. return false;
  11181. }
  11182. });
  11183. }
  11184. if ( $.isFunction( found ) ) {
  11185. response = found.apply(context, passedArguments);
  11186. }
  11187. else if(found !== undefined) {
  11188. response = found;
  11189. }
  11190. if(Array.isArray(returnedValue)) {
  11191. returnedValue.push(response);
  11192. }
  11193. else if(returnedValue !== undefined) {
  11194. returnedValue = [returnedValue, response];
  11195. }
  11196. else if(response !== undefined) {
  11197. returnedValue = response;
  11198. }
  11199. return found;
  11200. }
  11201. };
  11202. if(methodInvoked) {
  11203. if(instance === undefined) {
  11204. module.initialize();
  11205. }
  11206. module.invoke(query);
  11207. }
  11208. else {
  11209. if(instance !== undefined) {
  11210. instance.invoke('destroy');
  11211. }
  11212. module.initialize();
  11213. }
  11214. })
  11215. ;
  11216. return (returnedValue !== undefined)
  11217. ? returnedValue
  11218. : this
  11219. ;
  11220. };
  11221. // shortcut for tabbed content with no defined navigation
  11222. $.tab = function() {
  11223. $(window).tab.apply(this, arguments);
  11224. };
  11225. $.fn.tab.settings = {
  11226. name : 'Tab',
  11227. namespace : 'tab',
  11228. silent : false,
  11229. debug : false,
  11230. verbose : false,
  11231. performance : true,
  11232. auto : false, // uses pjax style endpoints fetching content from same url with remote-content headers
  11233. history : false, // use browser history
  11234. historyType : 'hash', // #/ or html5 state
  11235. path : false, // base path of url
  11236. context : false, // specify a context that tabs must appear inside
  11237. childrenOnly : false, // use only tabs that are children of context
  11238. maxDepth : 25, // max depth a tab can be nested
  11239. deactivate : 'siblings', // whether tabs should deactivate sibling menu elements or all elements initialized together
  11240. alwaysRefresh : false, // load tab content new every tab click
  11241. cache : true, // cache the content requests to pull locally
  11242. loadOnce : false, // Whether tab data should only be loaded once when using remote content
  11243. cacheType : 'response', // Whether to cache exact response, or to html cache contents after scripts execute
  11244. ignoreFirstLoad : false, // don't load remote content on first load
  11245. apiSettings : false, // settings for api call
  11246. evaluateScripts : 'once', // whether inline scripts should be parsed (true/false/once). Once will not re-evaluate on cached content
  11247. autoTabActivation: true, // whether a non existing active tab will auto activate the first available tab
  11248. onFirstLoad : function(tabPath, parameterArray, historyEvent) {}, // called first time loaded
  11249. onLoad : function(tabPath, parameterArray, historyEvent) {}, // called on every load
  11250. onVisible : function(tabPath, parameterArray, historyEvent) {}, // called every time tab visible
  11251. onRequest : function(tabPath, parameterArray, historyEvent) {}, // called ever time a tab beings loading remote content
  11252. templates : {
  11253. determineTitle: function(tabArray) {} // returns page title for path
  11254. },
  11255. error: {
  11256. api : 'You attempted to load content without API module',
  11257. method : 'The method you called is not defined',
  11258. missingTab : 'Activated tab cannot be found. Tabs are case-sensitive.',
  11259. noContent : 'The tab you specified is missing a content url.',
  11260. path : 'History enabled, but no path was specified',
  11261. recursion : 'Max recursive depth reached',
  11262. legacyInit : 'onTabInit has been renamed to onFirstLoad in 2.0, please adjust your code.',
  11263. legacyLoad : 'onTabLoad has been renamed to onLoad in 2.0. Please adjust your code',
  11264. state : 'History requires Asual\'s Address library <https://github.com/asual/jquery-address>'
  11265. },
  11266. regExp : {
  11267. escape : /[-[\]{}()*+?.,\\^$|#\s:=@]/g
  11268. },
  11269. metadata : {
  11270. tab : 'tab',
  11271. loaded : 'loaded',
  11272. promise: 'promise'
  11273. },
  11274. className : {
  11275. loading : 'loading',
  11276. active : 'active'
  11277. },
  11278. selector : {
  11279. tabs : '.ui.tab',
  11280. ui : '.ui'
  11281. }
  11282. };
  11283. })( jQuery, window, document );