| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419 |
- /*
- Copyright 2012 Igor Vaynberg
- Version: 3.5.2 Timestamp: Sat Nov 1 14:43:36 EDT 2014
- This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU
- General Public License version 2 (the "GPL License"). You may choose either license to govern your
- use of this software only upon the condition that you accept all of the terms of either the Apache
- License or the GPL License.
- You may obtain a copy of the Apache License and the GPL License at:
- http://www.apache.org/licenses/LICENSE-2.0
- http://www.gnu.org/licenses/gpl-2.0.html
- Unless required by applicable law or agreed to in writing, software distributed under the
- Apache License or the GPL License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
- CONDITIONS OF ANY KIND, either express or implied. See the Apache License and the GPL License for
- the specific language governing permissions and limitations under the Apache License and the GPL License.
- */
- ( function ( $ ) {
- if ( typeof $.fn.each2 == 'undefined' ) {
- $.extend( $.fn, {
- /*
- * 4-10 times faster .each replacement
- * use it carefully, as it overrides jQuery context of element on each iteration
- */
- each2: function ( c ) {
- var j = $( [ 0 ] ),
- i = -1,
- l = this.length;
- while (
- ++i < l &&
- ( j.context = j[ 0 ] = this[ i ] ) &&
- c.call( j[ 0 ], i, j ) !== false //"this"=DOM, i=index, j=jQuery object
- );
- return this;
- },
- } );
- }
- } )( jQuery );
- ( function ( $, undefined ) {
- 'use strict';
- /*global document, window, jQuery, console */
- if ( window.Select2 !== undefined ) {
- return;
- }
- var AbstractSelect2,
- SingleSelect2,
- MultiSelect2,
- nextUid,
- sizer,
- lastMousePosition = { x: 0, y: 0 },
- $document,
- scrollBarDimensions,
- KEY = {
- TAB: 9,
- ENTER: 13,
- ESC: 27,
- SPACE: 32,
- LEFT: 37,
- UP: 38,
- RIGHT: 39,
- DOWN: 40,
- SHIFT: 16,
- CTRL: 17,
- ALT: 18,
- PAGE_UP: 33,
- PAGE_DOWN: 34,
- HOME: 36,
- END: 35,
- BACKSPACE: 8,
- DELETE: 46,
- isArrow: function ( k ) {
- k = k.which ? k.which : k;
- switch ( k ) {
- case KEY.LEFT:
- case KEY.RIGHT:
- case KEY.UP:
- case KEY.DOWN:
- return true;
- }
- return false;
- },
- isControl: function ( e ) {
- var k = e.which;
- switch ( k ) {
- case KEY.SHIFT:
- case KEY.CTRL:
- case KEY.ALT:
- return true;
- }
- if ( e.metaKey ) return true;
- return false;
- },
- isFunctionKey: function ( k ) {
- k = k.which ? k.which : k;
- return k >= 112 && k <= 123;
- },
- },
- MEASURE_SCROLLBAR_TEMPLATE =
- "<div class='select2-measure-scrollbar'></div>",
- DIACRITICS = {
- '\u24B6': 'A',
- '\uFF21': 'A',
- '\u00C0': 'A',
- '\u00C1': 'A',
- '\u00C2': 'A',
- '\u1EA6': 'A',
- '\u1EA4': 'A',
- '\u1EAA': 'A',
- '\u1EA8': 'A',
- '\u00C3': 'A',
- '\u0100': 'A',
- '\u0102': 'A',
- '\u1EB0': 'A',
- '\u1EAE': 'A',
- '\u1EB4': 'A',
- '\u1EB2': 'A',
- '\u0226': 'A',
- '\u01E0': 'A',
- '\u00C4': 'A',
- '\u01DE': 'A',
- '\u1EA2': 'A',
- '\u00C5': 'A',
- '\u01FA': 'A',
- '\u01CD': 'A',
- '\u0200': 'A',
- '\u0202': 'A',
- '\u1EA0': 'A',
- '\u1EAC': 'A',
- '\u1EB6': 'A',
- '\u1E00': 'A',
- '\u0104': 'A',
- '\u023A': 'A',
- '\u2C6F': 'A',
- '\uA732': 'AA',
- '\u00C6': 'AE',
- '\u01FC': 'AE',
- '\u01E2': 'AE',
- '\uA734': 'AO',
- '\uA736': 'AU',
- '\uA738': 'AV',
- '\uA73A': 'AV',
- '\uA73C': 'AY',
- '\u24B7': 'B',
- '\uFF22': 'B',
- '\u1E02': 'B',
- '\u1E04': 'B',
- '\u1E06': 'B',
- '\u0243': 'B',
- '\u0182': 'B',
- '\u0181': 'B',
- '\u24B8': 'C',
- '\uFF23': 'C',
- '\u0106': 'C',
- '\u0108': 'C',
- '\u010A': 'C',
- '\u010C': 'C',
- '\u00C7': 'C',
- '\u1E08': 'C',
- '\u0187': 'C',
- '\u023B': 'C',
- '\uA73E': 'C',
- '\u24B9': 'D',
- '\uFF24': 'D',
- '\u1E0A': 'D',
- '\u010E': 'D',
- '\u1E0C': 'D',
- '\u1E10': 'D',
- '\u1E12': 'D',
- '\u1E0E': 'D',
- '\u0110': 'D',
- '\u018B': 'D',
- '\u018A': 'D',
- '\u0189': 'D',
- '\uA779': 'D',
- '\u01F1': 'DZ',
- '\u01C4': 'DZ',
- '\u01F2': 'Dz',
- '\u01C5': 'Dz',
- '\u24BA': 'E',
- '\uFF25': 'E',
- '\u00C8': 'E',
- '\u00C9': 'E',
- '\u00CA': 'E',
- '\u1EC0': 'E',
- '\u1EBE': 'E',
- '\u1EC4': 'E',
- '\u1EC2': 'E',
- '\u1EBC': 'E',
- '\u0112': 'E',
- '\u1E14': 'E',
- '\u1E16': 'E',
- '\u0114': 'E',
- '\u0116': 'E',
- '\u00CB': 'E',
- '\u1EBA': 'E',
- '\u011A': 'E',
- '\u0204': 'E',
- '\u0206': 'E',
- '\u1EB8': 'E',
- '\u1EC6': 'E',
- '\u0228': 'E',
- '\u1E1C': 'E',
- '\u0118': 'E',
- '\u1E18': 'E',
- '\u1E1A': 'E',
- '\u0190': 'E',
- '\u018E': 'E',
- '\u24BB': 'F',
- '\uFF26': 'F',
- '\u1E1E': 'F',
- '\u0191': 'F',
- '\uA77B': 'F',
- '\u24BC': 'G',
- '\uFF27': 'G',
- '\u01F4': 'G',
- '\u011C': 'G',
- '\u1E20': 'G',
- '\u011E': 'G',
- '\u0120': 'G',
- '\u01E6': 'G',
- '\u0122': 'G',
- '\u01E4': 'G',
- '\u0193': 'G',
- '\uA7A0': 'G',
- '\uA77D': 'G',
- '\uA77E': 'G',
- '\u24BD': 'H',
- '\uFF28': 'H',
- '\u0124': 'H',
- '\u1E22': 'H',
- '\u1E26': 'H',
- '\u021E': 'H',
- '\u1E24': 'H',
- '\u1E28': 'H',
- '\u1E2A': 'H',
- '\u0126': 'H',
- '\u2C67': 'H',
- '\u2C75': 'H',
- '\uA78D': 'H',
- '\u24BE': 'I',
- '\uFF29': 'I',
- '\u00CC': 'I',
- '\u00CD': 'I',
- '\u00CE': 'I',
- '\u0128': 'I',
- '\u012A': 'I',
- '\u012C': 'I',
- '\u0130': 'I',
- '\u00CF': 'I',
- '\u1E2E': 'I',
- '\u1EC8': 'I',
- '\u01CF': 'I',
- '\u0208': 'I',
- '\u020A': 'I',
- '\u1ECA': 'I',
- '\u012E': 'I',
- '\u1E2C': 'I',
- '\u0197': 'I',
- '\u24BF': 'J',
- '\uFF2A': 'J',
- '\u0134': 'J',
- '\u0248': 'J',
- '\u24C0': 'K',
- '\uFF2B': 'K',
- '\u1E30': 'K',
- '\u01E8': 'K',
- '\u1E32': 'K',
- '\u0136': 'K',
- '\u1E34': 'K',
- '\u0198': 'K',
- '\u2C69': 'K',
- '\uA740': 'K',
- '\uA742': 'K',
- '\uA744': 'K',
- '\uA7A2': 'K',
- '\u24C1': 'L',
- '\uFF2C': 'L',
- '\u013F': 'L',
- '\u0139': 'L',
- '\u013D': 'L',
- '\u1E36': 'L',
- '\u1E38': 'L',
- '\u013B': 'L',
- '\u1E3C': 'L',
- '\u1E3A': 'L',
- '\u0141': 'L',
- '\u023D': 'L',
- '\u2C62': 'L',
- '\u2C60': 'L',
- '\uA748': 'L',
- '\uA746': 'L',
- '\uA780': 'L',
- '\u01C7': 'LJ',
- '\u01C8': 'Lj',
- '\u24C2': 'M',
- '\uFF2D': 'M',
- '\u1E3E': 'M',
- '\u1E40': 'M',
- '\u1E42': 'M',
- '\u2C6E': 'M',
- '\u019C': 'M',
- '\u24C3': 'N',
- '\uFF2E': 'N',
- '\u01F8': 'N',
- '\u0143': 'N',
- '\u00D1': 'N',
- '\u1E44': 'N',
- '\u0147': 'N',
- '\u1E46': 'N',
- '\u0145': 'N',
- '\u1E4A': 'N',
- '\u1E48': 'N',
- '\u0220': 'N',
- '\u019D': 'N',
- '\uA790': 'N',
- '\uA7A4': 'N',
- '\u01CA': 'NJ',
- '\u01CB': 'Nj',
- '\u24C4': 'O',
- '\uFF2F': 'O',
- '\u00D2': 'O',
- '\u00D3': 'O',
- '\u00D4': 'O',
- '\u1ED2': 'O',
- '\u1ED0': 'O',
- '\u1ED6': 'O',
- '\u1ED4': 'O',
- '\u00D5': 'O',
- '\u1E4C': 'O',
- '\u022C': 'O',
- '\u1E4E': 'O',
- '\u014C': 'O',
- '\u1E50': 'O',
- '\u1E52': 'O',
- '\u014E': 'O',
- '\u022E': 'O',
- '\u0230': 'O',
- '\u00D6': 'O',
- '\u022A': 'O',
- '\u1ECE': 'O',
- '\u0150': 'O',
- '\u01D1': 'O',
- '\u020C': 'O',
- '\u020E': 'O',
- '\u01A0': 'O',
- '\u1EDC': 'O',
- '\u1EDA': 'O',
- '\u1EE0': 'O',
- '\u1EDE': 'O',
- '\u1EE2': 'O',
- '\u1ECC': 'O',
- '\u1ED8': 'O',
- '\u01EA': 'O',
- '\u01EC': 'O',
- '\u00D8': 'O',
- '\u01FE': 'O',
- '\u0186': 'O',
- '\u019F': 'O',
- '\uA74A': 'O',
- '\uA74C': 'O',
- '\u01A2': 'OI',
- '\uA74E': 'OO',
- '\u0222': 'OU',
- '\u24C5': 'P',
- '\uFF30': 'P',
- '\u1E54': 'P',
- '\u1E56': 'P',
- '\u01A4': 'P',
- '\u2C63': 'P',
- '\uA750': 'P',
- '\uA752': 'P',
- '\uA754': 'P',
- '\u24C6': 'Q',
- '\uFF31': 'Q',
- '\uA756': 'Q',
- '\uA758': 'Q',
- '\u024A': 'Q',
- '\u24C7': 'R',
- '\uFF32': 'R',
- '\u0154': 'R',
- '\u1E58': 'R',
- '\u0158': 'R',
- '\u0210': 'R',
- '\u0212': 'R',
- '\u1E5A': 'R',
- '\u1E5C': 'R',
- '\u0156': 'R',
- '\u1E5E': 'R',
- '\u024C': 'R',
- '\u2C64': 'R',
- '\uA75A': 'R',
- '\uA7A6': 'R',
- '\uA782': 'R',
- '\u24C8': 'S',
- '\uFF33': 'S',
- '\u1E9E': 'S',
- '\u015A': 'S',
- '\u1E64': 'S',
- '\u015C': 'S',
- '\u1E60': 'S',
- '\u0160': 'S',
- '\u1E66': 'S',
- '\u1E62': 'S',
- '\u1E68': 'S',
- '\u0218': 'S',
- '\u015E': 'S',
- '\u2C7E': 'S',
- '\uA7A8': 'S',
- '\uA784': 'S',
- '\u24C9': 'T',
- '\uFF34': 'T',
- '\u1E6A': 'T',
- '\u0164': 'T',
- '\u1E6C': 'T',
- '\u021A': 'T',
- '\u0162': 'T',
- '\u1E70': 'T',
- '\u1E6E': 'T',
- '\u0166': 'T',
- '\u01AC': 'T',
- '\u01AE': 'T',
- '\u023E': 'T',
- '\uA786': 'T',
- '\uA728': 'TZ',
- '\u24CA': 'U',
- '\uFF35': 'U',
- '\u00D9': 'U',
- '\u00DA': 'U',
- '\u00DB': 'U',
- '\u0168': 'U',
- '\u1E78': 'U',
- '\u016A': 'U',
- '\u1E7A': 'U',
- '\u016C': 'U',
- '\u00DC': 'U',
- '\u01DB': 'U',
- '\u01D7': 'U',
- '\u01D5': 'U',
- '\u01D9': 'U',
- '\u1EE6': 'U',
- '\u016E': 'U',
- '\u0170': 'U',
- '\u01D3': 'U',
- '\u0214': 'U',
- '\u0216': 'U',
- '\u01AF': 'U',
- '\u1EEA': 'U',
- '\u1EE8': 'U',
- '\u1EEE': 'U',
- '\u1EEC': 'U',
- '\u1EF0': 'U',
- '\u1EE4': 'U',
- '\u1E72': 'U',
- '\u0172': 'U',
- '\u1E76': 'U',
- '\u1E74': 'U',
- '\u0244': 'U',
- '\u24CB': 'V',
- '\uFF36': 'V',
- '\u1E7C': 'V',
- '\u1E7E': 'V',
- '\u01B2': 'V',
- '\uA75E': 'V',
- '\u0245': 'V',
- '\uA760': 'VY',
- '\u24CC': 'W',
- '\uFF37': 'W',
- '\u1E80': 'W',
- '\u1E82': 'W',
- '\u0174': 'W',
- '\u1E86': 'W',
- '\u1E84': 'W',
- '\u1E88': 'W',
- '\u2C72': 'W',
- '\u24CD': 'X',
- '\uFF38': 'X',
- '\u1E8A': 'X',
- '\u1E8C': 'X',
- '\u24CE': 'Y',
- '\uFF39': 'Y',
- '\u1EF2': 'Y',
- '\u00DD': 'Y',
- '\u0176': 'Y',
- '\u1EF8': 'Y',
- '\u0232': 'Y',
- '\u1E8E': 'Y',
- '\u0178': 'Y',
- '\u1EF6': 'Y',
- '\u1EF4': 'Y',
- '\u01B3': 'Y',
- '\u024E': 'Y',
- '\u1EFE': 'Y',
- '\u24CF': 'Z',
- '\uFF3A': 'Z',
- '\u0179': 'Z',
- '\u1E90': 'Z',
- '\u017B': 'Z',
- '\u017D': 'Z',
- '\u1E92': 'Z',
- '\u1E94': 'Z',
- '\u01B5': 'Z',
- '\u0224': 'Z',
- '\u2C7F': 'Z',
- '\u2C6B': 'Z',
- '\uA762': 'Z',
- '\u24D0': 'a',
- '\uFF41': 'a',
- '\u1E9A': 'a',
- '\u00E0': 'a',
- '\u00E1': 'a',
- '\u00E2': 'a',
- '\u1EA7': 'a',
- '\u1EA5': 'a',
- '\u1EAB': 'a',
- '\u1EA9': 'a',
- '\u00E3': 'a',
- '\u0101': 'a',
- '\u0103': 'a',
- '\u1EB1': 'a',
- '\u1EAF': 'a',
- '\u1EB5': 'a',
- '\u1EB3': 'a',
- '\u0227': 'a',
- '\u01E1': 'a',
- '\u00E4': 'a',
- '\u01DF': 'a',
- '\u1EA3': 'a',
- '\u00E5': 'a',
- '\u01FB': 'a',
- '\u01CE': 'a',
- '\u0201': 'a',
- '\u0203': 'a',
- '\u1EA1': 'a',
- '\u1EAD': 'a',
- '\u1EB7': 'a',
- '\u1E01': 'a',
- '\u0105': 'a',
- '\u2C65': 'a',
- '\u0250': 'a',
- '\uA733': 'aa',
- '\u00E6': 'ae',
- '\u01FD': 'ae',
- '\u01E3': 'ae',
- '\uA735': 'ao',
- '\uA737': 'au',
- '\uA739': 'av',
- '\uA73B': 'av',
- '\uA73D': 'ay',
- '\u24D1': 'b',
- '\uFF42': 'b',
- '\u1E03': 'b',
- '\u1E05': 'b',
- '\u1E07': 'b',
- '\u0180': 'b',
- '\u0183': 'b',
- '\u0253': 'b',
- '\u24D2': 'c',
- '\uFF43': 'c',
- '\u0107': 'c',
- '\u0109': 'c',
- '\u010B': 'c',
- '\u010D': 'c',
- '\u00E7': 'c',
- '\u1E09': 'c',
- '\u0188': 'c',
- '\u023C': 'c',
- '\uA73F': 'c',
- '\u2184': 'c',
- '\u24D3': 'd',
- '\uFF44': 'd',
- '\u1E0B': 'd',
- '\u010F': 'd',
- '\u1E0D': 'd',
- '\u1E11': 'd',
- '\u1E13': 'd',
- '\u1E0F': 'd',
- '\u0111': 'd',
- '\u018C': 'd',
- '\u0256': 'd',
- '\u0257': 'd',
- '\uA77A': 'd',
- '\u01F3': 'dz',
- '\u01C6': 'dz',
- '\u24D4': 'e',
- '\uFF45': 'e',
- '\u00E8': 'e',
- '\u00E9': 'e',
- '\u00EA': 'e',
- '\u1EC1': 'e',
- '\u1EBF': 'e',
- '\u1EC5': 'e',
- '\u1EC3': 'e',
- '\u1EBD': 'e',
- '\u0113': 'e',
- '\u1E15': 'e',
- '\u1E17': 'e',
- '\u0115': 'e',
- '\u0117': 'e',
- '\u00EB': 'e',
- '\u1EBB': 'e',
- '\u011B': 'e',
- '\u0205': 'e',
- '\u0207': 'e',
- '\u1EB9': 'e',
- '\u1EC7': 'e',
- '\u0229': 'e',
- '\u1E1D': 'e',
- '\u0119': 'e',
- '\u1E19': 'e',
- '\u1E1B': 'e',
- '\u0247': 'e',
- '\u025B': 'e',
- '\u01DD': 'e',
- '\u24D5': 'f',
- '\uFF46': 'f',
- '\u1E1F': 'f',
- '\u0192': 'f',
- '\uA77C': 'f',
- '\u24D6': 'g',
- '\uFF47': 'g',
- '\u01F5': 'g',
- '\u011D': 'g',
- '\u1E21': 'g',
- '\u011F': 'g',
- '\u0121': 'g',
- '\u01E7': 'g',
- '\u0123': 'g',
- '\u01E5': 'g',
- '\u0260': 'g',
- '\uA7A1': 'g',
- '\u1D79': 'g',
- '\uA77F': 'g',
- '\u24D7': 'h',
- '\uFF48': 'h',
- '\u0125': 'h',
- '\u1E23': 'h',
- '\u1E27': 'h',
- '\u021F': 'h',
- '\u1E25': 'h',
- '\u1E29': 'h',
- '\u1E2B': 'h',
- '\u1E96': 'h',
- '\u0127': 'h',
- '\u2C68': 'h',
- '\u2C76': 'h',
- '\u0265': 'h',
- '\u0195': 'hv',
- '\u24D8': 'i',
- '\uFF49': 'i',
- '\u00EC': 'i',
- '\u00ED': 'i',
- '\u00EE': 'i',
- '\u0129': 'i',
- '\u012B': 'i',
- '\u012D': 'i',
- '\u00EF': 'i',
- '\u1E2F': 'i',
- '\u1EC9': 'i',
- '\u01D0': 'i',
- '\u0209': 'i',
- '\u020B': 'i',
- '\u1ECB': 'i',
- '\u012F': 'i',
- '\u1E2D': 'i',
- '\u0268': 'i',
- '\u0131': 'i',
- '\u24D9': 'j',
- '\uFF4A': 'j',
- '\u0135': 'j',
- '\u01F0': 'j',
- '\u0249': 'j',
- '\u24DA': 'k',
- '\uFF4B': 'k',
- '\u1E31': 'k',
- '\u01E9': 'k',
- '\u1E33': 'k',
- '\u0137': 'k',
- '\u1E35': 'k',
- '\u0199': 'k',
- '\u2C6A': 'k',
- '\uA741': 'k',
- '\uA743': 'k',
- '\uA745': 'k',
- '\uA7A3': 'k',
- '\u24DB': 'l',
- '\uFF4C': 'l',
- '\u0140': 'l',
- '\u013A': 'l',
- '\u013E': 'l',
- '\u1E37': 'l',
- '\u1E39': 'l',
- '\u013C': 'l',
- '\u1E3D': 'l',
- '\u1E3B': 'l',
- '\u017F': 'l',
- '\u0142': 'l',
- '\u019A': 'l',
- '\u026B': 'l',
- '\u2C61': 'l',
- '\uA749': 'l',
- '\uA781': 'l',
- '\uA747': 'l',
- '\u01C9': 'lj',
- '\u24DC': 'm',
- '\uFF4D': 'm',
- '\u1E3F': 'm',
- '\u1E41': 'm',
- '\u1E43': 'm',
- '\u0271': 'm',
- '\u026F': 'm',
- '\u24DD': 'n',
- '\uFF4E': 'n',
- '\u01F9': 'n',
- '\u0144': 'n',
- '\u00F1': 'n',
- '\u1E45': 'n',
- '\u0148': 'n',
- '\u1E47': 'n',
- '\u0146': 'n',
- '\u1E4B': 'n',
- '\u1E49': 'n',
- '\u019E': 'n',
- '\u0272': 'n',
- '\u0149': 'n',
- '\uA791': 'n',
- '\uA7A5': 'n',
- '\u01CC': 'nj',
- '\u24DE': 'o',
- '\uFF4F': 'o',
- '\u00F2': 'o',
- '\u00F3': 'o',
- '\u00F4': 'o',
- '\u1ED3': 'o',
- '\u1ED1': 'o',
- '\u1ED7': 'o',
- '\u1ED5': 'o',
- '\u00F5': 'o',
- '\u1E4D': 'o',
- '\u022D': 'o',
- '\u1E4F': 'o',
- '\u014D': 'o',
- '\u1E51': 'o',
- '\u1E53': 'o',
- '\u014F': 'o',
- '\u022F': 'o',
- '\u0231': 'o',
- '\u00F6': 'o',
- '\u022B': 'o',
- '\u1ECF': 'o',
- '\u0151': 'o',
- '\u01D2': 'o',
- '\u020D': 'o',
- '\u020F': 'o',
- '\u01A1': 'o',
- '\u1EDD': 'o',
- '\u1EDB': 'o',
- '\u1EE1': 'o',
- '\u1EDF': 'o',
- '\u1EE3': 'o',
- '\u1ECD': 'o',
- '\u1ED9': 'o',
- '\u01EB': 'o',
- '\u01ED': 'o',
- '\u00F8': 'o',
- '\u01FF': 'o',
- '\u0254': 'o',
- '\uA74B': 'o',
- '\uA74D': 'o',
- '\u0275': 'o',
- '\u01A3': 'oi',
- '\u0223': 'ou',
- '\uA74F': 'oo',
- '\u24DF': 'p',
- '\uFF50': 'p',
- '\u1E55': 'p',
- '\u1E57': 'p',
- '\u01A5': 'p',
- '\u1D7D': 'p',
- '\uA751': 'p',
- '\uA753': 'p',
- '\uA755': 'p',
- '\u24E0': 'q',
- '\uFF51': 'q',
- '\u024B': 'q',
- '\uA757': 'q',
- '\uA759': 'q',
- '\u24E1': 'r',
- '\uFF52': 'r',
- '\u0155': 'r',
- '\u1E59': 'r',
- '\u0159': 'r',
- '\u0211': 'r',
- '\u0213': 'r',
- '\u1E5B': 'r',
- '\u1E5D': 'r',
- '\u0157': 'r',
- '\u1E5F': 'r',
- '\u024D': 'r',
- '\u027D': 'r',
- '\uA75B': 'r',
- '\uA7A7': 'r',
- '\uA783': 'r',
- '\u24E2': 's',
- '\uFF53': 's',
- '\u00DF': 's',
- '\u015B': 's',
- '\u1E65': 's',
- '\u015D': 's',
- '\u1E61': 's',
- '\u0161': 's',
- '\u1E67': 's',
- '\u1E63': 's',
- '\u1E69': 's',
- '\u0219': 's',
- '\u015F': 's',
- '\u023F': 's',
- '\uA7A9': 's',
- '\uA785': 's',
- '\u1E9B': 's',
- '\u24E3': 't',
- '\uFF54': 't',
- '\u1E6B': 't',
- '\u1E97': 't',
- '\u0165': 't',
- '\u1E6D': 't',
- '\u021B': 't',
- '\u0163': 't',
- '\u1E71': 't',
- '\u1E6F': 't',
- '\u0167': 't',
- '\u01AD': 't',
- '\u0288': 't',
- '\u2C66': 't',
- '\uA787': 't',
- '\uA729': 'tz',
- '\u24E4': 'u',
- '\uFF55': 'u',
- '\u00F9': 'u',
- '\u00FA': 'u',
- '\u00FB': 'u',
- '\u0169': 'u',
- '\u1E79': 'u',
- '\u016B': 'u',
- '\u1E7B': 'u',
- '\u016D': 'u',
- '\u00FC': 'u',
- '\u01DC': 'u',
- '\u01D8': 'u',
- '\u01D6': 'u',
- '\u01DA': 'u',
- '\u1EE7': 'u',
- '\u016F': 'u',
- '\u0171': 'u',
- '\u01D4': 'u',
- '\u0215': 'u',
- '\u0217': 'u',
- '\u01B0': 'u',
- '\u1EEB': 'u',
- '\u1EE9': 'u',
- '\u1EEF': 'u',
- '\u1EED': 'u',
- '\u1EF1': 'u',
- '\u1EE5': 'u',
- '\u1E73': 'u',
- '\u0173': 'u',
- '\u1E77': 'u',
- '\u1E75': 'u',
- '\u0289': 'u',
- '\u24E5': 'v',
- '\uFF56': 'v',
- '\u1E7D': 'v',
- '\u1E7F': 'v',
- '\u028B': 'v',
- '\uA75F': 'v',
- '\u028C': 'v',
- '\uA761': 'vy',
- '\u24E6': 'w',
- '\uFF57': 'w',
- '\u1E81': 'w',
- '\u1E83': 'w',
- '\u0175': 'w',
- '\u1E87': 'w',
- '\u1E85': 'w',
- '\u1E98': 'w',
- '\u1E89': 'w',
- '\u2C73': 'w',
- '\u24E7': 'x',
- '\uFF58': 'x',
- '\u1E8B': 'x',
- '\u1E8D': 'x',
- '\u24E8': 'y',
- '\uFF59': 'y',
- '\u1EF3': 'y',
- '\u00FD': 'y',
- '\u0177': 'y',
- '\u1EF9': 'y',
- '\u0233': 'y',
- '\u1E8F': 'y',
- '\u00FF': 'y',
- '\u1EF7': 'y',
- '\u1E99': 'y',
- '\u1EF5': 'y',
- '\u01B4': 'y',
- '\u024F': 'y',
- '\u1EFF': 'y',
- '\u24E9': 'z',
- '\uFF5A': 'z',
- '\u017A': 'z',
- '\u1E91': 'z',
- '\u017C': 'z',
- '\u017E': 'z',
- '\u1E93': 'z',
- '\u1E95': 'z',
- '\u01B6': 'z',
- '\u0225': 'z',
- '\u0240': 'z',
- '\u2C6C': 'z',
- '\uA763': 'z',
- '\u0386': '\u0391',
- '\u0388': '\u0395',
- '\u0389': '\u0397',
- '\u038A': '\u0399',
- '\u03AA': '\u0399',
- '\u038C': '\u039F',
- '\u038E': '\u03A5',
- '\u03AB': '\u03A5',
- '\u038F': '\u03A9',
- '\u03AC': '\u03B1',
- '\u03AD': '\u03B5',
- '\u03AE': '\u03B7',
- '\u03AF': '\u03B9',
- '\u03CA': '\u03B9',
- '\u0390': '\u03B9',
- '\u03CC': '\u03BF',
- '\u03CD': '\u03C5',
- '\u03CB': '\u03C5',
- '\u03B0': '\u03C5',
- '\u03C9': '\u03C9',
- '\u03C2': '\u03C3',
- };
- $document = $( document );
- nextUid = ( function () {
- var counter = 1;
- return function () {
- return counter++;
- };
- } )();
- function reinsertElement( element ) {
- var placeholder = $( document.createTextNode( '' ) );
- element.before( placeholder );
- placeholder.before( element );
- placeholder.remove();
- }
- function stripDiacritics( str ) {
- // Used 'uni range + named function' from http://jsperf.com/diacritics/18
- function match( a ) {
- return DIACRITICS[ a ] || a;
- }
- return str.replace( /[^\u0000-\u007E]/g, match );
- }
- function indexOf( value, array ) {
- var i = 0,
- l = array.length;
- for ( ; i < l; i = i + 1 ) {
- if ( equal( value, array[ i ] ) ) return i;
- }
- return -1;
- }
- function measureScrollbar() {
- var $template = $( MEASURE_SCROLLBAR_TEMPLATE );
- $template.appendTo( document.body );
- var dim = {
- width: $template.width() - $template[ 0 ].clientWidth,
- height: $template.height() - $template[ 0 ].clientHeight,
- };
- $template.remove();
- return dim;
- }
- /**
- * Compares equality of a and b
- * @param a
- * @param b
- */
- function equal( a, b ) {
- if ( a === b ) return true;
- if ( a === undefined || b === undefined ) return false;
- if ( a === null || b === null ) return false;
- // Check whether 'a' or 'b' is a string (primitive or object).
- // The concatenation of an empty string (+'') converts its argument to a string's primitive.
- if ( a.constructor === String ) return a + '' === b + ''; // a+'' - in case 'a' is a String object
- if ( b.constructor === String ) return b + '' === a + ''; // b+'' - in case 'b' is a String object
- return false;
- }
- /**
- * Splits the string into an array of values, transforming each value. An empty array is returned for nulls or empty
- * strings
- * @param string
- * @param separator
- */
- function splitVal( string, separator, transform ) {
- var val, i, l;
- if ( string === null || string.length < 1 ) return [];
- val = string.split( separator );
- for ( i = 0, l = val.length; i < l; i = i + 1 )
- val[ i ] = transform( val[ i ] );
- return val;
- }
- function getSideBorderPadding( element ) {
- return element.outerWidth( false ) - element.width();
- }
- function installKeyUpChangeEvent( element ) {
- var key = 'keyup-change-value';
- element.on( 'keydown', function () {
- if ( $.data( element, key ) === undefined ) {
- $.data( element, key, element.val() );
- }
- } );
- element.on( 'keyup', function () {
- var val = $.data( element, key );
- if ( val !== undefined && element.val() !== val ) {
- $.removeData( element, key );
- element.trigger( 'keyup-change' );
- }
- } );
- }
- /**
- * filters mouse events so an event is fired only if the mouse moved.
- *
- * filters out mouse events that occur when mouse is stationary but
- * the elements under the pointer are scrolled.
- */
- function installFilteredMouseMove( element ) {
- element.on( 'mousemove', function ( e ) {
- var lastpos = lastMousePosition;
- if (
- lastpos === undefined ||
- lastpos.x !== e.pageX ||
- lastpos.y !== e.pageY
- ) {
- $( e.target ).trigger( 'mousemove-filtered', e );
- }
- } );
- }
- /**
- * Debounces a function. Returns a function that calls the original fn function only if no invocations have been made
- * within the last quietMillis milliseconds.
- *
- * @param quietMillis number of milliseconds to wait before invoking fn
- * @param fn function to be debounced
- * @param ctx object to be used as this reference within fn
- * @return debounced version of fn
- */
- function debounce( quietMillis, fn, ctx ) {
- ctx = ctx || undefined;
- var timeout;
- return function () {
- var args = arguments;
- window.clearTimeout( timeout );
- timeout = window.setTimeout( function () {
- fn.apply( ctx, args );
- }, quietMillis );
- };
- }
- function installDebouncedScroll( threshold, element ) {
- var notify = debounce( threshold, function ( e ) {
- element.trigger( 'scroll-debounced', e );
- } );
- element.on( 'scroll', function ( e ) {
- if ( indexOf( e.target, element.get() ) >= 0 ) notify( e );
- } );
- }
- function focus( $el ) {
- if ( $el[ 0 ] === document.activeElement ) return;
- /* set the focus in a 0 timeout - that way the focus is set after the processing
- of the current event has finished - which seems like the only reliable way
- to set focus */
- window.setTimeout( function () {
- var el = $el[ 0 ],
- pos = $el.val().length,
- range;
- $el.focus();
- /* make sure el received focus so we do not error out when trying to manipulate the caret.
- sometimes modals or others listeners may steal it after its set */
- var isVisible = el.offsetWidth > 0 || el.offsetHeight > 0;
- if ( isVisible && el === document.activeElement ) {
- /* after the focus is set move the caret to the end, necessary when we val()
- just before setting focus */
- if ( el.setSelectionRange ) {
- el.setSelectionRange( pos, pos );
- } else if ( el.createTextRange ) {
- range = el.createTextRange();
- range.collapse( false );
- range.select();
- }
- }
- }, 0 );
- }
- function getCursorInfo( el ) {
- el = $( el )[ 0 ];
- var offset = 0;
- var length = 0;
- if ( 'selectionStart' in el ) {
- offset = el.selectionStart;
- length = el.selectionEnd - offset;
- } else if ( 'selection' in document ) {
- el.focus();
- var sel = document.selection.createRange();
- length = document.selection.createRange().text.length;
- sel.moveStart( 'character', -el.value.length );
- offset = sel.text.length - length;
- }
- return { offset: offset, length: length };
- }
- function killEvent( event ) {
- event.preventDefault();
- event.stopPropagation();
- }
- function killEventImmediately( event ) {
- event.preventDefault();
- event.stopImmediatePropagation();
- }
- function measureTextWidth( e ) {
- if ( ! sizer ) {
- var style =
- e[ 0 ].currentStyle || window.getComputedStyle( e[ 0 ], null );
- sizer = $( document.createElement( 'div' ) ).css( {
- position: 'absolute',
- left: '-10000px',
- top: '-10000px',
- display: 'none',
- fontSize: style.fontSize,
- fontFamily: style.fontFamily,
- fontStyle: style.fontStyle,
- fontWeight: style.fontWeight,
- letterSpacing: style.letterSpacing,
- textTransform: style.textTransform,
- whiteSpace: 'nowrap',
- } );
- sizer.attr( 'class', 'select2-sizer' );
- $( document.body ).append( sizer );
- }
- sizer.text( e.val() );
- return sizer.width();
- }
- function syncCssClasses( dest, src, adapter ) {
- var classes,
- replacements = [],
- adapted;
- classes = $.trim( dest.attr( 'class' ) );
- if ( classes ) {
- classes = '' + classes; // for IE which returns object
- $( classes.split( /\s+/ ) ).each2( function () {
- if ( this.indexOf( 'select2-' ) === 0 ) {
- replacements.push( this );
- }
- } );
- }
- classes = $.trim( src.attr( 'class' ) );
- if ( classes ) {
- classes = '' + classes; // for IE which returns object
- $( classes.split( /\s+/ ) ).each2( function () {
- if ( this.indexOf( 'select2-' ) !== 0 ) {
- adapted = adapter( this );
- if ( adapted ) {
- replacements.push( adapted );
- }
- }
- } );
- }
- dest.attr( 'class', replacements.join( ' ' ) );
- }
- function markMatch( text, term, markup, escapeMarkup ) {
- var match = stripDiacritics( text.toUpperCase() ).indexOf(
- stripDiacritics( term.toUpperCase() )
- ),
- tl = term.length;
- if ( match < 0 ) {
- markup.push( escapeMarkup( text ) );
- return;
- }
- markup.push( escapeMarkup( text.substring( 0, match ) ) );
- markup.push( "<span class='select2-match'>" );
- markup.push( escapeMarkup( text.substring( match, match + tl ) ) );
- markup.push( '</span>' );
- markup.push(
- escapeMarkup( text.substring( match + tl, text.length ) )
- );
- }
- function defaultEscapeMarkup( markup ) {
- var replace_map = {
- '\\': '\',
- '&': '&',
- '<': '<',
- '>': '>',
- '"': '"',
- "'": ''',
- '/': '/',
- };
- return String( markup ).replace( /[&<>"'\/\\]/g, function ( match ) {
- return replace_map[ match ];
- } );
- }
- /**
- * Produces an ajax-based query function
- *
- * @param options object containing configuration parameters
- * @param options.params parameter map for the transport ajax call, can contain such options as cache, jsonpCallback, etc. see $.ajax
- * @param options.transport function that will be used to execute the ajax request. must be compatible with parameters supported by $.ajax
- * @param options.url url for the data
- * @param options.data a function(searchTerm, pageNumber, context) that should return an object containing query string parameters for the above url.
- * @param options.dataType request data type: ajax, jsonp, other datatypes supported by jQuery's $.ajax function or the transport function if specified
- * @param options.quietMillis (optional) milliseconds to wait before making the ajaxRequest, helps debounce the ajax function if invoked too often
- * @param options.results a function(remoteData, pageNumber, query) that converts data returned form the remote request to the format expected by Select2.
- * The expected format is an object containing the following keys:
- * results array of objects that will be used as choices
- * more (optional) boolean indicating whether there are more results available
- * Example: {results:[{id:1, text:'Red'},{id:2, text:'Blue'}], more:true}
- */
- function ajax( options ) {
- var timeout, // current scheduled but not yet executed request
- handler = null,
- quietMillis = options.quietMillis || 100,
- ajaxUrl = options.url,
- self = this;
- return function ( query ) {
- window.clearTimeout( timeout );
- timeout = window.setTimeout( function () {
- var data = options.data, // ajax data function
- url = ajaxUrl, // ajax url string or function
- transport =
- options.transport ||
- $.fn.select2.ajaxDefaults.transport,
- // deprecated - to be removed in 4.0 - use params instead
- deprecated = {
- type: options.type || 'GET', // set type of request (GET or POST)
- cache: options.cache || false,
- jsonpCallback: options.jsonpCallback || undefined,
- dataType: options.dataType || 'json',
- },
- params = $.extend(
- {},
- $.fn.select2.ajaxDefaults.params,
- deprecated
- );
- data = data
- ? data.call( self, query.term, query.page, query.context )
- : null;
- url =
- typeof url === 'function'
- ? url.call(
- self,
- query.term,
- query.page,
- query.context
- )
- : url;
- if ( handler && typeof handler.abort === 'function' ) {
- handler.abort();
- }
- if ( options.params ) {
- if ( $.isFunction( options.params ) ) {
- $.extend( params, options.params.call( self ) );
- } else {
- $.extend( params, options.params );
- }
- }
- $.extend( params, {
- url: url,
- dataType: options.dataType,
- data: data,
- success: function ( data ) {
- // TODO - replace query.page with query so users have access to term, page, etc.
- // added query as third paramter to keep backwards compatibility
- var results = options.results(
- data,
- query.page,
- query
- );
- query.callback( results );
- },
- error: function ( jqXHR, textStatus, errorThrown ) {
- var results = {
- hasError: true,
- jqXHR: jqXHR,
- textStatus: textStatus,
- errorThrown: errorThrown,
- };
- query.callback( results );
- },
- } );
- handler = transport.call( self, params );
- }, quietMillis );
- };
- }
- /**
- * Produces a query function that works with a local array
- *
- * @param options object containing configuration parameters. The options parameter can either be an array or an
- * object.
- *
- * If the array form is used it is assumed that it contains objects with 'id' and 'text' keys.
- *
- * If the object form is used it is assumed that it contains 'data' and 'text' keys. The 'data' key should contain
- * an array of objects that will be used as choices. These objects must contain at least an 'id' key. The 'text'
- * key can either be a String in which case it is expected that each element in the 'data' array has a key with the
- * value of 'text' which will be used to match choices. Alternatively, text can be a function(item) that can extract
- * the text.
- */
- function local( options ) {
- var data = options, // data elements
- dataText,
- tmp,
- text = function ( item ) {
- return '' + item.text;
- }; // function used to retrieve the text portion of a data item that is matched against the search
- if ( $.isArray( data ) ) {
- tmp = data;
- data = { results: tmp };
- }
- if ( $.isFunction( data ) === false ) {
- tmp = data;
- data = function () {
- return tmp;
- };
- }
- var dataItem = data();
- if ( dataItem.text ) {
- text = dataItem.text;
- // if text is not a function we assume it to be a key name
- if ( ! $.isFunction( text ) ) {
- dataText = dataItem.text; // we need to store this in a separate variable because in the next step data gets reset and data.text is no longer available
- text = function ( item ) {
- return item[ dataText ];
- };
- }
- }
- return function ( query ) {
- var t = query.term,
- filtered = { results: [] },
- process;
- if ( t === '' ) {
- query.callback( data() );
- return;
- }
- process = function ( datum, collection ) {
- var group, attr;
- datum = datum[ 0 ];
- if ( datum.children ) {
- group = {};
- for ( attr in datum ) {
- if ( datum.hasOwnProperty( attr ) )
- group[ attr ] = datum[ attr ];
- }
- group.children = [];
- $( datum.children ).each2( function ( i, childDatum ) {
- process( childDatum, group.children );
- } );
- if (
- group.children.length ||
- query.matcher( t, text( group ), datum )
- ) {
- collection.push( group );
- }
- } else {
- if ( query.matcher( t, text( datum ), datum ) ) {
- collection.push( datum );
- }
- }
- };
- $( data().results ).each2( function ( i, datum ) {
- process( datum, filtered.results );
- } );
- query.callback( filtered );
- };
- }
- // TODO javadoc
- function tags( data ) {
- var isFunc = $.isFunction( data );
- return function ( query ) {
- var t = query.term,
- filtered = { results: [] };
- var result = isFunc ? data( query ) : data;
- if ( $.isArray( result ) ) {
- $( result ).each( function () {
- var isObject = this.text !== undefined,
- text = isObject ? this.text : this;
- if ( t === '' || query.matcher( t, text ) ) {
- filtered.results.push(
- isObject ? this : { id: this, text: this }
- );
- }
- } );
- query.callback( filtered );
- }
- };
- }
- /**
- * Checks if the formatter function should be used.
- *
- * Throws an error if it is not a function. Returns true if it should be used,
- * false if no formatting should be performed.
- *
- * @param formatter
- */
- function checkFormatter( formatter, formatterName ) {
- if ( $.isFunction( formatter ) ) return true;
- if ( ! formatter ) return false;
- if ( typeof formatter === 'string' ) return true;
- throw new Error(
- formatterName + ' must be a string, function, or falsy value'
- );
- }
- /**
- * Returns a given value
- * If given a function, returns its output
- *
- * @param val string|function
- * @param context value of "this" to be passed to function
- * @returns {*}
- */
- function evaluate( val, context ) {
- if ( $.isFunction( val ) ) {
- var args = Array.prototype.slice.call( arguments, 2 );
- return val.apply( context, args );
- }
- return val;
- }
- function countResults( results ) {
- var count = 0;
- $.each( results, function ( i, item ) {
- if ( item.children ) {
- count += countResults( item.children );
- } else {
- count++;
- }
- } );
- return count;
- }
- /**
- * Default tokenizer. This function uses breaks the input on substring match of any string from the
- * opts.tokenSeparators array and uses opts.createSearchChoice to create the choice object. Both of those
- * two options have to be defined in order for the tokenizer to work.
- *
- * @param input text user has typed so far or pasted into the search field
- * @param selection currently selected choices
- * @param selectCallback function(choice) callback tho add the choice to selection
- * @param opts select2's opts
- * @return undefined/null to leave the current input unchanged, or a string to change the input to the returned value
- */
- function defaultTokenizer( input, selection, selectCallback, opts ) {
- var original = input, // store the original so we can compare and know if we need to tell the search to update its text
- dupe = false, // check for whether a token we extracted represents a duplicate selected choice
- token, // token
- index, // position at which the separator was found
- i,
- l, // looping variables
- separator; // the matched separator
- if (
- ! opts.createSearchChoice ||
- ! opts.tokenSeparators ||
- opts.tokenSeparators.length < 1
- )
- return undefined;
- while ( true ) {
- index = -1;
- for ( i = 0, l = opts.tokenSeparators.length; i < l; i++ ) {
- separator = opts.tokenSeparators[ i ];
- index = input.indexOf( separator );
- if ( index >= 0 ) break;
- }
- if ( index < 0 ) break; // did not find any token separator in the input string, bail
- token = input.substring( 0, index );
- input = input.substring( index + separator.length );
- if ( token.length > 0 ) {
- token = opts.createSearchChoice.call( this, token, selection );
- if (
- token !== undefined &&
- token !== null &&
- opts.id( token ) !== undefined &&
- opts.id( token ) !== null
- ) {
- dupe = false;
- for ( i = 0, l = selection.length; i < l; i++ ) {
- if (
- equal( opts.id( token ), opts.id( selection[ i ] ) )
- ) {
- dupe = true;
- break;
- }
- }
- if ( ! dupe ) selectCallback( token );
- }
- }
- }
- if ( original !== input ) return input;
- }
- function cleanupJQueryElements() {
- var self = this;
- $.each( arguments, function ( i, element ) {
- self[ element ].remove();
- self[ element ] = null;
- } );
- }
- /**
- * Creates a new class
- *
- * @param superClass
- * @param methods
- */
- function clazz( SuperClass, methods ) {
- var constructor = function () {};
- constructor.prototype = new SuperClass();
- constructor.prototype.constructor = constructor;
- constructor.prototype.parent = SuperClass.prototype;
- constructor.prototype = $.extend( constructor.prototype, methods );
- return constructor;
- }
- AbstractSelect2 = clazz( Object, {
- // abstract
- bind: function ( func ) {
- var self = this;
- return function () {
- func.apply( self, arguments );
- };
- },
- // abstract
- init: function ( opts ) {
- var results,
- search,
- resultsSelector = '.select2-results';
- // prepare options
- this.opts = opts = this.prepareOpts( opts );
- this.id = opts.id;
- // destroy if called on an existing component
- if (
- opts.element.data( 'select2' ) !== undefined &&
- opts.element.data( 'select2' ) !== null
- ) {
- opts.element.data( 'select2' ).destroy();
- }
- this.container = this.createContainer();
- this.liveRegion = $( '.select2-hidden-accessible' );
- if ( this.liveRegion.length == 0 ) {
- this.liveRegion = $( '<span>', {
- role: 'status',
- 'aria-live': 'polite',
- } )
- .addClass( 'select2-hidden-accessible' )
- .appendTo( document.body );
- }
- this.containerId =
- 's2id_' +
- ( opts.element.attr( 'id' ) || 'autogen' + nextUid() );
- this.containerEventName = this.containerId
- .replace( /([.])/g, '_' )
- .replace( /([;&,\-\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g, '\\$1' );
- this.container.attr( 'id', this.containerId );
- this.container.attr( 'title', opts.element.attr( 'title' ) );
- this.body = $( document.body );
- syncCssClasses(
- this.container,
- this.opts.element,
- this.opts.adaptContainerCssClass
- );
- this.container.attr( 'style', opts.element.attr( 'style' ) );
- this.container.css(
- evaluate( opts.containerCss, this.opts.element )
- );
- this.container.addClass(
- evaluate( opts.containerCssClass, this.opts.element )
- );
- this.elementTabIndex = this.opts.element.attr( 'tabindex' );
- // swap container for the element
- this.opts.element
- .data( 'select2', this )
- .attr( 'tabindex', '-1' )
- .before( this.container )
- .on( 'click.select2', killEvent ); // do not leak click events
- this.container.data( 'select2', this );
- this.dropdown = this.container.find( '.select2-drop' );
- syncCssClasses(
- this.dropdown,
- this.opts.element,
- this.opts.adaptDropdownCssClass
- );
- this.dropdown.addClass(
- evaluate( opts.dropdownCssClass, this.opts.element )
- );
- this.dropdown.data( 'select2', this );
- this.dropdown.on( 'click', killEvent );
- this.results = results = this.container.find( resultsSelector );
- this.search = search = this.container.find( 'input.select2-input' );
- this.queryCount = 0;
- this.resultsPage = 0;
- this.context = null;
- // initialize the container
- this.initContainer();
- this.container.on( 'click', killEvent );
- installFilteredMouseMove( this.results );
- this.dropdown.on(
- 'mousemove-filtered',
- resultsSelector,
- this.bind( this.highlightUnderEvent )
- );
- this.dropdown.on(
- 'touchstart touchmove touchend',
- resultsSelector,
- this.bind( function ( event ) {
- this._touchEvent = true;
- this.highlightUnderEvent( event );
- } )
- );
- this.dropdown.on(
- 'touchmove',
- resultsSelector,
- this.bind( this.touchMoved )
- );
- this.dropdown.on(
- 'touchstart touchend',
- resultsSelector,
- this.bind( this.clearTouchMoved )
- );
- // Waiting for a click event on touch devices to select option and hide dropdown
- // otherwise click will be triggered on an underlying element
- this.dropdown.on(
- 'click',
- this.bind( function ( event ) {
- if ( this._touchEvent ) {
- this._touchEvent = false;
- this.selectHighlighted();
- }
- } )
- );
- installDebouncedScroll( 80, this.results );
- this.dropdown.on(
- 'scroll-debounced',
- resultsSelector,
- this.bind( this.loadMoreIfNeeded )
- );
- // do not propagate change event from the search field out of the component
- $( this.container ).on( 'change', '.select2-input', function ( e ) {
- e.stopPropagation();
- } );
- $( this.dropdown ).on( 'change', '.select2-input', function ( e ) {
- e.stopPropagation();
- } );
- // if jquery.mousewheel plugin is installed we can prevent out-of-bounds scrolling of results via mousewheel
- if ( $.fn.mousewheel ) {
- results.mousewheel( function ( e, delta, deltaX, deltaY ) {
- var top = results.scrollTop();
- if ( deltaY > 0 && top - deltaY <= 0 ) {
- results.scrollTop( 0 );
- killEvent( e );
- } else if (
- deltaY < 0 &&
- results.get( 0 ).scrollHeight -
- results.scrollTop() +
- deltaY <=
- results.height()
- ) {
- results.scrollTop(
- results.get( 0 ).scrollHeight - results.height()
- );
- killEvent( e );
- }
- } );
- }
- installKeyUpChangeEvent( search );
- search.on(
- 'keyup-change input paste',
- this.bind( this.updateResults )
- );
- search.on( 'focus', function () {
- search.addClass( 'select2-focused' );
- } );
- search.on( 'blur', function () {
- search.removeClass( 'select2-focused' );
- } );
- this.dropdown.on(
- 'mouseup',
- resultsSelector,
- this.bind( function ( e ) {
- if (
- $( e.target ).closest( '.select2-result-selectable' )
- .length > 0
- ) {
- this.highlightUnderEvent( e );
- this.selectHighlighted( e );
- }
- } )
- );
- // trap all mouse events from leaving the dropdown. sometimes there may be a modal that is listening
- // for mouse events outside of itself so it can close itself. since the dropdown is now outside the select2's
- // dom it will trigger the popup close, which is not what we want
- // focusin can cause focus wars between modals and select2 since the dropdown is outside the modal.
- this.dropdown.on(
- 'click mouseup mousedown touchstart touchend focusin',
- function ( e ) {
- e.stopPropagation();
- }
- );
- this.nextSearchTerm = undefined;
- if ( $.isFunction( this.opts.initSelection ) ) {
- // initialize selection based on the current value of the source element
- this.initSelection();
- // if the user has provided a function that can set selection based on the value of the source element
- // we monitor the change event on the element and trigger it, allowing for two way synchronization
- this.monitorSource();
- }
- if ( opts.maximumInputLength !== null ) {
- this.search.attr( 'maxlength', opts.maximumInputLength );
- }
- var disabled = opts.element.prop( 'disabled' );
- if ( disabled === undefined ) disabled = false;
- this.enable( ! disabled );
- var readonly = opts.element.prop( 'readonly' );
- if ( readonly === undefined ) readonly = false;
- this.readonly( readonly );
- // Calculate size of scrollbar
- scrollBarDimensions = scrollBarDimensions || measureScrollbar();
- this.autofocus = opts.element.prop( 'autofocus' );
- opts.element.prop( 'autofocus', false );
- if ( this.autofocus ) this.focus();
- this.search.attr( 'placeholder', opts.searchInputPlaceholder );
- },
- // abstract
- destroy: function () {
- var element = this.opts.element,
- select2 = element.data( 'select2' ),
- self = this;
- this.close();
- if ( element.length && element[ 0 ].detachEvent && self._sync ) {
- element.each( function () {
- if ( self._sync ) {
- this.detachEvent( 'onpropertychange', self._sync );
- }
- } );
- }
- if ( this.propertyObserver ) {
- this.propertyObserver.disconnect();
- this.propertyObserver = null;
- }
- this._sync = null;
- if ( select2 !== undefined ) {
- select2.container.remove();
- select2.liveRegion.remove();
- select2.dropdown.remove();
- element
- .show()
- .removeData( 'select2' )
- .off( '.select2' )
- .prop( 'autofocus', this.autofocus || false );
- if ( this.elementTabIndex ) {
- element.attr( { tabindex: this.elementTabIndex } );
- } else {
- element.removeAttr( 'tabindex' );
- }
- element.show();
- }
- cleanupJQueryElements.call(
- this,
- 'container',
- 'liveRegion',
- 'dropdown',
- 'results',
- 'search'
- );
- },
- // abstract
- optionToData: function ( element ) {
- if ( element.is( 'option' ) ) {
- return {
- id: element.prop( 'value' ),
- text: element.text(),
- element: element.get(),
- css: element.attr( 'class' ),
- disabled: element.prop( 'disabled' ),
- locked:
- equal( element.attr( 'locked' ), 'locked' ) ||
- equal( element.data( 'locked' ), true ),
- };
- } else if ( element.is( 'optgroup' ) ) {
- return {
- text: element.attr( 'label' ),
- children: [],
- element: element.get(),
- css: element.attr( 'class' ),
- };
- }
- },
- // abstract
- prepareOpts: function ( opts ) {
- var element,
- select,
- idKey,
- ajaxUrl,
- self = this;
- element = opts.element;
- if ( element.get( 0 ).tagName.toLowerCase() === 'select' ) {
- this.select = select = opts.element;
- }
- if ( select ) {
- // these options are not allowed when attached to a select because they are picked up off the element itself
- $.each(
- [
- 'id',
- 'multiple',
- 'ajax',
- 'query',
- 'createSearchChoice',
- 'initSelection',
- 'data',
- 'tags',
- ],
- function () {
- if ( this in opts ) {
- throw new Error(
- "Option '" +
- this +
- "' is not allowed for Select2 when attached to a <select> element."
- );
- }
- }
- );
- }
- opts = $.extend(
- {},
- {
- populateResults: function ( container, results, query ) {
- var populate,
- id = this.opts.id,
- liveRegion = this.liveRegion;
- populate = function ( results, container, depth ) {
- var i,
- l,
- result,
- selectable,
- disabled,
- compound,
- node,
- label,
- innerContainer,
- formatted;
- results = opts.sortResults(
- results,
- container,
- query
- );
- // collect the created nodes for bulk append
- var nodes = [];
- for (
- i = 0, l = results.length;
- i < l;
- i = i + 1
- ) {
- result = results[ i ];
- disabled = result.disabled === true;
- selectable =
- ! disabled && id( result ) !== undefined;
- compound =
- result.children &&
- result.children.length > 0;
- node = $( '<li></li>' );
- node.addClass(
- 'select2-results-dept-' + depth
- );
- node.addClass( 'select2-result' );
- node.addClass(
- selectable
- ? 'select2-result-selectable'
- : 'select2-result-unselectable'
- );
- if ( disabled ) {
- node.addClass( 'select2-disabled' );
- }
- if ( compound ) {
- node.addClass(
- 'select2-result-with-children'
- );
- }
- node.addClass(
- self.opts.formatResultCssClass( result )
- );
- node.attr( 'role', 'presentation' );
- label = $( document.createElement( 'div' ) );
- label.addClass( 'select2-result-label' );
- label.attr(
- 'id',
- 'select2-result-label-' + nextUid()
- );
- label.attr( 'role', 'option' );
- formatted = opts.formatResult(
- result,
- label,
- query,
- self.opts.escapeMarkup
- );
- if ( formatted !== undefined ) {
- label.html( formatted );
- node.append( label );
- }
- if ( compound ) {
- innerContainer = $( '<ul></ul>' );
- innerContainer.addClass(
- 'select2-result-sub'
- );
- populate(
- result.children,
- innerContainer,
- depth + 1
- );
- node.append( innerContainer );
- }
- node.data( 'select2-data', result );
- nodes.push( node[ 0 ] );
- }
- // bulk append the created nodes
- container.append( nodes );
- liveRegion.text(
- opts.formatMatches( results.length )
- );
- };
- populate( results, container, 0 );
- },
- },
- $.fn.select2.defaults,
- opts
- );
- if ( typeof opts.id !== 'function' ) {
- idKey = opts.id;
- opts.id = function ( e ) {
- return e[ idKey ];
- };
- }
- if ( $.isArray( opts.element.data( 'select2Tags' ) ) ) {
- if ( 'tags' in opts ) {
- throw (
- "tags specified as both an attribute 'data-select2-tags' and in options of Select2 " +
- opts.element.attr( 'id' )
- );
- }
- opts.tags = opts.element.data( 'select2Tags' );
- }
- if ( select ) {
- opts.query = this.bind( function ( query ) {
- var data = { results: [], more: false },
- term = query.term,
- children,
- placeholderOption,
- process;
- process = function ( element, collection ) {
- var group;
- if ( element.is( 'option' ) ) {
- if (
- query.matcher( term, element.text(), element )
- ) {
- collection.push( self.optionToData( element ) );
- }
- } else if ( element.is( 'optgroup' ) ) {
- group = self.optionToData( element );
- element.children().each2( function ( i, elm ) {
- process( elm, group.children );
- } );
- if ( group.children.length > 0 ) {
- collection.push( group );
- }
- }
- };
- children = element.children();
- // ignore the placeholder option if there is one
- if (
- this.getPlaceholder() !== undefined &&
- children.length > 0
- ) {
- placeholderOption = this.getPlaceholderOption();
- if ( placeholderOption ) {
- children = children.not( placeholderOption );
- }
- }
- children.each2( function ( i, elm ) {
- process( elm, data.results );
- } );
- query.callback( data );
- } );
- // this is needed because inside val() we construct choices from options and their id is hardcoded
- opts.id = function ( e ) {
- return e.id;
- };
- } else {
- if ( ! ( 'query' in opts ) ) {
- if ( 'ajax' in opts ) {
- ajaxUrl = opts.element.data( 'ajax-url' );
- if ( ajaxUrl && ajaxUrl.length > 0 ) {
- opts.ajax.url = ajaxUrl;
- }
- opts.query = ajax.call( opts.element, opts.ajax );
- } else if ( 'data' in opts ) {
- opts.query = local( opts.data );
- } else if ( 'tags' in opts ) {
- opts.query = tags( opts.tags );
- if ( opts.createSearchChoice === undefined ) {
- opts.createSearchChoice = function ( term ) {
- return {
- id: $.trim( term ),
- text: $.trim( term ),
- };
- };
- }
- if ( opts.initSelection === undefined ) {
- opts.initSelection = function (
- element,
- callback
- ) {
- var data = [];
- $(
- splitVal(
- element.val(),
- opts.separator,
- opts.transformVal
- )
- ).each( function () {
- var obj = { id: this, text: this },
- tags = opts.tags;
- if ( $.isFunction( tags ) ) tags = tags();
- $( tags ).each( function () {
- if ( equal( this.id, obj.id ) ) {
- obj = this;
- return false;
- }
- } );
- data.push( obj );
- } );
- callback( data );
- };
- }
- }
- }
- }
- if ( typeof opts.query !== 'function' ) {
- throw (
- 'query function not defined for Select2 ' +
- opts.element.attr( 'id' )
- );
- }
- if ( opts.createSearchChoicePosition === 'top' ) {
- opts.createSearchChoicePosition = function ( list, item ) {
- list.unshift( item );
- };
- } else if ( opts.createSearchChoicePosition === 'bottom' ) {
- opts.createSearchChoicePosition = function ( list, item ) {
- list.push( item );
- };
- } else if (
- typeof opts.createSearchChoicePosition !== 'function'
- ) {
- throw "invalid createSearchChoicePosition option must be 'top', 'bottom' or a custom function";
- }
- return opts;
- },
- /**
- * Monitor the original element for changes and update select2 accordingly
- */
- // abstract
- monitorSource: function () {
- var el = this.opts.element,
- observer,
- self = this;
- el.on(
- 'change.select2',
- this.bind( function ( e ) {
- if (
- this.opts.element.data( 'select2-change-triggered' ) !==
- true
- ) {
- this.initSelection();
- }
- } )
- );
- this._sync = this.bind( function () {
- // sync enabled state
- var disabled = el.prop( 'disabled' );
- if ( disabled === undefined ) disabled = false;
- this.enable( ! disabled );
- var readonly = el.prop( 'readonly' );
- if ( readonly === undefined ) readonly = false;
- this.readonly( readonly );
- if ( this.container ) {
- syncCssClasses(
- this.container,
- this.opts.element,
- this.opts.adaptContainerCssClass
- );
- this.container.addClass(
- evaluate(
- this.opts.containerCssClass,
- this.opts.element
- )
- );
- }
- if ( this.dropdown ) {
- syncCssClasses(
- this.dropdown,
- this.opts.element,
- this.opts.adaptDropdownCssClass
- );
- this.dropdown.addClass(
- evaluate(
- this.opts.dropdownCssClass,
- this.opts.element
- )
- );
- }
- } );
- // IE8-10 (IE9/10 won't fire propertyChange via attachEventListener)
- if ( el.length && el[ 0 ].attachEvent ) {
- el.each( function () {
- this.attachEvent( 'onpropertychange', self._sync );
- } );
- }
- // safari, chrome, firefox, IE11
- observer =
- window.MutationObserver ||
- window.WebKitMutationObserver ||
- window.MozMutationObserver;
- if ( observer !== undefined ) {
- if ( this.propertyObserver ) {
- delete this.propertyObserver;
- this.propertyObserver = null;
- }
- this.propertyObserver = new observer( function ( mutations ) {
- $.each( mutations, self._sync );
- } );
- this.propertyObserver.observe( el.get( 0 ), {
- attributes: true,
- subtree: false,
- } );
- }
- },
- // abstract
- triggerSelect: function ( data ) {
- var evt = $.Event( 'select2-selecting', {
- val: this.id( data ),
- object: data,
- choice: data,
- } );
- this.opts.element.trigger( evt );
- return ! evt.isDefaultPrevented();
- },
- /**
- * Triggers the change event on the source element
- */
- // abstract
- triggerChange: function ( details ) {
- details = details || {};
- details = $.extend( {}, details, {
- type: 'change',
- val: this.val(),
- } );
- // prevents recursive triggering
- this.opts.element.data( 'select2-change-triggered', true );
- this.opts.element.trigger( details );
- this.opts.element.data( 'select2-change-triggered', false );
- // some validation frameworks ignore the change event and listen instead to keyup, click for selects
- // so here we trigger the click event manually
- this.opts.element.click();
- // ValidationEngine ignores the change event and listens instead to blur
- // so here we trigger the blur event manually if so desired
- if ( this.opts.blurOnChange ) this.opts.element.blur();
- },
- //abstract
- isInterfaceEnabled: function () {
- return this.enabledInterface === true;
- },
- // abstract
- enableInterface: function () {
- var enabled = this._enabled && ! this._readonly,
- disabled = ! enabled;
- if ( enabled === this.enabledInterface ) return false;
- this.container.toggleClass(
- 'select2-container-disabled',
- disabled
- );
- this.close();
- this.enabledInterface = enabled;
- return true;
- },
- // abstract
- enable: function ( enabled ) {
- if ( enabled === undefined ) enabled = true;
- if ( this._enabled === enabled ) return;
- this._enabled = enabled;
- this.opts.element.prop( 'disabled', ! enabled );
- this.enableInterface();
- },
- // abstract
- disable: function () {
- this.enable( false );
- },
- // abstract
- readonly: function ( enabled ) {
- if ( enabled === undefined ) enabled = false;
- if ( this._readonly === enabled ) return;
- this._readonly = enabled;
- this.opts.element.prop( 'readonly', enabled );
- this.enableInterface();
- },
- // abstract
- opened: function () {
- return this.container
- ? this.container.hasClass( 'select2-dropdown-open' )
- : false;
- },
- // abstract
- positionDropdown: function () {
- var $dropdown = this.dropdown,
- container = this.container,
- offset = container.offset(),
- height = container.outerHeight( false ),
- width = container.outerWidth( false ),
- dropHeight = $dropdown.outerHeight( false ),
- $window = $( window ),
- windowWidth = $window.width(),
- windowHeight = $window.height(),
- viewPortRight = $window.scrollLeft() + windowWidth,
- viewportBottom = $window.scrollTop() + windowHeight,
- dropTop = offset.top + height,
- dropLeft = offset.left,
- enoughRoomBelow = dropTop + dropHeight <= viewportBottom,
- enoughRoomAbove =
- offset.top - dropHeight >= $window.scrollTop(),
- dropWidth = $dropdown.outerWidth( false ),
- enoughRoomOnRight = function () {
- return dropLeft + dropWidth <= viewPortRight;
- },
- enoughRoomOnLeft = function () {
- return (
- offset.left +
- viewPortRight +
- container.outerWidth( false ) >
- dropWidth
- );
- },
- aboveNow = $dropdown.hasClass( 'select2-drop-above' ),
- bodyOffset,
- above,
- changeDirection,
- css,
- resultsListNode;
- // always prefer the current above/below alignment, unless there is not enough room
- if ( aboveNow ) {
- above = true;
- if ( ! enoughRoomAbove && enoughRoomBelow ) {
- changeDirection = true;
- above = false;
- }
- } else {
- above = false;
- if ( ! enoughRoomBelow && enoughRoomAbove ) {
- changeDirection = true;
- above = true;
- }
- }
- //if we are changing direction we need to get positions when dropdown is hidden;
- if ( changeDirection ) {
- $dropdown.hide();
- offset = this.container.offset();
- height = this.container.outerHeight( false );
- width = this.container.outerWidth( false );
- dropHeight = $dropdown.outerHeight( false );
- viewPortRight = $window.scrollLeft() + windowWidth;
- viewportBottom = $window.scrollTop() + windowHeight;
- dropTop = offset.top + height;
- dropLeft = offset.left;
- dropWidth = $dropdown.outerWidth( false );
- $dropdown.show();
- // fix so the cursor does not move to the left within the search-textbox in IE
- this.focusSearch();
- }
- if ( this.opts.dropdownAutoWidth ) {
- resultsListNode = $( '.select2-results', $dropdown )[ 0 ];
- $dropdown.addClass( 'select2-drop-auto-width' );
- $dropdown.css( 'width', '' );
- // Add scrollbar width to dropdown if vertical scrollbar is present
- dropWidth =
- $dropdown.outerWidth( false ) +
- ( resultsListNode.scrollHeight ===
- resultsListNode.clientHeight
- ? 0
- : scrollBarDimensions.width );
- dropWidth > width
- ? ( width = dropWidth )
- : ( dropWidth = width );
- dropHeight = $dropdown.outerHeight( false );
- } else {
- this.container.removeClass( 'select2-drop-auto-width' );
- }
- //console.log("below/ droptop:", dropTop, "dropHeight", dropHeight, "sum", (dropTop+dropHeight)+" viewport bottom", viewportBottom, "enough?", enoughRoomBelow);
- //console.log("above/ offset.top", offset.top, "dropHeight", dropHeight, "top", (offset.top-dropHeight), "scrollTop", this.body.scrollTop(), "enough?", enoughRoomAbove);
- // fix positioning when body has an offset and is not position: static
- if ( this.body.css( 'position' ) !== 'static' ) {
- bodyOffset = this.body.offset();
- dropTop -= bodyOffset.top;
- dropLeft -= bodyOffset.left;
- }
- if ( ! enoughRoomOnRight() && enoughRoomOnLeft() ) {
- dropLeft =
- offset.left +
- this.container.outerWidth( false ) -
- dropWidth;
- }
- css = {
- left: dropLeft,
- width: width,
- };
- if ( above ) {
- css.top = offset.top - dropHeight;
- css.bottom = 'auto';
- this.container.addClass( 'select2-drop-above' );
- $dropdown.addClass( 'select2-drop-above' );
- } else {
- css.top = dropTop;
- css.bottom = 'auto';
- this.container.removeClass( 'select2-drop-above' );
- $dropdown.removeClass( 'select2-drop-above' );
- }
- css = $.extend(
- css,
- evaluate( this.opts.dropdownCss, this.opts.element )
- );
- $dropdown.css( css );
- },
- // abstract
- shouldOpen: function () {
- var event;
- if ( this.opened() ) return false;
- if ( this._enabled === false || this._readonly === true )
- return false;
- event = $.Event( 'select2-opening' );
- this.opts.element.trigger( event );
- return ! event.isDefaultPrevented();
- },
- // abstract
- clearDropdownAlignmentPreference: function () {
- // clear the classes used to figure out the preference of where the dropdown should be opened
- this.container.removeClass( 'select2-drop-above' );
- this.dropdown.removeClass( 'select2-drop-above' );
- },
- /**
- * Opens the dropdown
- *
- * @return {Boolean} whether or not dropdown was opened. This method will return false if, for example,
- * the dropdown is already open, or if the 'open' event listener on the element called preventDefault().
- */
- // abstract
- open: function () {
- if ( ! this.shouldOpen() ) return false;
- this.opening();
- // Only bind the document mousemove when the dropdown is visible
- $document.on( 'mousemove.select2Event', function ( e ) {
- lastMousePosition.x = e.pageX;
- lastMousePosition.y = e.pageY;
- } );
- return true;
- },
- /**
- * Performs the opening of the dropdown
- */
- // abstract
- opening: function () {
- var cid = this.containerEventName,
- scroll = 'scroll.' + cid,
- resize = 'resize.' + cid,
- orient = 'orientationchange.' + cid,
- mask;
- this.container
- .addClass( 'select2-dropdown-open' )
- .addClass( 'select2-container-active' );
- this.clearDropdownAlignmentPreference();
- if ( this.dropdown[ 0 ] !== this.body.children().last()[ 0 ] ) {
- this.dropdown.detach().appendTo( this.body );
- }
- // create the dropdown mask if doesn't already exist
- mask = $( '#select2-drop-mask' );
- if ( mask.length === 0 ) {
- mask = $( document.createElement( 'div' ) );
- mask.attr( 'id', 'select2-drop-mask' ).attr(
- 'class',
- 'select2-drop-mask'
- );
- mask.hide();
- mask.appendTo( this.body );
- mask.on( 'mousedown touchstart click', function ( e ) {
- // Prevent IE from generating a click event on the body
- reinsertElement( mask );
- var dropdown = $( '#select2-drop' ),
- self;
- if ( dropdown.length > 0 ) {
- self = dropdown.data( 'select2' );
- if ( self.opts.selectOnBlur ) {
- self.selectHighlighted( { noFocus: true } );
- }
- self.close();
- e.preventDefault();
- e.stopPropagation();
- }
- } );
- }
- // ensure the mask is always right before the dropdown
- if ( this.dropdown.prev()[ 0 ] !== mask[ 0 ] ) {
- this.dropdown.before( mask );
- }
- // move the global id to the correct dropdown
- $( '#select2-drop' ).removeAttr( 'id' );
- this.dropdown.attr( 'id', 'select2-drop' );
- // show the elements
- mask.show();
- this.positionDropdown();
- this.dropdown.show();
- this.positionDropdown();
- this.dropdown.addClass( 'select2-drop-active' );
- // attach listeners to events that can change the position of the container and thus require
- // the position of the dropdown to be updated as well so it does not come unglued from the container
- var that = this;
- this.container
- .parents()
- .add( window )
- .each( function () {
- $( this ).on(
- resize + ' ' + scroll + ' ' + orient,
- function ( e ) {
- if ( that.opened() ) that.positionDropdown();
- }
- );
- } );
- },
- // abstract
- close: function () {
- if ( ! this.opened() ) return;
- var cid = this.containerEventName,
- scroll = 'scroll.' + cid,
- resize = 'resize.' + cid,
- orient = 'orientationchange.' + cid;
- // unbind event listeners
- this.container
- .parents()
- .add( window )
- .each( function () {
- $( this ).off( scroll ).off( resize ).off( orient );
- } );
- this.clearDropdownAlignmentPreference();
- $( '#select2-drop-mask' ).hide();
- this.dropdown.removeAttr( 'id' ); // only the active dropdown has the select2-drop id
- this.dropdown.hide();
- this.container
- .removeClass( 'select2-dropdown-open' )
- .removeClass( 'select2-container-active' );
- this.results.empty();
- // Now that the dropdown is closed, unbind the global document mousemove event
- $document.off( 'mousemove.select2Event' );
- this.clearSearch();
- this.search.removeClass( 'select2-active' );
- this.opts.element.trigger( $.Event( 'select2-close' ) );
- },
- /**
- * Opens control, sets input value, and updates results.
- */
- // abstract
- externalSearch: function ( term ) {
- this.open();
- this.search.val( term );
- this.updateResults( false );
- },
- // abstract
- clearSearch: function () {},
- //abstract
- getMaximumSelectionSize: function () {
- return evaluate(
- this.opts.maximumSelectionSize,
- this.opts.element
- );
- },
- // abstract
- ensureHighlightVisible: function () {
- var results = this.results,
- children,
- index,
- child,
- hb,
- rb,
- y,
- more,
- topOffset;
- index = this.highlight();
- if ( index < 0 ) return;
- if ( index == 0 ) {
- // if the first element is highlighted scroll all the way to the top,
- // that way any unselectable headers above it will also be scrolled
- // into view
- results.scrollTop( 0 );
- return;
- }
- children = this.findHighlightableChoices().find(
- '.select2-result-label'
- );
- child = $( children[ index ] );
- topOffset = ( child.offset() || {} ).top || 0;
- hb = topOffset + child.outerHeight( true );
- // if this is the last child lets also make sure select2-more-results is visible
- if ( index === children.length - 1 ) {
- more = results.find( 'li.select2-more-results' );
- if ( more.length > 0 ) {
- hb = more.offset().top + more.outerHeight( true );
- }
- }
- rb = results.offset().top + results.outerHeight( false );
- if ( hb > rb ) {
- results.scrollTop( results.scrollTop() + ( hb - rb ) );
- }
- y = topOffset - results.offset().top;
- // make sure the top of the element is visible
- if ( y < 0 && child.css( 'display' ) != 'none' ) {
- results.scrollTop( results.scrollTop() + y ); // y is negative
- }
- },
- // abstract
- findHighlightableChoices: function () {
- return this.results.find(
- '.select2-result-selectable:not(.select2-disabled):not(.select2-selected)'
- );
- },
- // abstract
- moveHighlight: function ( delta ) {
- var choices = this.findHighlightableChoices(),
- index = this.highlight();
- while ( index > -1 && index < choices.length ) {
- index += delta;
- var choice = $( choices[ index ] );
- if (
- choice.hasClass( 'select2-result-selectable' ) &&
- ! choice.hasClass( 'select2-disabled' ) &&
- ! choice.hasClass( 'select2-selected' )
- ) {
- this.highlight( index );
- break;
- }
- }
- },
- // abstract
- highlight: function ( index ) {
- var choices = this.findHighlightableChoices(),
- choice,
- data;
- if ( arguments.length === 0 ) {
- return indexOf(
- choices.filter( '.select2-highlighted' )[ 0 ],
- choices.get()
- );
- }
- if ( index >= choices.length ) index = choices.length - 1;
- if ( index < 0 ) index = 0;
- this.removeHighlight();
- choice = $( choices[ index ] );
- choice.addClass( 'select2-highlighted' );
- // ensure assistive technology can determine the active choice
- this.search.attr(
- 'aria-activedescendant',
- choice.find( '.select2-result-label' ).attr( 'id' )
- );
- this.ensureHighlightVisible();
- this.liveRegion.text( choice.text() );
- data = choice.data( 'select2-data' );
- if ( data ) {
- this.opts.element.trigger( {
- type: 'select2-highlight',
- val: this.id( data ),
- choice: data,
- } );
- }
- },
- removeHighlight: function () {
- this.results
- .find( '.select2-highlighted' )
- .removeClass( 'select2-highlighted' );
- },
- touchMoved: function () {
- this._touchMoved = true;
- },
- clearTouchMoved: function () {
- this._touchMoved = false;
- },
- // abstract
- countSelectableResults: function () {
- return this.findHighlightableChoices().length;
- },
- // abstract
- highlightUnderEvent: function ( event ) {
- var el = $( event.target ).closest( '.select2-result-selectable' );
- if ( el.length > 0 && ! el.is( '.select2-highlighted' ) ) {
- var choices = this.findHighlightableChoices();
- this.highlight( choices.index( el ) );
- } else if ( el.length == 0 ) {
- // if we are over an unselectable item remove all highlights
- this.removeHighlight();
- }
- },
- // abstract
- loadMoreIfNeeded: function () {
- var results = this.results,
- more = results.find( 'li.select2-more-results' ),
- below, // pixels the element is below the scroll fold, below==0 is when the element is starting to be visible
- page = this.resultsPage + 1,
- self = this,
- term = this.search.val(),
- context = this.context;
- if ( more.length === 0 ) return;
- below = more.offset().top - results.offset().top - results.height();
- if ( below <= this.opts.loadMorePadding ) {
- more.addClass( 'select2-active' );
- this.opts.query( {
- element: this.opts.element,
- term: term,
- page: page,
- context: context,
- matcher: this.opts.matcher,
- callback: this.bind( function ( data ) {
- // ignore a response if the select2 has been closed before it was received
- if ( ! self.opened() ) return;
- self.opts.populateResults.call(
- this,
- results,
- data.results,
- { term: term, page: page, context: context }
- );
- self.postprocessResults( data, false, false );
- if ( data.more === true ) {
- more.detach()
- .appendTo( results )
- .html(
- self.opts.escapeMarkup(
- evaluate(
- self.opts.formatLoadMore,
- self.opts.element,
- page + 1
- )
- )
- );
- window.setTimeout( function () {
- self.loadMoreIfNeeded();
- }, 10 );
- } else {
- more.remove();
- }
- self.positionDropdown();
- self.resultsPage = page;
- self.context = data.context;
- this.opts.element.trigger( {
- type: 'select2-loaded',
- items: data,
- } );
- } ),
- } );
- }
- },
- /**
- * Default tokenizer function which does nothing
- */
- tokenize: function () {},
- /**
- * @param initial whether or not this is the call to this method right after the dropdown has been opened
- */
- // abstract
- updateResults: function ( initial ) {
- var search = this.search,
- results = this.results,
- opts = this.opts,
- data,
- self = this,
- input,
- term = search.val(),
- lastTerm = $.data( this.container, 'select2-last-term' ),
- // sequence number used to drop out-of-order responses
- queryNumber;
- // prevent duplicate queries against the same term
- if ( initial !== true && lastTerm && equal( term, lastTerm ) )
- return;
- $.data( this.container, 'select2-last-term', term );
- // if the search is currently hidden we do not alter the results
- if (
- initial !== true &&
- ( this.showSearchInput === false || ! this.opened() )
- ) {
- return;
- }
- function postRender() {
- search.removeClass( 'select2-active' );
- self.positionDropdown();
- if (
- results.find(
- '.select2-no-results,.select2-selection-limit,.select2-searching'
- ).length
- ) {
- self.liveRegion.text( results.text() );
- } else {
- self.liveRegion.text(
- self.opts.formatMatches(
- results.find(
- '.select2-result-selectable:not(".select2-selected")'
- ).length
- )
- );
- }
- }
- function render( html ) {
- results.html( html );
- postRender();
- }
- queryNumber = ++this.queryCount;
- var maxSelSize = this.getMaximumSelectionSize();
- if ( maxSelSize >= 1 ) {
- data = this.data();
- if (
- $.isArray( data ) &&
- data.length >= maxSelSize &&
- checkFormatter(
- opts.formatSelectionTooBig,
- 'formatSelectionTooBig'
- )
- ) {
- render(
- "<li class='select2-selection-limit'>" +
- evaluate(
- opts.formatSelectionTooBig,
- opts.element,
- maxSelSize
- ) +
- '</li>'
- );
- return;
- }
- }
- if ( search.val().length < opts.minimumInputLength ) {
- if (
- checkFormatter(
- opts.formatInputTooShort,
- 'formatInputTooShort'
- )
- ) {
- render(
- "<li class='select2-no-results'>" +
- evaluate(
- opts.formatInputTooShort,
- opts.element,
- search.val(),
- opts.minimumInputLength
- ) +
- '</li>'
- );
- } else {
- render( '' );
- }
- if ( initial && this.showSearch ) this.showSearch( true );
- return;
- }
- if (
- opts.maximumInputLength &&
- search.val().length > opts.maximumInputLength
- ) {
- if (
- checkFormatter(
- opts.formatInputTooLong,
- 'formatInputTooLong'
- )
- ) {
- render(
- "<li class='select2-no-results'>" +
- evaluate(
- opts.formatInputTooLong,
- opts.element,
- search.val(),
- opts.maximumInputLength
- ) +
- '</li>'
- );
- } else {
- render( '' );
- }
- return;
- }
- if (
- opts.formatSearching &&
- this.findHighlightableChoices().length === 0
- ) {
- render(
- "<li class='select2-searching'>" +
- evaluate( opts.formatSearching, opts.element ) +
- '</li>'
- );
- }
- search.addClass( 'select2-active' );
- this.removeHighlight();
- // give the tokenizer a chance to pre-process the input
- input = this.tokenize();
- if ( input != undefined && input != null ) {
- search.val( input );
- }
- this.resultsPage = 1;
- opts.query( {
- element: opts.element,
- term: search.val(),
- page: this.resultsPage,
- context: null,
- matcher: opts.matcher,
- callback: this.bind( function ( data ) {
- var def; // default choice
- // ignore old responses
- if ( queryNumber != this.queryCount ) {
- return;
- }
- // ignore a response if the select2 has been closed before it was received
- if ( ! this.opened() ) {
- this.search.removeClass( 'select2-active' );
- return;
- }
- // handle ajax error
- if (
- data.hasError !== undefined &&
- checkFormatter(
- opts.formatAjaxError,
- 'formatAjaxError'
- )
- ) {
- render(
- "<li class='select2-ajax-error'>" +
- evaluate(
- opts.formatAjaxError,
- opts.element,
- data.jqXHR,
- data.textStatus,
- data.errorThrown
- ) +
- '</li>'
- );
- return;
- }
- // save context, if any
- this.context =
- data.context === undefined ? null : data.context;
- // create a default choice and prepend it to the list
- if ( this.opts.createSearchChoice && search.val() !== '' ) {
- def = this.opts.createSearchChoice.call(
- self,
- search.val(),
- data.results
- );
- if (
- def !== undefined &&
- def !== null &&
- self.id( def ) !== undefined &&
- self.id( def ) !== null
- ) {
- if (
- $( data.results ).filter( function () {
- return equal(
- self.id( this ),
- self.id( def )
- );
- } ).length === 0
- ) {
- this.opts.createSearchChoicePosition(
- data.results,
- def
- );
- }
- }
- }
- if (
- data.results.length === 0 &&
- checkFormatter(
- opts.formatNoMatches,
- 'formatNoMatches'
- )
- ) {
- render(
- "<li class='select2-no-results'>" +
- evaluate(
- opts.formatNoMatches,
- opts.element,
- search.val()
- ) +
- '</li>'
- );
- return;
- }
- results.empty();
- self.opts.populateResults.call(
- this,
- results,
- data.results,
- {
- term: search.val(),
- page: this.resultsPage,
- context: null,
- }
- );
- if (
- data.more === true &&
- checkFormatter( opts.formatLoadMore, 'formatLoadMore' )
- ) {
- results.append(
- "<li class='select2-more-results'>" +
- opts.escapeMarkup(
- evaluate(
- opts.formatLoadMore,
- opts.element,
- this.resultsPage
- )
- ) +
- '</li>'
- );
- window.setTimeout( function () {
- self.loadMoreIfNeeded();
- }, 10 );
- }
- this.postprocessResults( data, initial );
- postRender();
- this.opts.element.trigger( {
- type: 'select2-loaded',
- items: data,
- } );
- } ),
- } );
- },
- // abstract
- cancel: function () {
- this.close();
- },
- // abstract
- blur: function () {
- // if selectOnBlur == true, select the currently highlighted option
- if ( this.opts.selectOnBlur )
- this.selectHighlighted( { noFocus: true } );
- this.close();
- this.container.removeClass( 'select2-container-active' );
- // synonymous to .is(':focus'), which is available in jquery >= 1.6
- if ( this.search[ 0 ] === document.activeElement ) {
- this.search.blur();
- }
- this.clearSearch();
- this.selection
- .find( '.select2-search-choice-focus' )
- .removeClass( 'select2-search-choice-focus' );
- },
- // abstract
- focusSearch: function () {
- focus( this.search );
- },
- // abstract
- selectHighlighted: function ( options ) {
- if ( this._touchMoved ) {
- this.clearTouchMoved();
- return;
- }
- var index = this.highlight(),
- highlighted = this.results.find( '.select2-highlighted' ),
- data = highlighted
- .closest( '.select2-result' )
- .data( 'select2-data' );
- if ( data ) {
- this.highlight( index );
- this.onSelect( data, options );
- } else if ( options && options.noFocus ) {
- this.close();
- }
- },
- // abstract
- getPlaceholder: function () {
- var placeholderOption;
- return (
- this.opts.element.attr( 'placeholder' ) ||
- this.opts.element.attr( 'data-placeholder' ) || // jquery 1.4 compat
- this.opts.element.data( 'placeholder' ) ||
- this.opts.placeholder ||
- ( ( placeholderOption = this.getPlaceholderOption() ) !==
- undefined
- ? placeholderOption.text()
- : undefined )
- );
- },
- // abstract
- getPlaceholderOption: function () {
- if ( this.select ) {
- var firstOption = this.select.children( 'option' ).first();
- if ( this.opts.placeholderOption !== undefined ) {
- //Determine the placeholder option based on the specified placeholderOption setting
- return (
- ( this.opts.placeholderOption === 'first' &&
- firstOption ) ||
- ( typeof this.opts.placeholderOption === 'function' &&
- this.opts.placeholderOption( this.select ) )
- );
- } else if (
- $.trim( firstOption.text() ) === '' &&
- firstOption.val() === ''
- ) {
- //No explicit placeholder option specified, use the first if it's blank
- return firstOption;
- }
- }
- },
- /**
- * Get the desired width for the container element. This is
- * derived first from option `width` passed to select2, then
- * the inline 'style' on the original element, and finally
- * falls back to the jQuery calculated element width.
- */
- // abstract
- initContainerWidth: function () {
- function resolveContainerWidth() {
- var style, attrs, matches, i, l, attr;
- if ( this.opts.width === 'off' ) {
- return null;
- } else if ( this.opts.width === 'element' ) {
- return this.opts.element.outerWidth( false ) === 0
- ? 'auto'
- : this.opts.element.outerWidth( false ) + 'px';
- } else if (
- this.opts.width === 'copy' ||
- this.opts.width === 'resolve'
- ) {
- // check if there is inline style on the element that contains width
- style = this.opts.element.attr( 'style' );
- if ( style !== undefined ) {
- attrs = style.split( ';' );
- for ( i = 0, l = attrs.length; i < l; i = i + 1 ) {
- attr = attrs[ i ].replace( /\s/g, '' );
- matches = attr.match(
- /^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i
- );
- if ( matches !== null && matches.length >= 1 )
- return matches[ 1 ];
- }
- }
- if ( this.opts.width === 'resolve' ) {
- // next check if css('width') can resolve a width that is percent based, this is sometimes possible
- // when attached to input type=hidden or elements hidden via css
- style = this.opts.element.css( 'width' );
- if ( style.indexOf( '%' ) > 0 ) return style;
- // finally, fallback on the calculated width of the element
- return this.opts.element.outerWidth( false ) === 0
- ? 'auto'
- : this.opts.element.outerWidth( false ) + 'px';
- }
- return null;
- } else if ( $.isFunction( this.opts.width ) ) {
- return this.opts.width();
- } else {
- return this.opts.width;
- }
- }
- var width = resolveContainerWidth.call( this );
- if ( width !== null ) {
- this.container.css( 'width', width );
- }
- },
- } );
- SingleSelect2 = clazz( AbstractSelect2, {
- // single
- createContainer: function () {
- var container = $( document.createElement( 'div' ) )
- .attr( {
- class: 'select2-container',
- } )
- .html(
- [
- "<a href='javascript:void(0)' class='select2-choice' tabindex='-1'>",
- " <span class='select2-chosen'> </span><abbr class='select2-search-choice-close'></abbr>",
- " <span class='select2-arrow' role='presentation'><b role='presentation'></b></span>",
- '</a>',
- "<label for='' class='select2-offscreen'></label>",
- "<input class='select2-focusser select2-offscreen' type='text' aria-haspopup='true' role='button' />",
- "<div class='select2-drop select2-display-none'>",
- " <div class='select2-search'>",
- " <label for='' class='select2-offscreen'></label>",
- " <input type='text' autocomplete='off' autocorrect='off' autocapitalize='off' spellcheck='false' class='select2-input' role='combobox' aria-expanded='true'",
- " aria-autocomplete='list' />",
- ' </div>',
- " <ul class='select2-results' role='listbox'>",
- ' </ul>',
- '</div>',
- ].join( '' )
- );
- return container;
- },
- // single
- enableInterface: function () {
- if ( this.parent.enableInterface.apply( this, arguments ) ) {
- this.focusser.prop( 'disabled', ! this.isInterfaceEnabled() );
- }
- },
- // single
- opening: function () {
- var el, range, len;
- if ( this.opts.minimumResultsForSearch >= 0 ) {
- this.showSearch( true );
- }
- this.parent.opening.apply( this, arguments );
- if ( this.showSearchInput !== false ) {
- // IE appends focusser.val() at the end of field :/ so we manually insert it at the beginning using a range
- // all other browsers handle this just fine
- this.search.val( this.focusser.val() );
- }
- if ( this.opts.shouldFocusInput( this ) ) {
- this.search.focus();
- // move the cursor to the end after focussing, otherwise it will be at the beginning and
- // new text will appear *before* focusser.val()
- el = this.search.get( 0 );
- if ( el.createTextRange ) {
- range = el.createTextRange();
- range.collapse( false );
- range.select();
- } else if ( el.setSelectionRange ) {
- len = this.search.val().length;
- el.setSelectionRange( len, len );
- }
- }
- // initializes search's value with nextSearchTerm (if defined by user)
- // ignore nextSearchTerm if the dropdown is opened by the user pressing a letter
- if ( this.search.val() === '' ) {
- if ( this.nextSearchTerm != undefined ) {
- this.search.val( this.nextSearchTerm );
- this.search.select();
- }
- }
- this.focusser.prop( 'disabled', true ).val( '' );
- this.updateResults( true );
- this.opts.element.trigger( $.Event( 'select2-open' ) );
- },
- // single
- close: function () {
- if ( ! this.opened() ) return;
- this.parent.close.apply( this, arguments );
- this.focusser.prop( 'disabled', false );
- if ( this.opts.shouldFocusInput( this ) ) {
- this.focusser.focus();
- }
- },
- // single
- focus: function () {
- if ( this.opened() ) {
- this.close();
- } else {
- this.focusser.prop( 'disabled', false );
- if ( this.opts.shouldFocusInput( this ) ) {
- this.focusser.focus();
- }
- }
- },
- // single
- isFocused: function () {
- return this.container.hasClass( 'select2-container-active' );
- },
- // single
- cancel: function () {
- this.parent.cancel.apply( this, arguments );
- this.focusser.prop( 'disabled', false );
- if ( this.opts.shouldFocusInput( this ) ) {
- this.focusser.focus();
- }
- },
- // single
- destroy: function () {
- $( "label[for='" + this.focusser.attr( 'id' ) + "']" ).attr(
- 'for',
- this.opts.element.attr( 'id' )
- );
- this.parent.destroy.apply( this, arguments );
- cleanupJQueryElements.call( this, 'selection', 'focusser' );
- },
- // single
- initContainer: function () {
- var selection,
- container = this.container,
- dropdown = this.dropdown,
- idSuffix = nextUid(),
- elementLabel;
- if ( this.opts.minimumResultsForSearch < 0 ) {
- this.showSearch( false );
- } else {
- this.showSearch( true );
- }
- this.selection = selection = container.find( '.select2-choice' );
- this.focusser = container.find( '.select2-focusser' );
- // add aria associations
- selection
- .find( '.select2-chosen' )
- .attr( 'id', 'select2-chosen-' + idSuffix );
- this.focusser.attr(
- 'aria-labelledby',
- 'select2-chosen-' + idSuffix
- );
- this.results.attr( 'id', 'select2-results-' + idSuffix );
- this.search.attr( 'aria-owns', 'select2-results-' + idSuffix );
- // rewrite labels from original element to focusser
- this.focusser.attr( 'id', 's2id_autogen' + idSuffix );
- elementLabel = $(
- "label[for='" + this.opts.element.attr( 'id' ) + "']"
- );
- this.opts.element.focus(
- this.bind( function () {
- this.focus();
- } )
- );
- this.focusser
- .prev()
- .text( elementLabel.text() )
- .attr( 'for', this.focusser.attr( 'id' ) );
- // Ensure the original element retains an accessible name
- var originalTitle = this.opts.element.attr( 'title' );
- this.opts.element.attr(
- 'title',
- originalTitle || elementLabel.text()
- );
- this.focusser.attr( 'tabindex', this.elementTabIndex );
- // write label for search field using the label from the focusser element
- this.search.attr( 'id', this.focusser.attr( 'id' ) + '_search' );
- this.search
- .prev()
- .text(
- $(
- "label[for='" + this.focusser.attr( 'id' ) + "']"
- ).text()
- )
- .attr( 'for', this.search.attr( 'id' ) );
- this.search.on(
- 'keydown',
- this.bind( function ( e ) {
- if ( ! this.isInterfaceEnabled() ) return;
- // filter 229 keyCodes (input method editor is processing key input)
- if ( 229 == e.keyCode ) return;
- if (
- e.which === KEY.PAGE_UP ||
- e.which === KEY.PAGE_DOWN
- ) {
- // prevent the page from scrolling
- killEvent( e );
- return;
- }
- switch ( e.which ) {
- case KEY.UP:
- case KEY.DOWN:
- this.moveHighlight( e.which === KEY.UP ? -1 : 1 );
- killEvent( e );
- return;
- case KEY.ENTER:
- this.selectHighlighted();
- killEvent( e );
- return;
- case KEY.TAB:
- this.selectHighlighted( { noFocus: true } );
- return;
- case KEY.ESC:
- this.cancel( e );
- killEvent( e );
- return;
- }
- } )
- );
- this.search.on(
- 'blur',
- this.bind( function ( e ) {
- // a workaround for chrome to keep the search field focussed when the scroll bar is used to scroll the dropdown.
- // without this the search field loses focus which is annoying
- if ( document.activeElement === this.body.get( 0 ) ) {
- window.setTimeout(
- this.bind( function () {
- if ( this.opened() ) {
- this.search.focus();
- }
- } ),
- 0
- );
- }
- } )
- );
- this.focusser.on(
- 'keydown',
- this.bind( function ( e ) {
- if ( ! this.isInterfaceEnabled() ) return;
- if (
- e.which === KEY.TAB ||
- KEY.isControl( e ) ||
- KEY.isFunctionKey( e ) ||
- e.which === KEY.ESC
- ) {
- return;
- }
- if (
- this.opts.openOnEnter === false &&
- e.which === KEY.ENTER
- ) {
- killEvent( e );
- return;
- }
- if (
- e.which == KEY.DOWN ||
- e.which == KEY.UP ||
- ( e.which == KEY.ENTER && this.opts.openOnEnter )
- ) {
- if ( e.altKey || e.ctrlKey || e.shiftKey || e.metaKey )
- return;
- this.open();
- killEvent( e );
- return;
- }
- if ( e.which == KEY.DELETE || e.which == KEY.BACKSPACE ) {
- if ( this.opts.allowClear ) {
- this.clear();
- }
- killEvent( e );
- return;
- }
- } )
- );
- installKeyUpChangeEvent( this.focusser );
- this.focusser.on(
- 'keyup-change input',
- this.bind( function ( e ) {
- if ( this.opts.minimumResultsForSearch >= 0 ) {
- e.stopPropagation();
- if ( this.opened() ) return;
- this.open();
- }
- } )
- );
- selection.on(
- 'mousedown touchstart',
- 'abbr',
- this.bind( function ( e ) {
- if ( ! this.isInterfaceEnabled() ) {
- return;
- }
- this.clear();
- killEventImmediately( e );
- this.close();
- if ( this.selection ) {
- this.selection.focus();
- }
- } )
- );
- selection.on(
- 'mousedown touchstart',
- this.bind( function ( e ) {
- // Prevent IE from generating a click event on the body
- reinsertElement( selection );
- if (
- ! this.container.hasClass( 'select2-container-active' )
- ) {
- this.opts.element.trigger( $.Event( 'select2-focus' ) );
- }
- if ( this.opened() ) {
- this.close();
- } else if ( this.isInterfaceEnabled() ) {
- this.open();
- }
- killEvent( e );
- } )
- );
- dropdown.on(
- 'mousedown touchstart',
- this.bind( function () {
- if ( this.opts.shouldFocusInput( this ) ) {
- this.search.focus();
- }
- } )
- );
- selection.on(
- 'focus',
- this.bind( function ( e ) {
- killEvent( e );
- } )
- );
- this.focusser
- .on(
- 'focus',
- this.bind( function () {
- if (
- ! this.container.hasClass(
- 'select2-container-active'
- )
- ) {
- this.opts.element.trigger(
- $.Event( 'select2-focus' )
- );
- }
- this.container.addClass( 'select2-container-active' );
- } )
- )
- .on(
- 'blur',
- this.bind( function () {
- if ( ! this.opened() ) {
- this.container.removeClass(
- 'select2-container-active'
- );
- this.opts.element.trigger(
- $.Event( 'select2-blur' )
- );
- }
- } )
- );
- this.search.on(
- 'focus',
- this.bind( function () {
- if (
- ! this.container.hasClass( 'select2-container-active' )
- ) {
- this.opts.element.trigger( $.Event( 'select2-focus' ) );
- }
- this.container.addClass( 'select2-container-active' );
- } )
- );
- this.initContainerWidth();
- this.opts.element.hide();
- this.setPlaceholder();
- },
- // single
- clear: function ( triggerChange ) {
- var data = this.selection.data( 'select2-data' );
- if ( data ) {
- // guard against queued quick consecutive clicks
- var evt = $.Event( 'select2-clearing' );
- this.opts.element.trigger( evt );
- if ( evt.isDefaultPrevented() ) {
- return;
- }
- var placeholderOption = this.getPlaceholderOption();
- this.opts.element.val(
- placeholderOption ? placeholderOption.val() : ''
- );
- this.selection.find( '.select2-chosen' ).empty();
- this.selection.removeData( 'select2-data' );
- this.setPlaceholder();
- if ( triggerChange !== false ) {
- this.opts.element.trigger( {
- type: 'select2-removed',
- val: this.id( data ),
- choice: data,
- } );
- this.triggerChange( { removed: data } );
- }
- }
- },
- /**
- * Sets selection based on source element's value
- */
- // single
- initSelection: function () {
- var selected;
- if ( this.isPlaceholderOptionSelected() ) {
- this.updateSelection( null );
- this.close();
- this.setPlaceholder();
- } else {
- var self = this;
- this.opts.initSelection.call(
- null,
- this.opts.element,
- function ( selected ) {
- if ( selected !== undefined && selected !== null ) {
- self.updateSelection( selected );
- self.close();
- self.setPlaceholder();
- self.nextSearchTerm = self.opts.nextSearchTerm(
- selected,
- self.search.val()
- );
- }
- }
- );
- }
- },
- isPlaceholderOptionSelected: function () {
- var placeholderOption;
- if ( this.getPlaceholder() === undefined ) return false; // no placeholder specified so no option should be considered
- return (
- ( ( placeholderOption = this.getPlaceholderOption() ) !==
- undefined &&
- placeholderOption.prop( 'selected' ) ) ||
- this.opts.element.val() === '' ||
- this.opts.element.val() === undefined ||
- this.opts.element.val() === null
- );
- },
- // single
- prepareOpts: function () {
- var opts = this.parent.prepareOpts.apply( this, arguments ),
- self = this;
- if ( opts.element.get( 0 ).tagName.toLowerCase() === 'select' ) {
- // install the selection initializer
- opts.initSelection = function ( element, callback ) {
- var selected = element
- .find( 'option' )
- .filter( function () {
- return this.selected && ! this.disabled;
- } );
- // a single select box always has a value, no need to null check 'selected'
- callback( self.optionToData( selected ) );
- };
- } else if ( 'data' in opts ) {
- // install default initSelection when applied to hidden input and data is local
- opts.initSelection =
- opts.initSelection ||
- function ( element, callback ) {
- var id = element.val();
- //search in data by id, storing the actual matching item
- var match = null;
- opts.query( {
- matcher: function ( term, text, el ) {
- var is_match = equal( id, opts.id( el ) );
- if ( is_match ) {
- match = el;
- }
- return is_match;
- },
- callback: ! $.isFunction( callback )
- ? $.noop
- : function () {
- callback( match );
- },
- } );
- };
- }
- return opts;
- },
- // single
- getPlaceholder: function () {
- // if a placeholder is specified on a single select without a valid placeholder option ignore it
- if ( this.select ) {
- if ( this.getPlaceholderOption() === undefined ) {
- return undefined;
- }
- }
- return this.parent.getPlaceholder.apply( this, arguments );
- },
- // single
- setPlaceholder: function () {
- var placeholder = this.getPlaceholder();
- if (
- this.isPlaceholderOptionSelected() &&
- placeholder !== undefined
- ) {
- // check for a placeholder option if attached to a select
- if ( this.select && this.getPlaceholderOption() === undefined )
- return;
- this.selection
- .find( '.select2-chosen' )
- .html( this.opts.escapeMarkup( placeholder ) );
- this.selection.addClass( 'select2-default' );
- this.container.removeClass( 'select2-allowclear' );
- }
- },
- // single
- postprocessResults: function ( data, initial, noHighlightUpdate ) {
- var selected = 0,
- self = this,
- showSearchInput = true;
- // find the selected element in the result list
- this.findHighlightableChoices().each2( function ( i, elm ) {
- if (
- equal(
- self.id( elm.data( 'select2-data' ) ),
- self.opts.element.val()
- )
- ) {
- selected = i;
- return false;
- }
- } );
- // and highlight it
- if ( noHighlightUpdate !== false ) {
- if ( initial === true && selected >= 0 ) {
- this.highlight( selected );
- } else {
- this.highlight( 0 );
- }
- }
- // hide the search box if this is the first we got the results and there are enough of them for search
- if ( initial === true ) {
- var min = this.opts.minimumResultsForSearch;
- if ( min >= 0 ) {
- this.showSearch( countResults( data.results ) >= min );
- }
- }
- },
- // single
- showSearch: function ( showSearchInput ) {
- if ( this.showSearchInput === showSearchInput ) return;
- this.showSearchInput = showSearchInput;
- this.dropdown
- .find( '.select2-search' )
- .toggleClass( 'select2-search-hidden', ! showSearchInput );
- this.dropdown
- .find( '.select2-search' )
- .toggleClass( 'select2-offscreen', ! showSearchInput );
- //add "select2-with-searchbox" to the container if search box is shown
- $( this.dropdown, this.container ).toggleClass(
- 'select2-with-searchbox',
- showSearchInput
- );
- },
- // single
- onSelect: function ( data, options ) {
- if ( ! this.triggerSelect( data ) ) {
- return;
- }
- var old = this.opts.element.val(),
- oldData = this.data();
- this.opts.element.val( this.id( data ) );
- this.updateSelection( data );
- this.opts.element.trigger( {
- type: 'select2-selected',
- val: this.id( data ),
- choice: data,
- } );
- this.nextSearchTerm = this.opts.nextSearchTerm(
- data,
- this.search.val()
- );
- this.close();
- if (
- ( ! options || ! options.noFocus ) &&
- this.opts.shouldFocusInput( this )
- ) {
- this.focusser.focus();
- }
- if ( ! equal( old, this.id( data ) ) ) {
- this.triggerChange( { added: data, removed: oldData } );
- }
- },
- // single
- updateSelection: function ( data ) {
- var container = this.selection.find( '.select2-chosen' ),
- formatted,
- cssClass;
- this.selection.data( 'select2-data', data );
- container.empty();
- if ( data !== null ) {
- formatted = this.opts.formatSelection(
- data,
- container,
- this.opts.escapeMarkup
- );
- }
- if ( formatted !== undefined ) {
- container.append( formatted );
- }
- cssClass = this.opts.formatSelectionCssClass( data, container );
- if ( cssClass !== undefined ) {
- container.addClass( cssClass );
- }
- this.selection.removeClass( 'select2-default' );
- if ( this.opts.allowClear && this.getPlaceholder() !== undefined ) {
- this.container.addClass( 'select2-allowclear' );
- }
- },
- // single
- val: function () {
- var val,
- triggerChange = false,
- data = null,
- self = this,
- oldData = this.data();
- if ( arguments.length === 0 ) {
- return this.opts.element.val();
- }
- val = arguments[ 0 ];
- if ( arguments.length > 1 ) {
- triggerChange = arguments[ 1 ];
- }
- if ( this.select ) {
- this.select
- .val( val )
- .find( 'option' )
- .filter( function () {
- return this.selected;
- } )
- .each2( function ( i, elm ) {
- data = self.optionToData( elm );
- return false;
- } );
- this.updateSelection( data );
- this.setPlaceholder();
- if ( triggerChange ) {
- this.triggerChange( { added: data, removed: oldData } );
- }
- } else {
- // val is an id. !val is true for [undefined,null,'',0] - 0 is legal
- if ( ! val && val !== 0 ) {
- this.clear( triggerChange );
- return;
- }
- if ( this.opts.initSelection === undefined ) {
- throw new Error(
- 'cannot call val() if initSelection() is not defined'
- );
- }
- this.opts.element.val( val );
- this.opts.initSelection( this.opts.element, function ( data ) {
- self.opts.element.val( ! data ? '' : self.id( data ) );
- self.updateSelection( data );
- self.setPlaceholder();
- if ( triggerChange ) {
- self.triggerChange( { added: data, removed: oldData } );
- }
- } );
- }
- },
- // single
- clearSearch: function () {
- this.search.val( '' );
- this.focusser.val( '' );
- },
- // single
- data: function ( value ) {
- var data,
- triggerChange = false;
- if ( arguments.length === 0 ) {
- data = this.selection.data( 'select2-data' );
- if ( data == undefined ) data = null;
- return data;
- } else {
- if ( arguments.length > 1 ) {
- triggerChange = arguments[ 1 ];
- }
- if ( ! value ) {
- this.clear( triggerChange );
- } else {
- data = this.data();
- this.opts.element.val( ! value ? '' : this.id( value ) );
- this.updateSelection( value );
- if ( triggerChange ) {
- this.triggerChange( { added: value, removed: data } );
- }
- }
- }
- },
- } );
- MultiSelect2 = clazz( AbstractSelect2, {
- // multi
- createContainer: function () {
- var container = $( document.createElement( 'div' ) )
- .attr( {
- class: 'select2-container select2-container-multi',
- } )
- .html(
- [
- "<ul class='select2-choices'>",
- " <li class='select2-search-field'>",
- " <label for='' class='select2-offscreen'></label>",
- " <input type='text' autocomplete='off' autocorrect='off' autocapitalize='off' spellcheck='false' class='select2-input'>",
- ' </li>',
- '</ul>',
- "<div class='select2-drop select2-drop-multi select2-display-none'>",
- " <ul class='select2-results'>",
- ' </ul>',
- '</div>',
- ].join( '' )
- );
- return container;
- },
- // multi
- prepareOpts: function () {
- var opts = this.parent.prepareOpts.apply( this, arguments ),
- self = this;
- // TODO validate placeholder is a string if specified
- if ( opts.element.get( 0 ).tagName.toLowerCase() === 'select' ) {
- // install the selection initializer
- opts.initSelection = function ( element, callback ) {
- var data = [];
- element
- .find( 'option' )
- .filter( function () {
- return this.selected && ! this.disabled;
- } )
- .each2( function ( i, elm ) {
- data.push( self.optionToData( elm ) );
- } );
- callback( data );
- };
- } else if ( 'data' in opts ) {
- // install default initSelection when applied to hidden input and data is local
- opts.initSelection =
- opts.initSelection ||
- function ( element, callback ) {
- var ids = splitVal(
- element.val(),
- opts.separator,
- opts.transformVal
- );
- //search in data by array of ids, storing matching items in a list
- var matches = [];
- opts.query( {
- matcher: function ( term, text, el ) {
- var is_match = $.grep( ids, function ( id ) {
- return equal( id, opts.id( el ) );
- } ).length;
- if ( is_match ) {
- matches.push( el );
- }
- return is_match;
- },
- callback: ! $.isFunction( callback )
- ? $.noop
- : function () {
- // reorder matches based on the order they appear in the ids array because right now
- // they are in the order in which they appear in data array
- var ordered = [];
- for ( var i = 0; i < ids.length; i++ ) {
- var id = ids[ i ];
- for (
- var j = 0;
- j < matches.length;
- j++
- ) {
- var match = matches[ j ];
- if (
- equal(
- id,
- opts.id( match )
- )
- ) {
- ordered.push( match );
- matches.splice( j, 1 );
- break;
- }
- }
- }
- callback( ordered );
- },
- } );
- };
- }
- return opts;
- },
- // multi
- selectChoice: function ( choice ) {
- var selected = this.container.find(
- '.select2-search-choice-focus'
- );
- if ( selected.length && choice && choice[ 0 ] == selected[ 0 ] ) {
- } else {
- if ( selected.length ) {
- this.opts.element.trigger( 'choice-deselected', selected );
- }
- selected.removeClass( 'select2-search-choice-focus' );
- if ( choice && choice.length ) {
- this.close();
- choice.addClass( 'select2-search-choice-focus' );
- this.opts.element.trigger( 'choice-selected', choice );
- }
- }
- },
- // multi
- destroy: function () {
- $( "label[for='" + this.search.attr( 'id' ) + "']" ).attr(
- 'for',
- this.opts.element.attr( 'id' )
- );
- this.parent.destroy.apply( this, arguments );
- cleanupJQueryElements.call( this, 'searchContainer', 'selection' );
- },
- // multi
- initContainer: function () {
- var selector = '.select2-choices',
- selection;
- this.searchContainer = this.container.find(
- '.select2-search-field'
- );
- this.selection = selection = this.container.find( selector );
- var _this = this;
- this.selection.on(
- 'click',
- '.select2-container:not(.select2-container-disabled) .select2-search-choice:not(.select2-locked)',
- function ( e ) {
- _this.search[ 0 ].focus();
- _this.selectChoice( $( this ) );
- }
- );
- // rewrite labels from original element to focusser
- this.search.attr( 'id', 's2id_autogen' + nextUid() );
- this.search
- .prev()
- .text(
- $(
- "label[for='" + this.opts.element.attr( 'id' ) + "']"
- ).text()
- )
- .attr( 'for', this.search.attr( 'id' ) );
- this.opts.element.focus(
- this.bind( function () {
- this.focus();
- } )
- );
- this.search.on(
- 'input paste',
- this.bind( function () {
- if (
- this.search.attr( 'placeholder' ) &&
- this.search.val().length == 0
- )
- return;
- if ( ! this.isInterfaceEnabled() ) return;
- if ( ! this.opened() ) {
- this.open();
- }
- } )
- );
- this.search.attr( 'tabindex', this.elementTabIndex );
- this.keydowns = 0;
- this.search.on(
- 'keydown',
- this.bind( function ( e ) {
- if ( ! this.isInterfaceEnabled() ) return;
- ++this.keydowns;
- var selected = selection.find(
- '.select2-search-choice-focus'
- );
- var prev = selected.prev(
- '.select2-search-choice:not(.select2-locked)'
- );
- var next = selected.next(
- '.select2-search-choice:not(.select2-locked)'
- );
- var pos = getCursorInfo( this.search );
- if (
- selected.length &&
- ( e.which == KEY.LEFT ||
- e.which == KEY.RIGHT ||
- e.which == KEY.BACKSPACE ||
- e.which == KEY.DELETE ||
- e.which == KEY.ENTER )
- ) {
- var selectedChoice = selected;
- if ( e.which == KEY.LEFT && prev.length ) {
- selectedChoice = prev;
- } else if ( e.which == KEY.RIGHT ) {
- selectedChoice = next.length ? next : null;
- } else if ( e.which === KEY.BACKSPACE ) {
- if ( this.unselect( selected.first() ) ) {
- this.search.width( 10 );
- selectedChoice = prev.length ? prev : next;
- }
- } else if ( e.which == KEY.DELETE ) {
- if ( this.unselect( selected.first() ) ) {
- this.search.width( 10 );
- selectedChoice = next.length ? next : null;
- }
- } else if ( e.which == KEY.ENTER ) {
- selectedChoice = null;
- }
- this.selectChoice( selectedChoice );
- killEvent( e );
- if ( ! selectedChoice || ! selectedChoice.length ) {
- this.open();
- }
- return;
- } else if (
- ( ( e.which === KEY.BACKSPACE && this.keydowns == 1 ) ||
- e.which == KEY.LEFT ) &&
- pos.offset == 0 &&
- ! pos.length
- ) {
- this.selectChoice(
- selection
- .find(
- '.select2-search-choice:not(.select2-locked)'
- )
- .last()
- );
- killEvent( e );
- return;
- } else {
- this.selectChoice( null );
- }
- if ( this.opened() ) {
- switch ( e.which ) {
- case KEY.UP:
- case KEY.DOWN:
- this.moveHighlight(
- e.which === KEY.UP ? -1 : 1
- );
- killEvent( e );
- return;
- case KEY.ENTER:
- this.selectHighlighted();
- killEvent( e );
- return;
- case KEY.TAB:
- this.selectHighlighted( { noFocus: true } );
- this.close();
- return;
- case KEY.ESC:
- this.cancel( e );
- killEvent( e );
- return;
- }
- }
- if (
- e.which === KEY.TAB ||
- KEY.isControl( e ) ||
- KEY.isFunctionKey( e ) ||
- e.which === KEY.BACKSPACE ||
- e.which === KEY.ESC
- ) {
- return;
- }
- if ( e.which === KEY.ENTER ) {
- if ( this.opts.openOnEnter === false ) {
- return;
- } else if (
- e.altKey ||
- e.ctrlKey ||
- e.shiftKey ||
- e.metaKey
- ) {
- return;
- }
- }
- this.open();
- if (
- e.which === KEY.PAGE_UP ||
- e.which === KEY.PAGE_DOWN
- ) {
- // prevent the page from scrolling
- killEvent( e );
- }
- if ( e.which === KEY.ENTER ) {
- // prevent form from being submitted
- killEvent( e );
- }
- } )
- );
- this.search.on(
- 'keyup',
- this.bind( function ( e ) {
- this.keydowns = 0;
- this.resizeSearch();
- } )
- );
- this.search.on(
- 'blur',
- this.bind( function ( e ) {
- this.container.removeClass( 'select2-container-active' );
- this.search.removeClass( 'select2-focused' );
- this.selectChoice( null );
- if ( ! this.opened() ) this.clearSearch();
- e.stopImmediatePropagation();
- this.opts.element.trigger( $.Event( 'select2-blur' ) );
- } )
- );
- this.container.on(
- 'click',
- selector,
- this.bind( function ( e ) {
- if ( ! this.isInterfaceEnabled() ) return;
- if (
- $( e.target ).closest( '.select2-search-choice' )
- .length > 0
- ) {
- // clicked inside a select2 search choice, do not open
- return;
- }
- this.selectChoice( null );
- this.clearPlaceholder();
- if (
- ! this.container.hasClass( 'select2-container-active' )
- ) {
- this.opts.element.trigger( $.Event( 'select2-focus' ) );
- }
- this.open();
- this.focusSearch();
- e.preventDefault();
- } )
- );
- this.container.on(
- 'focus',
- selector,
- this.bind( function () {
- if ( ! this.isInterfaceEnabled() ) return;
- if (
- ! this.container.hasClass( 'select2-container-active' )
- ) {
- this.opts.element.trigger( $.Event( 'select2-focus' ) );
- }
- this.container.addClass( 'select2-container-active' );
- this.dropdown.addClass( 'select2-drop-active' );
- this.clearPlaceholder();
- } )
- );
- this.initContainerWidth();
- this.opts.element.hide();
- // set the placeholder if necessary
- this.clearSearch();
- },
- // multi
- enableInterface: function () {
- if ( this.parent.enableInterface.apply( this, arguments ) ) {
- this.search.prop( 'disabled', ! this.isInterfaceEnabled() );
- }
- },
- // multi
- initSelection: function () {
- var data;
- if (
- this.opts.element.val() === '' &&
- this.opts.element.text() === ''
- ) {
- this.updateSelection( [] );
- this.close();
- // set the placeholder if necessary
- this.clearSearch();
- }
- if ( this.select || this.opts.element.val() !== '' ) {
- var self = this;
- this.opts.initSelection.call(
- null,
- this.opts.element,
- function ( data ) {
- if ( data !== undefined && data !== null ) {
- self.updateSelection( data );
- self.close();
- // set the placeholder if necessary
- self.clearSearch();
- }
- }
- );
- }
- },
- // multi
- clearSearch: function () {
- var placeholder = this.getPlaceholder(),
- maxWidth = this.getMaxSearchWidth();
- if (
- placeholder !== undefined &&
- this.getVal().length === 0 &&
- this.search.hasClass( 'select2-focused' ) === false
- ) {
- this.search.val( placeholder ).addClass( 'select2-default' );
- // stretch the search box to full width of the container so as much of the placeholder is visible as possible
- // we could call this.resizeSearch(), but we do not because that requires a sizer and we do not want to create one so early because of a firefox bug, see #944
- this.search.width(
- maxWidth > 0 ? maxWidth : this.container.css( 'width' )
- );
- } else {
- this.search.val( '' ).width( 10 );
- }
- },
- // multi
- clearPlaceholder: function () {
- if ( this.search.hasClass( 'select2-default' ) ) {
- this.search.val( '' ).removeClass( 'select2-default' );
- }
- },
- // multi
- opening: function () {
- this.clearPlaceholder(); // should be done before super so placeholder is not used to search
- this.resizeSearch();
- this.parent.opening.apply( this, arguments );
- this.focusSearch();
- // initializes search's value with nextSearchTerm (if defined by user)
- // ignore nextSearchTerm if the dropdown is opened by the user pressing a letter
- if ( this.search.val() === '' ) {
- if ( this.nextSearchTerm != undefined ) {
- this.search.val( this.nextSearchTerm );
- this.search.select();
- }
- }
- this.updateResults( true );
- if ( this.opts.shouldFocusInput( this ) ) {
- this.search.focus();
- }
- this.opts.element.trigger( $.Event( 'select2-open' ) );
- },
- // multi
- close: function () {
- if ( ! this.opened() ) return;
- this.parent.close.apply( this, arguments );
- },
- // multi
- focus: function () {
- this.close();
- this.search.focus();
- },
- // multi
- isFocused: function () {
- return this.search.hasClass( 'select2-focused' );
- },
- // multi
- updateSelection: function ( data ) {
- var ids = [],
- filtered = [],
- self = this;
- // filter out duplicates
- $( data ).each( function () {
- if ( indexOf( self.id( this ), ids ) < 0 ) {
- ids.push( self.id( this ) );
- filtered.push( this );
- }
- } );
- data = filtered;
- this.selection.find( '.select2-search-choice' ).remove();
- $( data ).each( function () {
- self.addSelectedChoice( this );
- } );
- self.postprocessResults();
- },
- // multi
- tokenize: function () {
- var input = this.search.val();
- input = this.opts.tokenizer.call(
- this,
- input,
- this.data(),
- this.bind( this.onSelect ),
- this.opts
- );
- if ( input != null && input != undefined ) {
- this.search.val( input );
- if ( input.length > 0 ) {
- this.open();
- }
- }
- },
- // multi
- onSelect: function ( data, options ) {
- if ( ! this.triggerSelect( data ) || data.text === '' ) {
- return;
- }
- this.addSelectedChoice( data );
- this.opts.element.trigger( {
- type: 'selected',
- val: this.id( data ),
- choice: data,
- } );
- // keep track of the search's value before it gets cleared
- this.nextSearchTerm = this.opts.nextSearchTerm(
- data,
- this.search.val()
- );
- this.clearSearch();
- this.updateResults();
- if ( this.select || ! this.opts.closeOnSelect )
- this.postprocessResults(
- data,
- false,
- this.opts.closeOnSelect === true
- );
- if ( this.opts.closeOnSelect ) {
- this.close();
- this.search.width( 10 );
- } else {
- if ( this.countSelectableResults() > 0 ) {
- this.search.width( 10 );
- this.resizeSearch();
- if (
- this.getMaximumSelectionSize() > 0 &&
- this.val().length >= this.getMaximumSelectionSize()
- ) {
- // if we reached max selection size repaint the results so choices
- // are replaced with the max selection reached message
- this.updateResults( true );
- } else {
- // initializes search's value with nextSearchTerm and update search result
- if ( this.nextSearchTerm != undefined ) {
- this.search.val( this.nextSearchTerm );
- this.updateResults();
- this.search.select();
- }
- }
- this.positionDropdown();
- } else {
- // if nothing left to select close
- this.close();
- this.search.width( 10 );
- }
- }
- // since its not possible to select an element that has already been
- // added we do not need to check if this is a new element before firing change
- this.triggerChange( { added: data } );
- if ( ! options || ! options.noFocus ) this.focusSearch();
- },
- // multi
- cancel: function () {
- this.close();
- this.focusSearch();
- },
- addSelectedChoice: function ( data ) {
- var enableChoice = ! data.locked,
- enabledItem = $(
- "<li class='select2-search-choice'>" +
- ' <div></div>' +
- " <a href='#' class='select2-search-choice-close' tabindex='-1'></a>" +
- '</li>'
- ),
- disabledItem = $(
- "<li class='select2-search-choice select2-locked'>" +
- '<div></div>' +
- '</li>'
- );
- var choice = enableChoice ? enabledItem : disabledItem,
- id = this.id( data ),
- val = this.getVal(),
- formatted,
- cssClass;
- formatted = this.opts.formatSelection(
- data,
- choice.find( 'div' ),
- this.opts.escapeMarkup
- );
- if ( formatted != undefined ) {
- choice
- .find( 'div' )
- .replaceWith( $( '<div></div>' ).html( formatted ) );
- }
- cssClass = this.opts.formatSelectionCssClass(
- data,
- choice.find( 'div' )
- );
- if ( cssClass != undefined ) {
- choice.addClass( cssClass );
- }
- if ( enableChoice ) {
- choice
- .find( '.select2-search-choice-close' )
- .on( 'mousedown', killEvent )
- .on(
- 'click dblclick',
- this.bind( function ( e ) {
- if ( ! this.isInterfaceEnabled() ) return;
- this.unselect( $( e.target ) );
- this.selection
- .find( '.select2-search-choice-focus' )
- .removeClass( 'select2-search-choice-focus' );
- killEvent( e );
- this.close();
- this.focusSearch();
- } )
- )
- .on(
- 'focus',
- this.bind( function () {
- if ( ! this.isInterfaceEnabled() ) return;
- this.container.addClass(
- 'select2-container-active'
- );
- this.dropdown.addClass( 'select2-drop-active' );
- } )
- );
- }
- choice.data( 'select2-data', data );
- choice.insertBefore( this.searchContainer );
- val.push( id );
- this.setVal( val );
- },
- // multi
- unselect: function ( selected ) {
- var val = this.getVal(),
- data,
- index;
- selected = selected.closest( '.select2-search-choice' );
- if ( selected.length === 0 ) {
- throw (
- 'Invalid argument: ' +
- selected +
- '. Must be .select2-search-choice'
- );
- }
- data = selected.data( 'select2-data' );
- if ( ! data ) {
- // prevent a race condition when the 'x' is clicked really fast repeatedly the event can be queued
- // and invoked on an element already removed
- return;
- }
- var evt = $.Event( 'select2-removing' );
- evt.val = this.id( data );
- evt.choice = data;
- this.opts.element.trigger( evt );
- if ( evt.isDefaultPrevented() ) {
- return false;
- }
- while ( ( index = indexOf( this.id( data ), val ) ) >= 0 ) {
- val.splice( index, 1 );
- this.setVal( val );
- if ( this.select ) this.postprocessResults();
- }
- selected.remove();
- this.opts.element.trigger( {
- type: 'select2-removed',
- val: this.id( data ),
- choice: data,
- } );
- this.triggerChange( { removed: data } );
- return true;
- },
- // multi
- postprocessResults: function ( data, initial, noHighlightUpdate ) {
- var val = this.getVal(),
- choices = this.results.find( '.select2-result' ),
- compound = this.results.find( '.select2-result-with-children' ),
- self = this;
- choices.each2( function ( i, choice ) {
- var id = self.id( choice.data( 'select2-data' ) );
- if ( indexOf( id, val ) >= 0 ) {
- choice.addClass( 'select2-selected' );
- // mark all children of the selected parent as selected
- choice
- .find( '.select2-result-selectable' )
- .addClass( 'select2-selected' );
- }
- } );
- compound.each2( function ( i, choice ) {
- // hide an optgroup if it doesn't have any selectable children
- if (
- ! choice.is( '.select2-result-selectable' ) &&
- choice.find(
- '.select2-result-selectable:not(.select2-selected)'
- ).length === 0
- ) {
- choice.addClass( 'select2-selected' );
- }
- } );
- if (
- this.highlight() == -1 &&
- noHighlightUpdate !== false &&
- this.opts.closeOnSelect === true
- ) {
- self.highlight( 0 );
- }
- //If all results are chosen render formatNoMatches
- if (
- ! this.opts.createSearchChoice &&
- ! choices.filter( '.select2-result:not(.select2-selected)' )
- .length > 0
- ) {
- if (
- ! data ||
- ( data &&
- ! data.more &&
- this.results.find( '.select2-no-results' ).length ===
- 0 )
- ) {
- if (
- checkFormatter(
- self.opts.formatNoMatches,
- 'formatNoMatches'
- )
- ) {
- this.results.append(
- "<li class='select2-no-results'>" +
- evaluate(
- self.opts.formatNoMatches,
- self.opts.element,
- self.search.val()
- ) +
- '</li>'
- );
- }
- }
- }
- },
- // multi
- getMaxSearchWidth: function () {
- return this.selection.width() - getSideBorderPadding( this.search );
- },
- // multi
- resizeSearch: function () {
- var minimumWidth,
- left,
- maxWidth,
- containerLeft,
- searchWidth,
- sideBorderPadding = getSideBorderPadding( this.search );
- minimumWidth = measureTextWidth( this.search ) + 10;
- left = this.search.offset().left;
- maxWidth = this.selection.width();
- containerLeft = this.selection.offset().left;
- searchWidth =
- maxWidth - ( left - containerLeft ) - sideBorderPadding;
- if ( searchWidth < minimumWidth ) {
- searchWidth = maxWidth - sideBorderPadding;
- }
- if ( searchWidth < 40 ) {
- searchWidth = maxWidth - sideBorderPadding;
- }
- if ( searchWidth <= 0 ) {
- searchWidth = minimumWidth;
- }
- this.search.width( Math.floor( searchWidth ) );
- },
- // multi
- getVal: function () {
- var val;
- if ( this.select ) {
- val = this.select.val();
- return val === null ? [] : val;
- } else {
- val = this.opts.element.val();
- return splitVal(
- val,
- this.opts.separator,
- this.opts.transformVal
- );
- }
- },
- // multi
- setVal: function ( val ) {
- var unique;
- if ( this.select ) {
- this.select.val( val );
- } else {
- unique = [];
- // filter out duplicates
- $( val ).each( function () {
- if ( indexOf( this, unique ) < 0 ) unique.push( this );
- } );
- this.opts.element.val(
- unique.length === 0
- ? ''
- : unique.join( this.opts.separator )
- );
- }
- },
- // multi
- buildChangeDetails: function ( old, current ) {
- var current = current.slice( 0 ),
- old = old.slice( 0 );
- // remove intersection from each array
- for ( var i = 0; i < current.length; i++ ) {
- for ( var j = 0; j < old.length; j++ ) {
- if (
- equal(
- this.opts.id( current[ i ] ),
- this.opts.id( old[ j ] )
- )
- ) {
- current.splice( i, 1 );
- if ( i > 0 ) {
- i--;
- }
- old.splice( j, 1 );
- j--;
- }
- }
- }
- return { added: current, removed: old };
- },
- // multi
- val: function ( val, triggerChange ) {
- var oldData,
- self = this;
- if ( arguments.length === 0 ) {
- return this.getVal();
- }
- oldData = this.data();
- if ( ! oldData.length ) oldData = [];
- // val is an id. !val is true for [undefined,null,'',0] - 0 is legal
- if ( ! val && val !== 0 ) {
- this.opts.element.val( '' );
- this.updateSelection( [] );
- this.clearSearch();
- if ( triggerChange ) {
- this.triggerChange( {
- added: this.data(),
- removed: oldData,
- } );
- }
- return;
- }
- // val is a list of ids
- this.setVal( val );
- if ( this.select ) {
- this.opts.initSelection(
- this.select,
- this.bind( this.updateSelection )
- );
- if ( triggerChange ) {
- this.triggerChange(
- this.buildChangeDetails( oldData, this.data() )
- );
- }
- } else {
- if ( this.opts.initSelection === undefined ) {
- throw new Error(
- 'val() cannot be called if initSelection() is not defined'
- );
- }
- this.opts.initSelection( this.opts.element, function ( data ) {
- var ids = $.map( data, self.id );
- self.setVal( ids );
- self.updateSelection( data );
- self.clearSearch();
- if ( triggerChange ) {
- self.triggerChange(
- self.buildChangeDetails( oldData, self.data() )
- );
- }
- } );
- }
- this.clearSearch();
- },
- // multi
- onSortStart: function () {
- if ( this.select ) {
- throw new Error(
- "Sorting of elements is not supported when attached to <select>. Attach to <input type='hidden'/> instead."
- );
- }
- // collapse search field into 0 width so its container can be collapsed as well
- this.search.width( 0 );
- // hide the container
- this.searchContainer.hide();
- },
- // multi
- onSortEnd: function () {
- var val = [],
- self = this;
- // show search and move it to the end of the list
- this.searchContainer.show();
- // make sure the search container is the last item in the list
- this.searchContainer.appendTo( this.searchContainer.parent() );
- // since we collapsed the width in dragStarted, we resize it here
- this.resizeSearch();
- // update selection
- this.selection.find( '.select2-search-choice' ).each( function () {
- val.push( self.opts.id( $( this ).data( 'select2-data' ) ) );
- } );
- this.setVal( val );
- this.triggerChange();
- },
- // multi
- data: function ( values, triggerChange ) {
- var self = this,
- ids,
- old;
- if ( arguments.length === 0 ) {
- return this.selection
- .children( '.select2-search-choice' )
- .map( function () {
- return $( this ).data( 'select2-data' );
- } )
- .get();
- } else {
- old = this.data();
- if ( ! values ) {
- values = [];
- }
- ids = $.map( values, function ( e ) {
- return self.opts.id( e );
- } );
- this.setVal( ids );
- this.updateSelection( values );
- this.clearSearch();
- if ( triggerChange ) {
- this.triggerChange(
- this.buildChangeDetails( old, this.data() )
- );
- }
- }
- },
- } );
- $.fn.select2 = function () {
- var args = Array.prototype.slice.call( arguments, 0 ),
- opts,
- select2,
- method,
- value,
- multiple,
- allowedMethods = [
- 'val',
- 'destroy',
- 'opened',
- 'open',
- 'close',
- 'focus',
- 'isFocused',
- 'container',
- 'dropdown',
- 'onSortStart',
- 'onSortEnd',
- 'enable',
- 'disable',
- 'readonly',
- 'positionDropdown',
- 'data',
- 'search',
- ],
- valueMethods = [ 'opened', 'isFocused', 'container', 'dropdown' ],
- propertyMethods = [ 'val', 'data' ],
- methodsMap = { search: 'externalSearch' };
- this.each( function () {
- if ( args.length === 0 || typeof args[ 0 ] === 'object' ) {
- opts = args.length === 0 ? {} : $.extend( {}, args[ 0 ] );
- opts.element = $( this );
- if (
- opts.element.get( 0 ).tagName.toLowerCase() === 'select'
- ) {
- multiple = opts.element.prop( 'multiple' );
- } else {
- multiple = opts.multiple || false;
- if ( 'tags' in opts ) {
- opts.multiple = multiple = true;
- }
- }
- select2 = multiple
- ? new window.Select2[ 'class' ].multi()
- : new window.Select2[ 'class' ].single();
- select2.init( opts );
- } else if ( typeof args[ 0 ] === 'string' ) {
- if ( indexOf( args[ 0 ], allowedMethods ) < 0 ) {
- throw 'Unknown method: ' + args[ 0 ];
- }
- value = undefined;
- select2 = $( this ).data( 'select2' );
- if ( select2 === undefined ) return;
- method = args[ 0 ];
- if ( method === 'container' ) {
- value = select2.container;
- } else if ( method === 'dropdown' ) {
- value = select2.dropdown;
- } else {
- if ( methodsMap[ method ] ) method = methodsMap[ method ];
- value = select2[ method ].apply( select2, args.slice( 1 ) );
- }
- if (
- indexOf( args[ 0 ], valueMethods ) >= 0 ||
- ( indexOf( args[ 0 ], propertyMethods ) >= 0 &&
- args.length == 1 )
- ) {
- return false; // abort the iteration, ready to return first matched value
- }
- } else {
- throw 'Invalid arguments to select2 plugin: ' + args;
- }
- } );
- return value === undefined ? this : value;
- };
- // plugin defaults, accessible to users
- $.fn.select2.defaults = {
- width: 'copy',
- loadMorePadding: 0,
- closeOnSelect: true,
- openOnEnter: true,
- containerCss: {},
- dropdownCss: {},
- containerCssClass: '',
- dropdownCssClass: '',
- formatResult: function ( result, container, query, escapeMarkup ) {
- var markup = [];
- markMatch( this.text( result ), query.term, markup, escapeMarkup );
- return markup.join( '' );
- },
- transformVal: function ( val ) {
- return $.trim( val );
- },
- formatSelection: function ( data, container, escapeMarkup ) {
- return data ? escapeMarkup( this.text( data ) ) : undefined;
- },
- sortResults: function ( results, container, query ) {
- return results;
- },
- formatResultCssClass: function ( data ) {
- return data.css;
- },
- formatSelectionCssClass: function ( data, container ) {
- return undefined;
- },
- minimumResultsForSearch: 0,
- minimumInputLength: 0,
- maximumInputLength: null,
- maximumSelectionSize: 0,
- id: function ( e ) {
- return e == undefined ? null : e.id;
- },
- text: function ( e ) {
- if ( e && this.data && this.data.text ) {
- if ( $.isFunction( this.data.text ) ) {
- return this.data.text( e );
- } else {
- return e[ this.data.text ];
- }
- } else {
- return e.text;
- }
- },
- matcher: function ( term, text ) {
- return (
- stripDiacritics( '' + text )
- .toUpperCase()
- .indexOf( stripDiacritics( '' + term ).toUpperCase() ) >= 0
- );
- },
- separator: ',',
- tokenSeparators: [],
- tokenizer: defaultTokenizer,
- escapeMarkup: defaultEscapeMarkup,
- blurOnChange: false,
- selectOnBlur: false,
- adaptContainerCssClass: function ( c ) {
- return c;
- },
- adaptDropdownCssClass: function ( c ) {
- return null;
- },
- nextSearchTerm: function ( selectedObject, currentSearchTerm ) {
- return undefined;
- },
- searchInputPlaceholder: '',
- createSearchChoicePosition: 'top',
- shouldFocusInput: function ( instance ) {
- // Attempt to detect touch devices
- var supportsTouchEvents =
- 'ontouchstart' in window || navigator.msMaxTouchPoints > 0;
- // Only devices which support touch events should be special cased
- if ( ! supportsTouchEvents ) {
- return true;
- }
- // Never focus the input if search is disabled
- if ( instance.opts.minimumResultsForSearch < 0 ) {
- return false;
- }
- return true;
- },
- };
- $.fn.select2.locales = [];
- $.fn.select2.locales[ 'en' ] = {
- formatMatches: function ( matches ) {
- if ( matches === 1 ) {
- return 'One result is available, press enter to select it.';
- }
- return (
- matches +
- ' results are available, use up and down arrow keys to navigate.'
- );
- },
- formatNoMatches: function () {
- return 'No matches found';
- },
- formatAjaxError: function ( jqXHR, textStatus, errorThrown ) {
- return 'Loading failed';
- },
- formatInputTooShort: function ( input, min ) {
- var n = min - input.length;
- return (
- 'Please enter ' +
- n +
- ' or more character' +
- ( n == 1 ? '' : 's' )
- );
- },
- formatInputTooLong: function ( input, max ) {
- var n = input.length - max;
- return 'Please delete ' + n + ' character' + ( n == 1 ? '' : 's' );
- },
- formatSelectionTooBig: function ( limit ) {
- return (
- 'You can only select ' +
- limit +
- ' item' +
- ( limit == 1 ? '' : 's' )
- );
- },
- formatLoadMore: function ( pageNumber ) {
- return 'Loading more results…';
- },
- formatSearching: function () {
- return 'Searching…';
- },
- };
- $.extend( $.fn.select2.defaults, $.fn.select2.locales[ 'en' ] );
- $.fn.select2.ajaxDefaults = {
- transport: $.ajax,
- params: {
- type: 'GET',
- cache: false,
- dataType: 'json',
- },
- };
- // exports
- window.Select2 = {
- query: {
- ajax: ajax,
- local: local,
- tags: tags,
- },
- util: {
- debounce: debounce,
- markMatch: markMatch,
- escapeMarkup: defaultEscapeMarkup,
- stripDiacritics: stripDiacritics,
- },
- class: {
- abstract: AbstractSelect2,
- single: SingleSelect2,
- multi: MultiSelect2,
- },
- };
- } )( jQuery );
|