| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795 |
- /*
- Copyright 2012 Igor Vaynberg
- Version: 3.5.4-tribe
- 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.lastSearchTerm = 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.removeData( "select2" )
- .off( ".select2" );
- if ( !element.is( "input[type='hidden']" ) ) {
- element
- .show()
- .prop( "autofocus", this.autofocus || false );
- if ( this.elementTabIndex ) {
- element.attr( { tabindex: this.elementTabIndex } );
- } else {
- element.removeAttr( "tabindex" );
- }
- element.show();
- } else {
- element.css( "display", "" );
- }
- }
- 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.debug = opts.debug || $.fn.select2.defaults.debug;
- // Warnings for options renamed/removed in Select2 4.0.0
- // Only when it's enabled through debug mode
- if ( opts.debug && console && console.warn ) {
- // id was removed
- if ( opts.id != null ) {
- console.warn(
- 'Select2: The `id` option has been removed in Select2 4.0.0, ' +
- 'consider renaming your `id` property or mapping the property before your data makes it to Select2. ' +
- 'You can read more at https://select2.github.io/announcements-4.0.html#changed-id'
- );
- }
- // text was removed
- if ( opts.text != null ) {
- console.warn(
- 'Select2: The `text` option has been removed in Select2 4.0.0, ' +
- 'consider renaming your `text` property or mapping the property before your data makes it to Select2. ' +
- 'You can read more at https://select2.github.io/announcements-4.0.html#changed-id'
- );
- }
- // sortResults was renamed to results
- if ( opts.sortResults != null ) {
- console.warn(
- 'Select2: the `sortResults` option has been renamed to `sorter` in Select2 4.0.0. '
- );
- }
- // selectOnBlur was renamed to selectOnClose
- if ( opts.selectOnBlur != null ) {
- console.warn(
- 'Select2: The `selectOnBlur` option has been renamed to `selectOnClose` in Select2 4.0.0.'
- );
- }
- // ajax.results was renamed to ajax.processResults
- if ( opts.ajax != null && opts.ajax.results != null ) {
- console.warn(
- 'Select2: The `ajax.results` option has been renamed to `ajax.processResults` in Select2 4.0.0.'
- );
- }
- // format* options were renamed to language.*
- if ( opts.formatNoResults != null ) {
- console.warn(
- 'Select2: The `formatNoResults` option has been renamed to `language.noResults` in Select2 4.0.0.'
- );
- }
- if ( opts.formatSearching != null ) {
- console.warn(
- 'Select2: The `formatSearching` option has been renamed to `language.searching` in Select2 4.0.0.'
- );
- }
- if ( opts.formatInputTooShort != null ) {
- console.warn(
- 'Select2: The `formatInputTooShort` option has been renamed to `language.inputTooShort` in Select2 4.0.0.'
- );
- }
- if ( opts.formatInputTooLong != null ) {
- console.warn(
- 'Select2: The `formatInputTooLong` option has been renamed to `language.inputTooLong` in Select2 4.0.0.'
- );
- }
- if ( opts.formatLoading != null ) {
- console.warn(
- 'Select2: The `formatLoading` option has been renamed to `language.loadingMore` in Select2 4.0.0.'
- );
- }
- if ( opts.formatSelectionTooBig != null ) {
- console.warn(
- 'Select2: The `formatSelectionTooBig` option has been renamed to `language.maximumSelected` in Select2 4.0.0.'
- );
- }
- if ( opts.element.data( 'select2Tags' ) ) {
- console.warn(
- 'Select2: The `data-select2-tags` attribute has been renamed to `data-tags` in Select2 4.0.0.'
- );
- }
- }
- // Aliasing options renamed in Select2 4.0.0
- // data-select2-tags -> data-tags
- if ( opts.element.data( 'tags' ) != null ) {
- var elemTags = opts.element.data( 'tags' );
- // data-tags should actually be a boolean
- if ( !$.isArray( elemTags ) ) {
- elemTags = [];
- }
- opts.element.data( 'select2Tags', elemTags );
- }
- // sortResults -> sorter
- if ( opts.sorter != null ) {
- opts.sortResults = opts.sorter;
- }
- // selectOnBlur -> selectOnClose
- if ( opts.selectOnClose != null ) {
- opts.selectOnBlur = opts.selectOnClose;
- }
- // ajax.results -> ajax.processResults
- if ( opts.ajax != null ) {
- if ( $.isFunction( opts.ajax.processResults ) ) {
- opts.ajax.results = opts.ajax.processResults;
- }
- }
- // Formatters/language options
- if ( opts.language != null ) {
- var lang = opts.language;
- // formatNoMatches -> language.noMatches
- if ( $.isFunction( lang.noMatches ) ) {
- opts.formatNoMatches = lang.noMatches;
- }
- // formatSearching -> language.searching
- if ( $.isFunction( lang.searching ) ) {
- opts.formatSearching = lang.searching;
- }
- // formatInputTooShort -> language.inputTooShort
- if ( $.isFunction( lang.inputTooShort ) ) {
- opts.formatInputTooShort = lang.inputTooShort;
- }
- // formatInputTooLong -> language.inputTooLong
- if ( $.isFunction( lang.inputTooLong ) ) {
- opts.formatInputTooLong = lang.inputTooLong;
- }
- // formatLoading -> language.loadingMore
- if ( $.isFunction( lang.loadingMore ) ) {
- opts.formatLoading = lang.loadingMore;
- }
- // formatSelectionTooBig -> language.maximumSelected
- if ( $.isFunction( lang.maximumSelected ) ) {
- opts.formatSelectionTooBig = lang.maximumSelected;
- }
- }
- 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 ) {
- this.container.addClass( "select2-drop-above" );
- $dropdown.addClass( "select2-drop-above" );
- dropHeight = $dropdown.outerHeight( false );
- css.top = offset.top - dropHeight;
- css.bottom = 'auto';
- }
- 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" );
- // Remove the aria active descendant for highlighted element
- this.search.removeAttr( "aria-activedescendant" );
- 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 () {
- },
- /**
- * @return {Boolean} Whether or not search value was changed.
- * @private
- */
- prefillNextSearchTerm: function () {
- // 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() !== "" ) {
- return false;
- }
- var nextSearchTerm = this.opts.nextSearchTerm( this.data(), this.lastSearchTerm );
- if ( nextSearchTerm !== undefined ) {
- this.search.val( nextSearchTerm );
- this.search.select();
- return true;
- }
- return false;
- },
- //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>" );
- if ( this.showSearch ) {
- this.showSearch( search.val() );
- }
- 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 ( typeof(style) === "string" ) {
- 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 );
- }
- }
- this.prefillNextSearchTerm();
- 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.on( 'focus.select2', 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.results && this.results.length > 1 ) {
- 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.lastSearchTerm = 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 {
- // The following vendorlib modification corrects for some IE errors specific to version 3.5.x.
- // This issue is fixed in newer versions of the library, including selectWoo.
- // Credit: https://stackoverflow.com/a/29796883/1837376
- if ( document.documentMode === 10 || document.documentMode === 11 ) {
- // This is IE 10 or 11.
- if ( initial.target.value == data.results[ 0 ].id || data.results.length == 1 ) {
- this.highlight( 0 );
- }
- } 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.lastSearchTerm = 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.opts.debug && console && console.warn ) {
- console.warn(
- 'Select2: The second option to `select2("val")` is not supported in Select2 4.0.0. ' +
- 'The `change` event will always be triggered in 4.0.0.'
- );
- }
- }
- if ( this.select ) {
- if ( this.opts.debug && console && console.warn ) {
- console.warn(
- 'Select2: Setting the value on a <select> using `select2("val")` is no longer supported in 4.0.0. ' +
- 'You can use the `.val(newValue).trigger("change")` method provided by jQuery instead.'
- );
- }
- 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 ( this.opts.debug && console && console.warn ) {
- console.warn(
- 'Select2: The `select2("data")` method can no longer set selected values in 4.0.0, ' +
- 'consider using the `.val()` method instead.'
- );
- }
- 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.on( 'focus.select2', 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();
- this.prefillNextSearchTerm();
- 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 ( !(self.id( this ) in ids) ) {
- ids[ self.id( this ) ] = 0;
- filtered.push( this );
- }
- } );
- this.selection.find( ".select2-search-choice" ).remove();
- this.addSelectedChoice( filtered );
- 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.lastSearchTerm = 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.prefillNextSearchTerm() ) {
- this.updateResults();
- }
- }
- 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 val = this.getVal(),
- self = this;
- $( data ).each( function () {
- val.push( self.createChoice( this ) );
- } );
- this.setVal( val );
- },
- createChoice: 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 ),
- 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 );
- return id;
- },
- // 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 ) {
- if ( this.select ) {
- this.select.val( val );
- } else {
- var unique = [],
- valMap = {};
- // filter out duplicates
- $( val ).each( function () {
- if ( !(this in valMap) ) {
- unique.push( this );
- valMap[ this ] = 0;
- }
- } );
- 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 );
- i--;
- old.splice( j, 1 );
- break;
- }
- }
- }
- 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 = {
- debug: false,
- 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 ));
|