| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766 | (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){/* Copyright (c) 2012 Joshfire - MIT license *//** * @fileoverview Core of the JSON Form client-side library. * * Generates an HTML form from a structured data model and a layout description. * * The library may also validate inputs entered by the user against the data model * upon form submission and create the structured data object initialized with the * values that were submitted. * * The library depends on: *  - jQuery *  - the underscore library *  - a JSON parser/serializer. Nothing to worry about in modern browsers. *  - the JSONFormValidation library (in jsv.js) for validation purpose * * See documentation at: * http://developer.joshfire.com/doc/dev/ref/jsonform * * The library creates and maintains an internal data tree along with the DOM. * That structure is necessary to handle arrays (and nested arrays!) that are * dynamic by essence. */ /*global window*/(function(serverside, global, $, _, JSON) {  if (serverside && !_) {    _ = require('underscore');  }  /**   * Regular expressions used to extract array indexes in input field names   */  var reArray = /\[([0-9]*)\](?=\[|\.|$)/g;  /**   * Template settings for form views   */  var fieldTemplateSettings = {    evaluate    : /<%([\s\S]+?)%>/g,    interpolate : /<%=([\s\S]+?)%>/g  };  /**   * Template settings for value replacement   */  var valueTemplateSettings = {    evaluate    : /\{\[([\s\S]+?)\]\}/g,    interpolate : /\{\{([\s\S]+?)\}\}/g  };  /**   * Returns true if given value is neither "undefined" nor null   */  var isSet = function (value) {    return !(_.isUndefined(value) || _.isNull(value));  };  /**   * Returns true if given property is directly property of an object   */  var hasOwnProperty = function (obj, prop) {    return typeof obj === 'object' && obj.hasOwnProperty(prop);  }  /**   * The jsonform object whose methods will be exposed to the window object   */  var jsonform = {util:{}};  // From backbonejs  var escapeHTML = function (string) {    if (!isSet(string)) {      return '';    }    string = '' + string;    if (!string) {      return '';    }    return string      .replace(/&(?!\w+;|#\d+;|#x[\da-f]+;)/gi, '&')      .replace(/</g, '<')      .replace(/>/g, '>')      .replace(/"/g, '"')      .replace(/'/g, ''')      .replace(/\//g, '/');  };/** * Escapes selector name for use with jQuery * * All meta-characters listed in jQuery doc are escaped: * http://api.jquery.com/category/selectors/ * * @function * @param {String} selector The jQuery selector to escape * @return {String} The escaped selector. */var escapeSelector = function (selector) {  return selector.replace(/([ \!\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;<\=\>\?\@\[\\\]\^\`\{\|\}\~])/g, '\\$1');};/** * * Slugifies a string by replacing spaces with _. Used to create * valid classnames and ids for the form. * * @function * @param {String} str The string to slugify * @return {String} The slugified string. */var slugify = function(str) {  return str.replace(/\ /g, '_');}/** * Initializes tabular sections in forms. Such sections are generated by the * 'selectfieldset' type of elements in JSON Form. * * Input fields that are not visible are automatically disabled * not to appear in the submitted form. That's on purpose, as tabs * are meant to convey an alternative (and not a sequence of steps). * * The tabs menu is not rendered as tabs but rather as a select field because * it's easier to grasp that it's an alternative. * * Code based on bootstrap-tabs.js, updated to: * - react to option selection instead of tab click * - disable input fields in non visible tabs * - disable the possibility to have dropdown menus (no meaning here) * - act as a regular function instead of as a jQuery plug-in. * * @function * @param {Object} tabs jQuery object that contains the tabular sections *  to initialize. The object may reference more than one element. */var initializeTabs = function (tabs) {  var activate = function (element, container) {    container      .find('> .active')      .removeClass('active');    element.addClass('active');  };  var enableFields = function ($target, targetIndex) {    // Enable all fields in the targeted tab    $target.find('input, textarea, select').removeAttr('disabled');    // Disable all fields in other tabs    $target.parent()      .children(':not([data-idx=' + targetIndex + '])')      .find('input, textarea, select')      .attr('disabled', 'disabled');  };  var optionSelected = function (e) {    var $option = $("option:selected", $(this)),      $select = $(this),      // do not use .attr() as it sometimes unexplicably fails      targetIdx = $option.get(0).getAttribute('data-idx') || $option.attr('value'),      $target;    e.preventDefault();    if ($option.hasClass('active')) {      return;    }    $target = $(this).parents('.tabbable').eq(0).find('> .tab-content > [data-idx=' + targetIdx + ']');    activate($option, $select);    activate($target, $target.parent());    enableFields($target, targetIdx);  };  var tabClicked = function (e) {    var $a = $('a', $(this));    var $content = $(this).parents('.tabbable').first()      .find('.tab-content').first();    var targetIdx = $(this).index();    // The `>` here is to prevent activating selectfieldsets inside a tabarray    var $target = $content.find('> [data-idx=' + targetIdx + ']');    e.preventDefault();    activate($(this), $(this).parent());    activate($target, $target.parent());    if ($(this).parent().hasClass('jsonform-alternative')) {      enableFields($target, targetIdx);    }  };  tabs.each(function () {    $(this).delegate('select.nav', 'change', optionSelected);    $(this).find('select.nav').each(function () {      $(this).val($(this).find('.active').attr('value'));      // do not use .attr() as it sometimes unexplicably fails      var targetIdx = $(this).find('option:selected').get(0).getAttribute('data-idx') ||        $(this).find('option:selected').attr('value');      var $target = $(this).parents('.tabbable').eq(0).find('> .tab-content > [data-idx=' + targetIdx + ']');      enableFields($target, targetIdx);    });    $(this).delegate('ul.nav li', 'click', tabClicked);    $(this).find('ul.nav li.active').click();  });};// Twitter bootstrap-friendly HTML boilerplate for standard inputsjsonform.fieldTemplate = function(inner) {  return '<div ' +    '<% for(var key in elt.htmlMetaData) {%>' +      '<%= key %>="<%= elt.htmlMetaData[key] %>" ' +    '<% }%>' +    'class="form-group jsonform-error-<%= keydash %>' +    '<%= elt.htmlClass ? " " + elt.htmlClass : "" %>' +    '<%= (node.schemaElement && node.schemaElement.required && (node.schemaElement.type !== "boolean") ? " jsonform-required" : "") %>' +    '<%= (node.readOnly ? " jsonform-readonly" : "") %>' +    '<%= (node.disabled ? " jsonform-disabled" : "") %>' +    '">' +    '<% if (!elt.notitle) { %>' +      '<label for="<%= node.id %>"><%= node.title ? node.title : node.name %></label>' +    '<% } %>' +    '<div class="controls">' +      '<% if (node.prepend || node.append) { %>' +      '<div class="<% if (node.prepend) { %>input-group<% } %>' +        '<% if (node.append) { %> input-group<% } %>">' +        '<% if (node.prepend) { %>' +          '<span class="input-group-addon"><%= node.prepend %></span>' +        '<% } %>' +      '<% } %>' +      inner +      '<% if (node.append) { %>' +        '<span class="input-group-addon"><%= node.append %></span>' +      '<% } %>' +      '<% if (node.prepend || node.append) { %>' +        '</div>' +      '<% } %>' +      '<% if (node.description) { %>' +        '<span class="help-block"><%= node.description %></span>' +      '<% } %>' +      '<span class="help-block jsonform-errortext" style="display:none;"></span>' +    '</div></div>';};var fileDisplayTemplate = '<div class="_jsonform-preview">' +  '<% if (value.type=="image") { %>' +  '<img class="jsonform-preview" id="jsonformpreview-<%= id %>" src="<%= value.url %>" />' +  '<% } else { %>' +  '<a href="<%= value.url %>"><%= value.name %></a> (<%= Math.ceil(value.size/1024) %>kB)' +  '<% } %>' +  '</div>' +  '<a href="#" class="btn btn-default _jsonform-delete"><i class="glyphicon glyphicon-remove" title="Remove"></i></a> ';var inputFieldTemplate = function (type) {  return {    'template': '<input type="' + type + '" ' +      'class=\'form-control<%= (fieldHtmlClass ? " " + fieldHtmlClass : "") %>\'' +      'name="<%= node.name %>" value="<%= escape(value) %>" id="<%= id %>"' +      '<%= (node.disabled? " disabled" : "")%>' +      '<%= (node.readOnly ? " readonly=\'readonly\'" : "") %>' +      '<%= (node.schemaElement && (node.schemaElement.step > 0 || node.schemaElement.step == "any") ? " step=\'" + node.schemaElement.step + "\'" : "") %>' +      '<%= (node.schemaElement && node.schemaElement.maxLength ? " maxlength=\'" + node.schemaElement.maxLength + "\'" : "") %>' +      '<%= (node.schemaElement && node.schemaElement.required && (node.schemaElement.type !== "boolean") ? " required=\'required\'" : "") %>' +      '<%= (node.placeholder? " placeholder=" + \'"\' + escape(node.placeholder) + \'"\' : "")%>' +      ' />',    'fieldtemplate': true,    'inputfield': true  }};jsonform.elementTypes = {  'none': {    'template': ''  },  'root': {    'template': '<div><%= children %></div>'  },  'text': inputFieldTemplate('text'),  'password': inputFieldTemplate('password'),  'date': inputFieldTemplate('date'),  'datetime': inputFieldTemplate('datetime'),  'datetime-local': inputFieldTemplate('datetime-local'),  'email': inputFieldTemplate('email'),  'month': inputFieldTemplate('month'),  'number': inputFieldTemplate('number'),  'search': inputFieldTemplate('search'),  'tel': inputFieldTemplate('tel'),  'time': inputFieldTemplate('time'),  'url': inputFieldTemplate('url'),  'week': inputFieldTemplate('week'),  'range': {    'template': '<input type="range" ' +      '<%= (fieldHtmlClass ? "class=\'" + fieldHtmlClass + "\' " : "") %>' +      'name="<%= node.name %>" value="<%= escape(value) %>" id="<%= id %>"' +      '<%= (node.disabled? " disabled" : "")%>' +      ' min=<%= range.min %>' +      ' max=<%= range.max %>' +      ' step=<%= range.step %>' +      '<%= (node.schemaElement && node.schemaElement.required ? " required=\'required\'" : "") %>' +      ' />',    'fieldtemplate': true,    'inputfield': true,    'onBeforeRender': function (data, node) {      data.range = {        min: 1,        max: 100,        step: 1      };      if (!node || !node.schemaElement) return;      if (node.formElement && node.formElement.step) {        data.range.step = node.formElement.step;      }      if (typeof node.schemaElement.minimum !== 'undefined') {        if (node.schemaElement.exclusiveMinimum) {          data.range.min = node.schemaElement.minimum + data.range.step;        }        else {          data.range.min = node.schemaElement.minimum;        }      }      if (typeof node.schemaElement.maximum !== 'undefined') {        if (node.schemaElement.exclusiveMaximum) {          data.range.max = node.schemaElement.maximum - data.range.step;        }        else {          data.range.max = node.schemaElement.maximum;        }      }    }  },  'color':{    'template':'<input type="text" ' +      '<%= (fieldHtmlClass ? "class=\'" + fieldHtmlClass + "\' " : "") %>' +      'name="<%= node.name %>" value="<%= escape(value) %>" id="<%= id %>"' +      '<%= (node.disabled? " disabled" : "")%>' +      '<%= (node.schemaElement && node.schemaElement.required ? " required=\'required\'" : "") %>' +      ' />',    'fieldtemplate': true,    'inputfield': true,    'onInsert': function(evt, node) {      $(node.el).find('#' + escapeSelector(node.id)).spectrum({        preferredFormat: "hex",        showInput: true      });    }  },  'textarea':{    'template':'<textarea id="<%= id %>" name="<%= node.name %>" ' +      '<%= (fieldHtmlClass ? "class=\'" + fieldHtmlClass + "\' " : "") %>' +      'style="height:<%= elt.height || "150px" %>;width:<%= elt.width || "100%" %>;"' +      '<%= (node.disabled? " disabled" : "")%>' +      '<%= (node.readOnly ? " readonly=\'readonly\'" : "") %>' +      '<%= (node.schemaElement && node.schemaElement.maxLength ? " maxlength=\'" + node.schemaElement.maxLength + "\'" : "") %>' +      '<%= (node.schemaElement && node.schemaElement.required ? " required=\'required\'" : "") %>' +      '<%= (node.placeholder? " placeholder=" + \'"\' + escape(node.placeholder) + \'"\' : "")%>' +      '><%= value %></textarea>',    'fieldtemplate': true,    'inputfield': true  },  'wysihtml5':{    'template':'<textarea id="<%= id %>" name="<%= node.name %>" style="height:<%= elt.height || "300px" %>;width:<%= elt.width || "100%" %>;"' +      '<%= (fieldHtmlClass ? "class=\'" + fieldHtmlClass + "\' " : "") %>' +      '<%= (node.disabled? " disabled" : "")%>' +      '<%= (node.readOnly ? " readonly=\'readonly\'" : "") %>' +      '<%= (node.schemaElement && node.schemaElement.maxLength ? " maxlength=\'" + node.schemaElement.maxLength + "\'" : "") %>' +      '<%= (node.schemaElement && node.schemaElement.required ? " required=\'required\'" : "") %>' +      '<%= (node.placeholder? " placeholder=" + \'"\' + escape(node.placeholder) + \'"\' : "")%>' +      '><%= value %></textarea>',    'fieldtemplate': true,    'inputfield': true,    'onInsert': function (evt, node) {      var setup = function () {        //protect from double init        if ($(node.el).data("wysihtml5")) return;        $(node.el).data("wysihtml5_loaded",true);        $(node.el).find('#' + escapeSelector(node.id)).wysihtml5({          "html": true,          "link": true,          "font-styles":true,          "image": false,          "events": {            "load": function () {              // In chrome, if an element is required and hidden, it leads to              // the error 'An invalid form control with name='' is not focusable'              // See http://stackoverflow.com/questions/7168645/invalid-form-control-only-in-google-chrome              $(this.textareaElement).removeAttr('required');            }          }        });      };      // Is there a setup hook?      if (window.jsonform_wysihtml5_setup) {        window.jsonform_wysihtml5_setup(setup);        return;      }      // Wait until wysihtml5 is loaded      var itv = window.setInterval(function() {        if (window.wysihtml5) {          window.clearInterval(itv);          setup();        }      },1000);    }  },  'ace':{    'template':'<div id="<%= id %>" style="position:relative;height:<%= elt.height || "300px" %>;"><div id="<%= id %>__ace" style="width:<%= elt.width || "100%" %>;height:<%= elt.height || "300px" %>;"></div><input type="hidden" name="<%= node.name %>" id="<%= id %>__hidden" value="<%= escape(value) %>"/></div>',    'fieldtemplate': true,    'inputfield': true,    'onInsert': function (evt, node) {      var setup = function () {        var formElement = node.formElement || {};        var ace = window.ace;        var editor = ace.edit($(node.el).find('#' + escapeSelector(node.id) + '__ace').get(0));        var idSelector = '#' + escapeSelector(node.id) + '__hidden';        // Force editor to use "\n" for new lines, not to bump into ACE "\r" conversion issue        // (ACE is ok with "\r" on pasting but fails to return "\r" when value is extracted)        editor.getSession().setNewLineMode('unix');        editor.renderer.setShowPrintMargin(false);        editor.setTheme("ace/theme/"+(formElement.aceTheme||"twilight"));        if (formElement.aceMode) {          editor.getSession().setMode("ace/mode/"+formElement.aceMode);        }        editor.getSession().setTabSize(2);        // Set the contents of the initial manifest file        editor.getSession().setValue(node.value||"");        //TODO this is clearly sub-optimal        // 'Lazily' bind to the onchange 'ace' event to give        // priority to user edits        var lazyChanged = _.debounce(function () {          $(node.el).find(idSelector).val(editor.getSession().getValue());          $(node.el).find(idSelector).change();        }, 600);        editor.getSession().on('change', lazyChanged);        editor.on('blur', function() {          $(node.el).find(idSelector).change();          $(node.el).find(idSelector).trigger("blur");        });        editor.on('focus', function() {          $(node.el).find(idSelector).trigger("focus");        });      };      // Is there a setup hook?      if (window.jsonform_ace_setup) {        window.jsonform_ace_setup(setup);        return;      }      // Wait until ACE is loaded      var itv = window.setInterval(function() {        if (window.ace) {          window.clearInterval(itv);          setup();        }      },1000);    }  },  'checkbox':{    'template': '<div class="checkbox"><label><input type="checkbox" id="<%= id %>" ' +      '<%= (fieldHtmlClass ? " class=\'" + fieldHtmlClass + "\'": "") %>' +      'name="<%= node.name %>" value="1" <% if (value) {%>checked<% } %>' +      '<%= (node.disabled? " disabled" : "")%>' +      '<%= (node.schemaElement && node.schemaElement.required && (node.schemaElement.type !== "boolean") ? " required=\'required\'" : "") %>' +      ' /><%= node.inlinetitle || "" %>' +      '</label></div>',    'fieldtemplate': true,    'inputfield': true,    'getElement': function (el) {      return $(el).parent().get(0);    }  },  'file':{    'template':'<input class="input-file" id="<%= id %>" name="<%= node.name %>" type="file" ' +      '<%= (node.schemaElement && node.schemaElement.required ? " required=\'required\'" : "") %>' +      '/>',    'fieldtemplate': true,    'inputfield': true  },  'file-hosted-public':{    'template':'<span><% if (value && (value.type||value.url)) { %>'+fileDisplayTemplate+'<% } %><input class="input-file" id="_transloadit_<%= id %>" type="file" name="<%= transloaditname %>" /><input data-transloadit-name="_transloadit_<%= transloaditname %>" type="hidden" id="<%= id %>" name="<%= node.name %>" value=\'<%= escape(JSON.stringify(node.value)) %>\' /></span>',    'fieldtemplate': true,    'inputfield': true,    'getElement': function (el) {      return $(el).parent().get(0);    },    'onBeforeRender': function (data, node) {      if (!node.ownerTree._transloadit_generic_public_index) {        node.ownerTree._transloadit_generic_public_index=1;      } else {        node.ownerTree._transloadit_generic_public_index++;      }      data.transloaditname = "_transloadit_jsonform_genericupload_public_"+node.ownerTree._transloadit_generic_public_index;      if (!node.ownerTree._transloadit_generic_elts) node.ownerTree._transloadit_generic_elts = {};      node.ownerTree._transloadit_generic_elts[data.transloaditname] = node;    },    'onChange': function(evt,elt) {      // The "transloadit" function should be called only once to enable      // the service when the form is submitted. Has it already been done?      if (elt.ownerTree._transloadit_bound) {        return false;      }      elt.ownerTree._transloadit_bound = true;      // Call the "transloadit" function on the form element      var formElt = $(elt.ownerTree.domRoot);      formElt.transloadit({        autoSubmit: false,        wait: true,        onSuccess: function (assembly) {          // Image has been uploaded. Check the "results" property that          // contains the list of files that Transloadit produced. There          // should be one image per file input in the form at most.          // console.log(assembly.results);          var results = _.values(assembly.results);          results = _.flatten(results);          _.each(results, function (result) {            // Save the assembly result in the right hidden input field            var id = elt.ownerTree._transloadit_generic_elts[result.field].id;            var input = formElt.find('#' + escapeSelector(id));            var nonEmptyKeys = _.filter(_.keys(result.meta), function (key) {              return !!isSet(result.meta[key]);            });            result.meta = _.pick(result.meta, nonEmptyKeys);            input.val(JSON.stringify(result));          });          // Unbind transloadit from the form          elt.ownerTree._transloadit_bound = false;          formElt.unbind('submit.transloadit');          // Submit the form on next tick          _.delay(function () {            console.log('submit form');            elt.ownerTree.submit();          }, 10);        },        onError: function (assembly) {          // TODO: report the error to the user          console.log('assembly error', assembly);        }      });    },    'onInsert': function (evt, node) {      $(node.el).find('a._jsonform-delete').on('click', function (evt) {        $(node.el).find('._jsonform-preview').remove();        $(node.el).find('a._jsonform-delete').remove();        $(node.el).find('#' + escapeSelector(node.id)).val('');        evt.preventDefault();        return false;      });    },    'onSubmit':function(evt, elt) {      if (elt.ownerTree._transloadit_bound) {        return false;      }      return true;    }  },  'file-transloadit': {    'template': '<span><% if (value && (value.type||value.url)) { %>'+fileDisplayTemplate+'<% } %><input class="input-file" id="_transloadit_<%= id %>" type="file" name="_transloadit_<%= node.name %>" /><input type="hidden" id="<%= id %>" name="<%= node.name %>" value=\'<%= escape(JSON.stringify(node.value)) %>\' /></span>',    'fieldtemplate': true,    'inputfield': true,    'getElement': function (el) {      return $(el).parent().get(0);    },    'onChange': function (evt, elt) {      // The "transloadit" function should be called only once to enable      // the service when the form is submitted. Has it already been done?      if (elt.ownerTree._transloadit_bound) {        return false;      }      elt.ownerTree._transloadit_bound = true;      // Call the "transloadit" function on the form element      var formElt = $(elt.ownerTree.domRoot);      formElt.transloadit({        autoSubmit: false,        wait: true,        onSuccess: function (assembly) {          // Image has been uploaded. Check the "results" property that          // contains the list of files that Transloadit produced. Note          // JSONForm only supports 1-to-1 associations, meaning it          // expects the "results" property to contain only one image          // per file input in the form.          // console.log(assembly.results);          var results = _.values(assembly.results);          results = _.flatten(results);          _.each(results, function (result) {            // Save the assembly result in the right hidden input field            var input = formElt.find('input[name="' +              result.field.replace(/^_transloadit_/, '') +              '"]');            var nonEmptyKeys = _.filter(_.keys(result.meta), function (key) {              return !!isSet(result.meta[key]);            });            result.meta = _.pick(result.meta, nonEmptyKeys);            input.val(JSON.stringify(result));          });          // Unbind transloadit from the form          elt.ownerTree._transloadit_bound = false;          formElt.unbind('submit.transloadit');          // Submit the form on next tick          _.delay(function () {            console.log('submit form');            elt.ownerTree.submit();          }, 10);        },        onError: function (assembly) {          // TODO: report the error to the user          console.log('assembly error', assembly);        }      });    },    'onInsert': function (evt, node) {      $(node.el).find('a._jsonform-delete').on('click', function (evt) {        $(node.el).find('._jsonform-preview').remove();        $(node.el).find('a._jsonform-delete').remove();        $(node.el).find('#' + escapeSelector(node.id)).val('');        evt.preventDefault();        return false;      });    },    'onSubmit': function (evt, elt) {      if (elt.ownerTree._transloadit_bound) {        return false;      }      return true;    }  },  'select':{    'template':'<select name="<%= node.name %>" id="<%= id %>"' +      'class=\'form-control<%= (fieldHtmlClass ? " " + fieldHtmlClass : "") %>\'' +      '<%= (node.schemaElement && node.schemaElement.disabled? " disabled" : "")%>' +      '<%= (node.schemaElement && node.schemaElement.required ? " required=\'required\'" : "") %>' +      '> ' +      '<% _.each(node.options, function(key, val) { if(key instanceof Object) { if (value === key.value) { %> <option selected value="<%= key.value %>"><%= key.title %></option> <% } else { %> <option value="<%= key.value %>"><%= key.title %></option> <% }} else { if (value === key) { %> <option selected value="<%= key %>"><%= key %></option> <% } else { %><option value="<%= key %>"><%= key %></option> <% }}}); %> ' +      '</select>',    'fieldtemplate': true,    'inputfield': true  },  'imageselect': {    'template': '<div>' +      '<input type="hidden" name="<%= node.name %>" id="<%= node.id %>" value="<%= value %>" />' +      '<div class="dropdown">' +      '<a class="btn<% if (buttonClass && node.value) { %> <%= buttonClass %><% } else { %> btn-default<% } %>" data-toggle="dropdown" href="#"<% if (node.value) { %> style="max-width:<%= width %>px;max-height:<%= height %>px"<% } %>>' +        '<% if (node.value) { %><img src="<% if (!node.value.match(/^https?:/)) { %><%= prefix %><% } %><%= node.value %><%= suffix %>" alt="" /><% } else { %><%= buttonTitle %><% } %>' +      '</a>' +      '<div class="dropdown-menu navbar" id="<%= node.id %>_dropdown">' +        '<div>' +        '<% _.each(node.options, function(key, idx) { if ((idx > 0) && ((idx % columns) === 0)) { %></div><div><% } %><a class="btn<% if (buttonClass) { %> <%= buttonClass %><% } else { %> btn-default<% } %>" style="max-width:<%= width %>px;max-height:<%= height %>px"><% if (key instanceof Object) { %><img src="<% if (!key.value.match(/^https?:/)) { %><%= prefix %><% } %><%= key.value %><%= suffix %>" alt="<%= key.title %>" /></a><% } else { %><img src="<% if (!key.match(/^https?:/)) { %><%= prefix %><% } %><%= key %><%= suffix %>" alt="" /><% } %></a> <% }); %>' +        '</div>' +        '<div class="pagination-right"><a class="btn btn-default">Reset</a></div>' +      '</div>' +      '</div>' +      '</div>',    'fieldtemplate': true,    'inputfield': true,    'onBeforeRender': function (data, node) {      var elt = node.formElement || {};      var nbRows = null;      var maxColumns = elt.imageSelectorColumns || 5;      data.buttonTitle = elt.imageSelectorTitle || 'Select...';      data.prefix = elt.imagePrefix || '';      data.suffix = elt.imageSuffix || '';      data.width = elt.imageWidth || 32;      data.height = elt.imageHeight || 32;      data.buttonClass = elt.imageButtonClass || false;      if (node.options.length > maxColumns) {        nbRows = Math.ceil(node.options.length / maxColumns);        data.columns = Math.ceil(node.options.length / nbRows);      }      else {        data.columns = maxColumns;      }    },    'getElement': function (el) {      return $(el).parent().get(0);    },    'onInsert': function (evt, node) {      $(node.el).on('click', '.dropdown-menu a', function (evt) {        evt.preventDefault();        evt.stopPropagation();        var img = (evt.target.nodeName.toLowerCase() === 'img') ?          $(evt.target) :          $(evt.target).find('img');        var value = img.attr('src');        var elt = node.formElement || {};        var prefix = elt.imagePrefix || '';        var suffix = elt.imageSuffix || '';        var width = elt.imageWidth || 32;        var height = elt.imageHeight || 32;        if (value) {          if (value.indexOf(prefix) === 0) {            value = value.substring(prefix.length);          }          value = value.substring(0, value.length - suffix.length);          $(node.el).find('input').attr('value', value);          $(node.el).find('a[data-toggle="dropdown"]')            .addClass(elt.imageButtonClass)            .attr('style', 'max-width:' + width + 'px;max-height:' + height + 'px')            .html('<img src="' + (!value.match(/^https?:/) ? prefix : '') + value + suffix + '" alt="" />');        }        else {          $(node.el).find('input').attr('value', '');          $(node.el).find('a[data-toggle="dropdown"]')            .removeClass(elt.imageButtonClass)            .removeAttr('style')            .html(elt.imageSelectorTitle || 'Select...');        }      });    }  },  'iconselect': {    'template': '<div>' +      '<input type="hidden" name="<%= node.name %>" id="<%= node.id %>" value="<%= value %>" />' +      '<div class="dropdown">' +      '<a class="btn<% if (buttonClass && node.value) { %> <%= buttonClass %><% } %>" data-toggle="dropdown" href="#"<% if (node.value) { %> style="max-width:<%= width %>px;max-height:<%= height %>px"<% } %>>' +        '<% if (node.value) { %><i class="icon-<%= node.value %>" /><% } else { %><%= buttonTitle %><% } %>' +      '</a>' +      '<div class="dropdown-menu navbar" id="<%= node.id %>_dropdown">' +        '<div>' +        '<% _.each(node.options, function(key, idx) { if ((idx > 0) && ((idx % columns) === 0)) { %></div><div><% } %><a class="btn<% if (buttonClass) { %> <%= buttonClass %><% } %>" ><% if (key instanceof Object) { %><i class="icon-<%= key.value %>" alt="<%= key.title %>" /></a><% } else { %><i class="icon-<%= key %>" alt="" /><% } %></a> <% }); %>' +        '</div>' +        '<div class="pagination-right"><a class="btn">Reset</a></div>' +      '</div>' +      '</div>' +      '</div>',    'fieldtemplate': true,    'inputfield': true,    'onBeforeRender': function (data, node) {      var elt = node.formElement || {};      var nbRows = null;      var maxColumns = elt.imageSelectorColumns || 5;      data.buttonTitle = elt.imageSelectorTitle || 'Select...';      data.buttonClass = elt.imageButtonClass || false;      if (node.options.length > maxColumns) {        nbRows = Math.ceil(node.options.length / maxColumns);        data.columns = Math.ceil(node.options.length / nbRows);      }      else {        data.columns = maxColumns;      }    },    'getElement': function (el) {      return $(el).parent().get(0);    },    'onInsert': function (evt, node) {      $(node.el).on('click', '.dropdown-menu a', function (evt) {        evt.preventDefault();        evt.stopPropagation();        var i = (evt.target.nodeName.toLowerCase() === 'i') ?          $(evt.target) :          $(evt.target).find('i');        var value = i.attr('class');        var elt = node.formElement || {};        if (value) {          value = value;          $(node.el).find('input').attr('value', value);          $(node.el).find('a[data-toggle="dropdown"]')            .addClass(elt.imageButtonClass)            .html('<i class="'+ value +'" alt="" />');        }        else {          $(node.el).find('input').attr('value', '');          $(node.el).find('a[data-toggle="dropdown"]')            .removeClass(elt.imageButtonClass)            .html(elt.imageSelectorTitle || 'Select...');        }      });    }  },  'radios':{    'template': '<div id="<%= node.id %>"><% _.each(node.options, function(key, val) { %><div class="radio"><label><input<%= (fieldHtmlClass ? " class=\'" + fieldHtmlClass + "\'": "") %> type="radio" <% if (((key instanceof Object) && (value === key.value)) || (value === key)) { %> checked="checked" <% } %> name="<%= node.name %>" value="<%= (key instanceof Object ? key.value : key) %>"' +      '<%= (node.disabled? " disabled" : "")%>' +      '<%= (node.schemaElement && node.schemaElement.required ? " required=\'required\'" : "") %>' +      '/><%= (key instanceof Object ? key.title : key) %></label></div> <% }); %></div>',    'fieldtemplate': true,    'inputfield': true  },  'radiobuttons': {    'template': '<div id="<%= node.id %>">' +      '<% _.each(node.options, function(key, val) { %>' +        '<label class="btn btn-default">' +        '<input<%= (fieldHtmlClass ? " class=\'" + fieldHtmlClass + "\'": "") %> type="radio" style="position:absolute;left:-9999px;" ' +        '<% if (((key instanceof Object) && (value === key.value)) || (value === key)) { %> checked="checked" <% } %> name="<%= node.name %>" value="<%= (key instanceof Object ? key.value : key) %>" />' +        '<span><%= (key instanceof Object ? key.title : key) %></span></label> ' +        '<% }); %>' +      '</div>',    'fieldtemplate': true,    'inputfield': true,    'onInsert': function (evt, node) {      var activeClass = 'active';      var elt = node.formElement || {};      if (elt.activeClass) {        activeClass += ' ' + elt.activeClass;      }      $(node.el).find('label').on('click', function () {        $(this).parent().find('label').removeClass(activeClass);        $(this).addClass(activeClass);      });    }  },  'checkboxes':{    'template': '<div><%= choiceshtml %></div>',    'fieldtemplate': true,    'inputfield': true,    'onBeforeRender': function (data, node) {      // Build up choices from the enumeration list      var choices = null;      var choiceshtml = null;      var template = '<div class="checkbox"><label>' +        '<input type="checkbox" <% if (value) { %> checked="checked" <% } %> name="<%= name %>" value="1"' +        '<%= (node.disabled? " disabled" : "")%>' +        '/><%= title %></label></div>';      if (!node || !node.schemaElement) return;      if (node.schemaElement.items) {        choices =          node.schemaElement.items["enum"] ||          node.schemaElement.items[0]["enum"];      } else {        choices = node.schemaElement["enum"];      }      if (!choices) return;      choiceshtml = '';      _.each(choices, function (choice, idx) {        choiceshtml += _.template(template, fieldTemplateSettings)({          name: node.key + '[' + idx + ']',          value: _.include(node.value, choice),          title: hasOwnProperty(node.formElement.titleMap, choice) ? node.formElement.titleMap[choice] : choice,          node: node        });      });      data.choiceshtml = choiceshtml;    }  },  'array': {    'template': '<div id="<%= id %>"><ul class="_jsonform-array-ul" style="list-style-type:none;"><%= children %></ul>' +      '<span class="_jsonform-array-buttons">' +        '<a href="#" class="btn btn-default _jsonform-array-addmore"><i class="glyphicon glyphicon-plus-sign" title="Add new"></i></a> ' +        '<a href="#" class="btn btn-default _jsonform-array-deletelast"><i class="glyphicon glyphicon-minus-sign" title="Delete last"></i></a>' +      '</span>' +      '</div>',    'fieldtemplate': true,    'array': true,    'childTemplate': function (inner) {      if ($('').sortable) {        // Insert a "draggable" icon        // floating to the left of the main element        return '<li data-idx="<%= node.childPos %>">' +          '<span class="draggable line"><i class="glyphicon glyphicon-list" title="Move item"></i></span>' +          inner +          '</li>';      }      else {        return '<li data-idx="<%= node.childPos %>">' +          inner +          '</li>';      }    },    'onInsert': function (evt, node) {      var $nodeid = $(node.el).find('#' + escapeSelector(node.id));      var boundaries = node.getArrayBoundaries();      // Switch two nodes in an array      var moveNodeTo = function (fromIdx, toIdx) {        // Note "switchValuesWith" extracts values from the DOM since field        // values are not synchronized with the tree data structure, so calls        // to render are needed at each step to force values down to the DOM        // before next move.        // TODO: synchronize field values and data structure completely and        // call render only once to improve efficiency.        if (fromIdx === toIdx) return;        var incr = (fromIdx < toIdx) ? 1: -1;        var i = 0;        var parentEl = $('> ul', $nodeid);        for (i = fromIdx; i !== toIdx; i += incr) {          node.children[i].switchValuesWith(node.children[i + incr]);          node.children[i].render(parentEl.get(0));          node.children[i + incr].render(parentEl.get(0));        }        // No simple way to prevent DOM reordering with jQuery UI Sortable,        // so we're going to need to move sorted DOM elements back to their        // origin position in the DOM ourselves (we switched values but not        // DOM elements)        var fromEl = $(node.children[fromIdx].el);        var toEl = $(node.children[toIdx].el);        fromEl.detach();        toEl.detach();        if (fromIdx < toIdx) {          if (fromIdx === 0) parentEl.prepend(fromEl);          else $(node.children[fromIdx-1].el).after(fromEl);          $(node.children[toIdx-1].el).after(toEl);        }        else {          if (toIdx === 0) parentEl.prepend(toEl);          else $(node.children[toIdx-1].el).after(toEl);          $(node.children[fromIdx-1].el).after(fromEl);        }      };      $('> span > a._jsonform-array-addmore', $nodeid).click(function (evt) {        evt.preventDefault();        evt.stopPropagation();        var idx = node.children.length;        if (boundaries.maxItems >= 0) {          if (node.children.length > boundaries.maxItems - 2) {            $nodeid.find('> span > a._jsonform-array-addmore')              .addClass('disabled');          }          if (node.children.length > boundaries.maxItems - 1) {            return false;          }        }        node.insertArrayItem(idx, $('> ul', $nodeid).get(0));        if ((boundaries.minItems <= 0) ||            ((boundaries.minItems > 0) &&              (node.children.length > boundaries.minItems - 1))) {          $nodeid.find('> span > a._jsonform-array-deletelast')            .removeClass('disabled');        }      });      //Simulate Users click to setup the form with its minItems      var curItems = $('> ul > li', $nodeid).length;      if ((boundaries.minItems > 0) &&          (curItems < boundaries.minItems)) {        for (var i = 0; i < (boundaries.minItems - 1) && ($nodeid.find('> ul > li').length < boundaries.minItems); i++) {          //console.log('Calling click: ',$nodeid);          //$('> span > a._jsonform-array-addmore', $nodeid).click();          node.insertArrayItem(curItems, $nodeid.find('> ul').get(0));        }      }      if ((boundaries.minItems > 0) &&          (node.children.length <= boundaries.minItems)) {        $nodeid.find('> span > a._jsonform-array-deletelast')          .addClass('disabled');      }      $('> span > a._jsonform-array-deletelast', $nodeid).click(function (evt) {        var idx = node.children.length - 1;        evt.preventDefault();        evt.stopPropagation();        if (boundaries.minItems > 0) {          if (node.children.length < boundaries.minItems + 2) {            $nodeid.find('> span > a._jsonform-array-deletelast')              .addClass('disabled');          }          if (node.children.length <= boundaries.minItems) {            return false;          }        }        else if (node.children.length === 1) {          $nodeid.find('> span > a._jsonform-array-deletelast')            .addClass('disabled');        }        node.deleteArrayItem(idx);        if ((boundaries.maxItems >= 0) && (idx <= boundaries.maxItems - 1)) {          $nodeid.find('> span > a._jsonform-array-addmore')            .removeClass('disabled');        }      });      if ($(node.el).sortable) {        $('> ul', $nodeid).sortable();        $('> ul', $nodeid).bind('sortstop', function (event, ui) {          var idx = $(ui.item).data('idx');          var newIdx = $(ui.item).index();          moveNodeTo(idx, newIdx);        });      }    }  },  'tabarray': {    'template': '<div id="<%= id %>"><div class="tabbable tabs-left">' +      '<ul class="nav nav-tabs">' +        '<%= tabs %>' +      '</ul>' +      '<div class="tab-content">' +        '<%= children %>' +      '</div>' +      '</div>' +      '<a href="#" class="btn btn-default _jsonform-array-addmore"><i class="glyphicon glyphicon-plus-sign" title="Add new"></i></a> ' +      '<a href="#" class="btn btn-default _jsonform-array-deleteitem"><i class="glyphicon glyphicon-minus-sign" title="Delete item"></i></a></div>',    'fieldtemplate': true,    'array': true,    'childTemplate': function (inner) {      return '<div data-idx="<%= node.childPos %>" class="tab-pane">' +        inner +        '</div>';    },    'onBeforeRender': function (data, node) {      // Generate the initial 'tabs' from the children      var tabs = '';      _.each(node.children, function (child, idx) {        var title = child.legend ||          child.title ||          ('Item ' + (idx+1));        tabs += '<li data-idx="' + idx + '"' +          ((idx === 0) ? ' class="active"' : '') +          '><a class="draggable tab" data-toggle="tab">' +          escapeHTML(title) +          '</a></li>';      });      data.tabs = tabs;    },    'onInsert': function (evt, node) {      var $nodeid = $(node.el).find('#' + escapeSelector(node.id));      var boundaries = node.getArrayBoundaries();      var moveNodeTo = function (fromIdx, toIdx) {        // Note "switchValuesWith" extracts values from the DOM since field        // values are not synchronized with the tree data structure, so calls        // to render are needed at each step to force values down to the DOM        // before next move.        // TODO: synchronize field values and data structure completely and        // call render only once to improve efficiency.        if (fromIdx === toIdx) return;        var incr = (fromIdx < toIdx) ? 1: -1;        var i = 0;        var tabEl = $('> .tabbable > .tab-content', $nodeid).get(0);        for (i = fromIdx; i !== toIdx; i += incr) {          node.children[i].switchValuesWith(node.children[i + incr]);          node.children[i].render(tabEl);          node.children[i + incr].render(tabEl);        }      };      // Refreshes the list of tabs      var updateTabs = function (selIdx) {        var tabs = '';        var activateFirstTab = false;        if (selIdx === undefined) {          selIdx = $('> .tabbable > .nav-tabs .active', $nodeid).data('idx');          if (selIdx) {            selIdx = parseInt(selIdx, 10);          }          else {            activateFirstTab = true;            selIdx = 0;          }        }        if (selIdx >= node.children.length) {          selIdx = node.children.length - 1;        }        _.each(node.children, function (child, idx) {          $('> .tabbable > .tab-content > [data-idx="' + idx + '"] > fieldset > legend', $nodeid).html(child.legend);          var title = child.legend || child.title || ('Item ' + (idx+1));          tabs += '<li data-idx="' + idx + '">' +                  '<a class="draggable tab" data-toggle="tab">' +                  escapeHTML(title) +                  '</a></li>';        });        $('> .tabbable > .nav-tabs', $nodeid).html(tabs);        if (activateFirstTab) {          $('> .tabbable > .nav-tabs [data-idx="0"]', $nodeid).addClass('active');        }        $('> .tabbable > .nav-tabs [data-toggle="tab"]', $nodeid).eq(selIdx).click();      };      $('> a._jsonform-array-deleteitem', $nodeid).click(function (evt) {        var idx = $('> .tabbable > .nav-tabs .active', $nodeid).data('idx');        evt.preventDefault();        evt.stopPropagation();        if (boundaries.minItems > 0) {          if (node.children.length < boundaries.minItems + 1) {            $nodeid.find('> a._jsonform-array-deleteitem')              .addClass('disabled');          }          if (node.children.length <= boundaries.minItems) return false;        }        node.deleteArrayItem(idx);        updateTabs();        if ((node.children.length < boundaries.minItems + 1) ||            (node.children.length === 0)) {          $nodeid.find('> a._jsonform-array-deleteitem').addClass('disabled');        }        if ((boundaries.maxItems >= 0) &&            (node.children.length <= boundaries.maxItems)) {          $nodeid.find('> a._jsonform-array-addmore').removeClass('disabled');        }      });      $('> a._jsonform-array-addmore', $nodeid).click(function (evt) {        var idx = node.children.length;        if (boundaries.maxItems>=0) {          if (node.children.length>boundaries.maxItems-2) {            $('> a._jsonform-array-addmore', $nodeid).addClass("disabled");          }          if (node.children.length > boundaries.maxItems - 1) {            return false;          }        }        evt.preventDefault();        evt.stopPropagation();        node.insertArrayItem(idx,          $nodeid.find('> .tabbable > .tab-content').get(0));        updateTabs(idx);        if ((boundaries.minItems <= 0) ||            ((boundaries.minItems > 0) && (idx > boundaries.minItems - 1))) {          $nodeid.find('> a._jsonform-array-deleteitem').removeClass('disabled');        }      });      $(node.el).on('legendUpdated', function (evt) {        updateTabs();        evt.preventDefault();        evt.stopPropagation();      });      if ($(node.el).sortable) {        $('> .tabbable > .nav-tabs', $nodeid).sortable({          containment: node.el,          tolerance: 'pointer'        });        $('> .tabbable > .nav-tabs', $nodeid).bind('sortstop', function (event, ui) {          var idx = $(ui.item).data('idx');          var newIdx = $(ui.item).index();          moveNodeTo(idx, newIdx);          updateTabs(newIdx);        });      }      // Simulate User's click to setup the form with its minItems      if ((boundaries.minItems >= 0)  &&           (node.children.length <= boundaries.minItems)) {        for (var i = 0; i < (boundaries.minItems - 1); i++) {          $nodeid.find('> a._jsonform-array-addmore').click();        }        $nodeid.find('> a._jsonform-array-deleteitem').addClass('disabled');        updateTabs();      }      if ((boundaries.maxItems >= 0) &&          (node.children.length >= boundaries.maxItems)) {        $nodeid.find('> a._jsonform-array-addmore').addClass('disabled');      }      if ((boundaries.minItems >= 0) &&          (node.children.length <= boundaries.minItems)) {        $nodeid.find('> a._jsonform-array-deleteitem').addClass('disabled');      }    }  },  'help': {    'template':'<span class="help-block" style="padding-top:5px"><%= elt.helpvalue %></span>',    'fieldtemplate': true  },  'msg': {    'template': '<%= elt.msg %>'  },  'fieldset': {    'template': '<fieldset class="form-group jsonform-error-<%= keydash %> <% if (elt.expandable) { %>expandable<% } %> <%= elt.htmlClass?elt.htmlClass:"" %>" ' +      '<% if (id) { %> id="<%= id %>"<% } %>' +      '>' +      '<% if (node.title || node.legend) { %><legend><%= node.title || node.legend %></legend><% } %>' +      '<% if (elt.expandable) { %><div class="form-group"><% } %>' +      '<%= children %>' +      '<% if (elt.expandable) { %></div><% } %>' +      '</fieldset>',    onInsert: function (evt, node) {      $('.expandable > div, .expandable > fieldset', node.el).hide();      // See #233       $(".expandable", node.el).removeClass("expanded");    }  },  'advancedfieldset': {    'template': '<fieldset' +      '<% if (id) { %> id="<%= id %>"<% } %>' +      ' class="expandable <%= elt.htmlClass?elt.htmlClass:"" %>">' +      '<legend><%= (node.title || node.legend) ? (node.title || node.legend) : "Advanced options" %></legend>' +      '<div class="form-group">' +      '<%= children %>' +      '</div>' +      '</fieldset>',    onInsert: function (evt, node) {      $('.expandable > div, .expandable > fieldset', node.el).hide();      // See #233       $(".expandable", node.el).removeClass("expanded");    }  },  'authfieldset': {    'template': '<fieldset' +      '<% if (id) { %> id="<%= id %>"<% } %>' +      ' class="expandable <%= elt.htmlClass?elt.htmlClass:"" %>">' +      '<legend><%= (node.title || node.legend) ? (node.title || node.legend) : "Authentication settings" %></legend>' +      '<div class="form-group">' +      '<%= children %>' +      '</div>' +      '</fieldset>',    onInsert: function (evt, node) {      $('.expandable > div, .expandable > fieldset', node.el).hide();      // See #233       $(".expandable", node.el).removeClass("expanded");    }  },  'submit':{    'template':'<input type="submit" <% if (id) { %> id="<%= id %>" <% } %> class="btn btn-primary <%= elt.htmlClass?elt.htmlClass:"" %>" value="<%= value || node.title %>"<%= (node.disabled? " disabled" : "")%>/>'  },  'button':{    'template':' <button type="button" <% if (id) { %> id="<%= id %>" <% } %> class="btn btn-default <%= elt.htmlClass?elt.htmlClass:"" %>"><%= node.title %></button> '  },  'actions':{    'template':'<div class="<%= elt.htmlClass?elt.htmlClass:"" %>"><%= children %></div>'  },  'hidden':{    'template':'<input type="hidden" id="<%= id %>" name="<%= node.name %>" value="<%= escape(value) %>" />',    'inputfield': true  },  'selectfieldset': {    'template': '<fieldset class="tab-container <%= elt.htmlClass?elt.htmlClass:"" %>">' +      '<% if (node.legend) { %><legend><%= node.legend %></legend><% } %>' +      '<% if (node.formElement.key) { %><input type="hidden" id="<%= node.id %>" name="<%= node.name %>" value="<%= escape(value) %>" /><% } else { %>' +        '<a id="<%= node.id %>"></a><% } %>' +      '<div class="tabbable">' +        '<div class="form-group<%= node.formElement.hideMenu ? " hide" : "" %>">' +          '<% if (!elt.notitle) { %><label for="<%= node.id %>"><%= node.title ? node.title : node.name %></label><% } %>' +          '<div class="controls"><%= tabs %></div>' +        '</div>' +        '<div class="tab-content">' +          '<%= children %>' +        '</div>' +      '</div>' +      '</fieldset>',    'inputfield': true,    'getElement': function (el) {      return $(el).parent().get(0);    },    'childTemplate': function (inner) {      return '<div data-idx="<%= node.childPos %>" class="tab-pane' +        '<% if (node.active) { %> active<% } %>">' +        inner +        '</div>';    },    'onBeforeRender': function (data, node) {      // Before rendering, this function ensures that:      // 1. direct children have IDs (used to show/hide the tabs contents)      // 2. the tab to active is flagged accordingly. The active tab is      // the first one, except if form values are available, in which case      // it's the first tab for which there is some value available (or back      // to the first one if there are none)      // 3. the HTML of the select field used to select tabs is exposed in the      // HTML template data as "tabs"      var children = null;      var choices = [];      if (node.schemaElement) {        choices = node.schemaElement['enum'] || [];      }      if (node.options) {        children = _.map(node.options, function (option, idx) {          var child = node.children[idx];          child.childPos = idx; // When nested the childPos is always 0.          if (option instanceof Object) {            option = _.extend({ node: child }, option);            option.title = option.title ||              child.legend ||              child.title ||              ('Option ' + (child.childPos+1));            option.value = isSet(option.value) ? option.value :              isSet(choices[idx]) ? choices[idx] : idx;            return option;          }          else {            return {              title: option,              value: isSet(choices[child.childPos]) ?                choices[child.childPos] :                child.childPos,              node: child            };          }        });      }      else {        children = _.map(node.children, function (child, idx) {          child.childPos = idx; // When nested the childPos is always 0.          return {            title: child.legend || child.title || ('Option ' + (child.childPos+1)),            value: choices[child.childPos] || child.childPos,            node: child          };        });      }      // Reset each children to inactive so that they are not shown on insert      // The active one will then be shown later one. This is useful when sorting      // arrays with selectfieldset, otherwise both fields could be active at the      // same time.      _.each(children, function (child, idx) {        child.node.active = false      });      var activeChild = null;      if (data.value) {        activeChild = _.find(children, function (child) {          return (child.value === node.value);        });      }      if (!activeChild) {        activeChild = _.find(children, function (child) {          return child.node.hasNonDefaultValue();        });      }      if (!activeChild) {        activeChild = children[0];      }      activeChild.node.active = true;      data.value = activeChild.value;      var elt = node.formElement;      var tabs = '<select class="nav"' +        (node.disabled ? ' disabled' : '') +        '>';      _.each(children, function (child, idx) {        tabs += '<option data-idx="' + idx + '" value="' + child.value + '"' +          (child.node.active ? ' selected="selected" class="active"' : '') +          '>' +          escapeHTML(child.title) +          '</option>';      });      tabs += '</select>';      data.tabs = tabs;      return data;    },    'onInsert': function (evt, node) {      $(node.el).find('select.nav').first().on('change', function (evt) {        var $option = $(this).find('option:selected');        $(node.el).find('input[type="hidden"]').first().val($option.attr('value'));      });    }  },  'optionfieldset': {    'template': '<div' +      '<% if (node.id) { %> id="<%= node.id %>"<% } %>' +      '>' +      '<%= children %>' +      '</div>'  },  'section': {    'template': '<div' +      '<% if (elt.htmlClass) { %> class="<%= elt.htmlClass %>"<% } %>' +      '<% if (node.id) { %> id="<%= node.id %>"<% } %>' +      '><%= children %></div>'  },  /**   * A "questions" field renders a series of question fields and binds the   * result to the value of a schema key.   */  'questions': {    'template': '<div>' +      '<input type="hidden" id="<%= node.id %>" name="<%= node.name %>" value="<%= escape(value) %>" />' +      '<%= children %>' +      '</div>',    'fieldtemplate': true,    'inputfield': true,    'getElement': function (el) {      return $(el).parent().get(0);    },    'onInsert': function (evt, node) {      if (!node.children || (node.children.length === 0)) return;      _.each(node.children, function (child) {        $(child.el).hide();      });      $(node.children[0].el).show();    }  },  /**   * A "question" field lets user choose a response among possible choices.   * The field is not associated with any schema key. A question should be   * part of a "questions" field that binds a series of questions to a   * schema key.   */  'question': {    'template': '<div id="<%= node.id %>"><% _.each(node.options, function(key, val) { %><label class="<%= (node.formElement.optionsType === "radiobuttons") ? "btn btn-default" : "" %><%= ((key instanceof Object && key.htmlClass) ? " " + key.htmlClass : "") %>"><input type="radio" <% if (node.formElement.optionsType === "radiobuttons") { %> style="position:absolute;left:-9999px;" <% } %>name="<%= node.id %>" value="<%= val %>"<%= (node.disabled? " disabled" : "")%>/><span><%= (key instanceof Object ? key.title : key) %></span></label> <% }); %></div>',    'fieldtemplate': true,    'onInsert': function (evt, node) {      var activeClass = 'active';      var elt = node.formElement || {};      if (elt.activeClass) {        activeClass += ' ' + elt.activeClass;      }      // Bind to change events on radio buttons      $(node.el).find('input[type="radio"]').on('change', function (evt) {        var questionNode = null;        var option = node.options[$(this).val()];        if (!node.parentNode || !node.parentNode.el) return;        $(this).parent().parent().find('label').removeClass(activeClass);        $(this).parent().addClass(activeClass);        $(node.el).nextAll().hide();        $(node.el).nextAll().find('input[type="radio"]').prop('checked', false);        // Execute possible actions (set key value, form submission, open link,        // move on to next question)        if (option.value) {          // Set the key of the 'Questions' parent          $(node.parentNode.el).find('input[type="hidden"]').val(option.value);        }        if (option.next) {          questionNode = _.find(node.parentNode.children, function (child) {            return (child.formElement && (child.formElement.qid === option.next));          });          $(questionNode.el).show();          $(questionNode.el).nextAll().hide();          $(questionNode.el).nextAll().find('input[type="radio"]').prop('checked', false);        }        if (option.href) {          if (option.target) {            window.open(option.href, option.target);          }          else {            window.location = option.href;          }        }        if (option.submit) {          setTimeout(function () {            node.ownerTree.submit();          }, 0);        }      });    }  }};//Allow to access subproperties by splitting "."/** * Retrieves the key identified by a path selector in the structured object. * * Levels in the path are separated by a dot. Array items are marked * with [x]. For instance: *  foo.bar[3].baz * * @function * @param {Object} obj Structured object to parse * @param {String} key Path to the key to retrieve * @param {boolean} ignoreArrays True to use first element in an array when *   stucked on a property. This parameter is basically only useful when *   parsing a JSON schema for which the "items" property may either be an *   object or an array with one object (only one because JSON form does not *   support mix of items for arrays). * @return {Object} The key's value. */jsonform.util.getObjKey = function (obj, key, ignoreArrays) {  var innerobj = obj;  var keyparts = key.split(".");  var subkey = null;  var arrayMatch = null;  var prop = null;  for (var i = 0; i < keyparts.length; i++) {    if ((innerobj === null) || (typeof innerobj !== "object")) return null;    subkey = keyparts[i];    prop = subkey.replace(reArray, '');    reArray.lastIndex = 0;    arrayMatch = reArray.exec(subkey);    if (arrayMatch) {      while (true) {        if (prop && !_.isArray(innerobj[prop])) return null;        innerobj = prop ? innerobj[prop][parseInt(arrayMatch[1])] : innerobj[parseInt(arrayMatch[1])];        arrayMatch = reArray.exec(subkey);        if (!arrayMatch) break;        // In the case of multidimensional arrays,        // we should not take innerobj[prop][0] anymore,        // but innerobj[0] directly        prop = null;      }    } else if (ignoreArrays &&        !innerobj[prop] &&        _.isArray(innerobj) &&        innerobj[0]) {      innerobj = innerobj[0][prop];    } else {      innerobj = innerobj[prop];    }  }  if (ignoreArrays && _.isArray(innerobj) && innerobj[0]) {    return innerobj[0];  } else {    return innerobj;  }};/** * Sets the key identified by a path selector to the given value. * * Levels in the path are separated by a dot. Array items are marked * with [x]. For instance: *  foo.bar[3].baz * * The hierarchy is automatically created if it does not exist yet. * * @function * @param {Object} obj The object to build * @param {String} key The path to the key to set where each level *  is separated by a dot, and array items are flagged with [x]. * @param {Object} value The value to set, may be of any type. */jsonform.util.setObjKey = function(obj,key,value) {  var innerobj = obj;  var keyparts = key.split(".");  var subkey = null;  var arrayMatch = null;  var prop = null;  for (var i = 0; i < keyparts.length-1; i++) {    subkey = keyparts[i];    prop = subkey.replace(reArray, '');    reArray.lastIndex = 0;    arrayMatch = reArray.exec(subkey);    if (arrayMatch) {      // Subkey is part of an array      while (true) {        if (!_.isArray(innerobj[prop])) {          innerobj[prop] = [];        }        innerobj = innerobj[prop];        prop = parseInt(arrayMatch[1], 10);        arrayMatch = reArray.exec(subkey);        if (!arrayMatch) break;      }      if ((typeof innerobj[prop] !== 'object') ||        (innerobj[prop] === null)) {        innerobj[prop] = {};      }      innerobj = innerobj[prop];    }    else {      // "Normal" subkey      if ((typeof innerobj[prop] !== 'object') ||        (innerobj[prop] === null)) {        innerobj[prop] = {};      }      innerobj = innerobj[prop];    }  }  // Set the final value  subkey = keyparts[keyparts.length - 1];  prop = subkey.replace(reArray, '');  reArray.lastIndex = 0;  arrayMatch = reArray.exec(subkey);  if (arrayMatch) {    while (true) {      if (!_.isArray(innerobj[prop])) {        innerobj[prop] = [];      }      innerobj = innerobj[prop];      prop = parseInt(arrayMatch[1], 10);      arrayMatch = reArray.exec(subkey);      if (!arrayMatch) break;    }    innerobj[prop] = value;  }  else {    innerobj[prop] = value;  }};/** * Retrieves the key definition from the given schema. * * The key is identified by the path that leads to the key in the * structured object that the schema would generate. Each level is * separated by a '.'. Array levels are marked with []. For instance: *  foo.bar[].baz * ... to retrieve the definition of the key at the following location * in the JSON schema (using a dotted path notation): *  foo.properties.bar.items.properties.baz * * @function * @param {Object} schema The JSON schema to retrieve the key from * @param {String} key The path to the key, each level being separated *  by a dot and array items being flagged with []. * @return {Object} The key definition in the schema, null if not found. */var getSchemaKey = function(schema,key) {  var schemaKey = key    .replace(/\./g, '.properties.')    .replace(/\[[0-9]*\]/g, '.items');  var schemaDef = jsonform.util.getObjKey(schema, schemaKey, true);  if (schemaDef && schemaDef.$ref) {    throw new Error('JSONForm does not yet support schemas that use the ' +      '$ref keyword. See: https://github.com/joshfire/jsonform/issues/54');  }  return schemaDef;};/** * Truncates the key path to the requested depth. * * For instance, if the key path is: *  foo.bar[].baz.toto[].truc[].bidule * and the requested depth is 1, the returned key will be: *  foo.bar[].baz.toto * * Note the function includes the path up to the next depth level. * * @function * @param {String} key The path to the key in the schema, each level being *  separated by a dot and array items being flagged with []. * @param {Number} depth The array depth * @return {String} The path to the key truncated to the given depth. */var truncateToArrayDepth = function (key, arrayDepth) {  var depth = 0;  var pos = 0;  if (!key) return null;  if (arrayDepth > 0) {    while (depth < arrayDepth) {      pos = key.indexOf('[]', pos);      if (pos === -1) {        // Key path is not "deep" enough, simply return the full key        return key;      }      pos = pos + 2;      depth += 1;    }  }  // Move one step further to the right without including the final []  pos = key.indexOf('[]', pos);  if (pos === -1) return key;  else return key.substring(0, pos);};/** * Applies the array path to the key path. * * For instance, if the key path is: *  foo.bar[].baz.toto[].truc[].bidule * and the arrayPath [4, 2], the returned key will be: *  foo.bar[4].baz.toto[2].truc[].bidule * * @function * @param {String} key The path to the key in the schema, each level being *  separated by a dot and array items being flagged with []. * @param {Array(Number)} arrayPath The array path to apply, e.g. [4, 2] * @return {String} The path to the key that matches the array path. */var applyArrayPath = function (key, arrayPath) {  var depth = 0;  if (!key) return null;  if (!arrayPath || (arrayPath.length === 0)) return key;  var newKey = key.replace(reArray, function (str, p1) {    // Note this function gets called as many times as there are [x] in the ID,    // from left to right in the string. The goal is to replace the [x] with    // the appropriate index in the new array path, if defined.    var newIndex = str;    if (isSet(arrayPath[depth])) {      newIndex = '[' + arrayPath[depth] + ']';    }    depth += 1;    return newIndex;  });  return newKey;};/** * Returns the initial value that a field identified by its key * should take. * * The "initial" value is defined as: * 1. the previously submitted value if already submitted * 2. the default value defined in the layout of the form * 3. the default value defined in the schema * * The "value" returned is intended for rendering purpose, * meaning that, for fields that define a titleMap property, * the function returns the label, and not the intrinsic value. * * The function handles values that contains template strings, * e.g. {{values.foo[].bar}} or {{idx}}. * * When the form is a string, the function truncates the resulting string * to meet a potential "maxLength" constraint defined in the schema, using * "..." to mark the truncation. Note it does not validate the resulting * string against other constraints (e.g. minLength, pattern) as it would * be hard to come up with an automated course of action to "fix" the value. * * @function * @param {Object} formObject The JSON Form object * @param {String} key The generic key path (e.g. foo[].bar.baz[]) * @param {Array(Number)} arrayPath The array path that identifies *  the unique value in the submitted form (e.g. [1, 3]) * @param {Object} tpldata Template data object * @param {Boolean} usePreviousValues true to use previously submitted values *  if defined. */var getInitialValue = function (formObject, key, arrayPath, tpldata, usePreviousValues) {  var value = null;  // Complete template data for template function  tpldata = tpldata || {};  tpldata.idx = tpldata.idx ||    (arrayPath ? arrayPath[arrayPath.length-1] : 1);  tpldata.value = isSet(tpldata.value) ? tpldata.value : '';  tpldata.getValue = tpldata.getValue || function (key) {    return getInitialValue(formObject, key, arrayPath, tpldata, usePreviousValues);  };  // Helper function that returns the form element that explicitly  // references the given key in the schema.  var getFormElement = function (elements, key) {    var formElement = null;    if (!elements || !elements.length) return null;    _.each(elements, function (elt) {      if (formElement) return;      if (elt === key) {        formElement = { key: elt };        return;      }      if (_.isString(elt)) return;      if (elt.key === key) {        formElement = elt;      }      else if (elt.items) {        formElement = getFormElement(elt.items, key);      }    });    return formElement;  };  var formElement = getFormElement(formObject.form || [], key);  var schemaElement = getSchemaKey(formObject.schema.properties, key);  if (usePreviousValues && formObject.value) {    // If values were previously submitted, use them directly if defined    value = jsonform.util.getObjKey(formObject.value, applyArrayPath(key, arrayPath));  }  if (!isSet(value)) {    if (formElement && (typeof formElement['value'] !== 'undefined')) {      // Extract the definition of the form field associated with      // the key as it may override the schema's default value      // (note a "null" value overrides a schema default value as well)      value = formElement['value'];    }    else if (schemaElement) {      // Simply extract the default value from the schema      if (isSet(schemaElement['default'])) {        value = schemaElement['default'];      }    }    if (value && value.indexOf('{{values.') !== -1) {      // This label wants to use the value of another input field.      // Convert that construct into {{getValue(key)}} for      // Underscore to call the appropriate function of formData      // when template gets called (note calling a function is not      // exactly Mustache-friendly but is supported by Underscore).      value = value.replace(        /\{\{values\.([^\}]+)\}\}/g,        '{{getValue("$1")}}');    }    if (value) {      value = _.template(value, valueTemplateSettings)(tpldata);    }  }  // TODO: handle on the formElement.options, because user can setup it too.  // Apply titleMap if needed  if (isSet(value) && formElement && hasOwnProperty(formElement.titleMap, value)) {    value = _.template(formElement.titleMap[value], valueTemplateSettings)(tpldata);  }  // Check maximum length of a string  if (value && _.isString(value) &&    schemaElement && schemaElement.maxLength) {    if (value.length > schemaElement.maxLength) {      // Truncate value to maximum length, adding continuation dots      value = value.substr(0, schemaElement.maxLength - 1) + '…';    }  }  if (!isSet(value)) {    return null;  }  else {    return value;  }};/** * Represents a node in the form. * * Nodes that have an ID are linked to the corresponding DOM element * when rendered * * Note the form element and the schema elements that gave birth to the * node may be shared among multiple nodes (in the case of arrays). * * @class */var formNode = function () {  /**   * The node's ID (may not be set)   */  this.id = null;  /**   * The node's key path (may not be set)   */  this.key = null;  /**   * DOM element associated witht the form element.   *   * The DOM element is set when the form element is rendered.   */  this.el = null;  /**   * Link to the form element that describes the node's layout   * (note the form element is shared among nodes in arrays)   */  this.formElement = null;  /**   * Link to the schema element that describes the node's value constraints   * (note the schema element is shared among nodes in arrays)   */  this.schemaElement = null;  /**   * Pointer to the "view" associated with the node, typically the right   * object in jsonform.elementTypes   */  this.view = null;  /**   * Node's subtree (if one is defined)   */  this.children = [];  /**   * A pointer to the form tree the node is attached to   */  this.ownerTree = null;  /**   * A pointer to the parent node of the node in the tree   */  this.parentNode = null;  /**   * Child template for array-like nodes.   *   * The child template gets cloned to create new array items.   */  this.childTemplate = null;  /**   * Direct children of array-like containers may use the value of a   * specific input field in their subtree as legend. The link to the   * legend child is kept here and initialized in computeInitialValues   * when a child sets "valueInLegend"   */  this.legendChild = null;  /**   * The path of indexes that lead to the current node when the   * form element is not at the root array level.   *   * Note a form element may well be nested element and still be   * at the root array level. That's typically the case for "fieldset"   * elements. An array level only gets created when a form element   * is of type "array" (or a derivated type such as "tabarray").   *   * The array path of a form element linked to the foo[2].bar.baz[3].toto   * element in the submitted values is [2, 3] for instance.   *   * The array path is typically used to compute the right ID for input   * fields. It is also used to update positions when an array item is   * created, moved around or suppressed.   *   * @type {Array(Number)}   */  this.arrayPath = [];  /**   * Position of the node in the list of children of its parents   */  this.childPos = 0;};/** * Clones a node * * @function * @param {formNode} New parent node to attach the node to * @return {formNode} Cloned node */formNode.prototype.clone = function (parentNode) {  var node = new formNode();  node.arrayPath = _.clone(this.arrayPath);  node.ownerTree = this.ownerTree;  node.parentNode = parentNode || this.parentNode;  node.formElement = this.formElement;  node.schemaElement = this.schemaElement;  node.view = this.view;  node.children = _.map(this.children, function (child) {    return child.clone(node);  });  if (this.childTemplate) {    node.childTemplate = this.childTemplate.clone(node);  }  return node;};/** * Returns true if the subtree that starts at the current node * has some non empty value attached to it */formNode.prototype.hasNonDefaultValue = function () {  // hidden elements don't count because they could make the wrong selectfieldset element active  if (this.formElement && this.formElement.type=="hidden") {    return false;  }  if (this.value && !this.defaultValue) {    return true;  }  var child = _.find(this.children, function (child) {    return child.hasNonDefaultValue();  });  return !!child;};/** * Attaches a child node to the current node. * * The child node is appended to the end of the list. * * @function * @param {formNode} node The child node to append * @return {formNode} The inserted node (same as the one given as parameter) */formNode.prototype.appendChild = function (node) {  node.parentNode = this;  node.childPos = this.children.length;  this.children.push(node);  return node;};/** * Removes the last child of the node. * * @function */formNode.prototype.removeChild = function () {  var child = this.children[this.children.length-1];  if (!child) return;  // Remove the child from the DOM  $(child.el).remove();  // Remove the child from the array  return this.children.pop();};/** * Moves the user entered values set in the current node's subtree to the * given node's subtree. * * The target node must follow the same structure as the current node * (typically, they should have been generated from the same node template) * * The current node MUST be rendered in the DOM. * * TODO: when current node is not in the DOM, extract values from formNode.value * properties, so that the function be available even when current node is not * in the DOM. * * Moving values around allows to insert/remove array items at arbitrary * positions. * * @function * @param {formNode} node Target node. */formNode.prototype.moveValuesTo = function (node) {  var values = this.getFormValues(node.arrayPath);  node.resetValues();  node.computeInitialValues(values, true);};/** * Switches nodes user entered values. * * The target node must follow the same structure as the current node * (typically, they should have been generated from the same node template) * * Both nodes MUST be rendered in the DOM. * * TODO: update getFormValues to work even if node is not rendered, using * formNode's "value" property. * * @function * @param {formNode} node Target node */formNode.prototype.switchValuesWith = function (node) {  var values = this.getFormValues(node.arrayPath);  var nodeValues = node.getFormValues(this.arrayPath);  node.resetValues();  node.computeInitialValues(values, true);  this.resetValues();  this.computeInitialValues(nodeValues, true);};/** * Resets all DOM values in the node's subtree. * * This operation also drops all array item nodes. * Note values are not reset to their default values, they are rather removed! * * @function */formNode.prototype.resetValues = function () {  var params = null;  var idx = 0;  // Reset value  this.value = null;  // Propagate the array path from the parent node  // (adding the position of the child for nodes that are direct  // children of array-like nodes)  if (this.parentNode) {    this.arrayPath = _.clone(this.parentNode.arrayPath);    if (this.parentNode.view && this.parentNode.view.array) {      this.arrayPath.push(this.childPos);    }  }  else {    this.arrayPath = [];  }  if (this.view && this.view.inputfield) {    // Simple input field, extract the value from the origin,    // set the target value and reset the origin value    params = $(':input', this.el).serializeArray();    _.each(params, function (param) {      // TODO: check this, there may exist corner cases with this approach      // (with multiple checkboxes for instance)      $('[name="' + escapeSelector(param.name) + '"]', $(this.el)).val('');    }, this);  }  else if (this.view && this.view.array) {    // The current node is an array, drop all children    while (this.children.length > 0) {      this.removeChild();    }  }  // Recurse down the tree  _.each(this.children, function (child) {    child.resetValues();  });};/** * Sets the child template node for the current node. * * The child template node is used to create additional children * in an array-like form element. The template is never rendered. * * @function * @param {formNode} node The child template node to set */formNode.prototype.setChildTemplate = function (node) {  this.childTemplate = node;  node.parentNode = this;};/** * Recursively sets values to all nodes of the current subtree * based on previously submitted values, or based on default * values when the submitted values are not enough * * The function should be called once in the lifetime of a node * in the tree. It expects its parent's arrayPath to be up to date. * * Three cases may arise: * 1. if the form element is a simple input field, the value is * extracted from previously submitted values of from default values * defined in the schema. * 2. if the form element is an array-like node, the child template * is used to create as many children as possible (and at least one). * 3. the function simply recurses down the node's subtree otherwise * (this happens when the form element is a fieldset-like element). * * @function * @param {Object} values Previously submitted values for the form * @param {Boolean} ignoreDefaultValues Ignore default values defined in the *  schema when set. */formNode.prototype.computeInitialValues = function (values, ignoreDefaultValues) {  var self = this;  var node = null;  var nbChildren = 1;  var i = 0;  var formData = this.ownerTree.formDesc.tpldata || {};  // Propagate the array path from the parent node  // (adding the position of the child for nodes that are direct  // children of array-like nodes)  if (this.parentNode) {    this.arrayPath = _.clone(this.parentNode.arrayPath);    if (this.parentNode.view && this.parentNode.view.array) {      this.arrayPath.push(this.childPos);    }  }  else {    this.arrayPath = [];  }  // Prepare special data param "idx" for templated values  // (is is the index of the child in its wrapping array, starting  // at 1 since that's more human-friendly than a zero-based index)  formData.idx = (this.arrayPath.length > 0) ?    this.arrayPath[this.arrayPath.length-1] + 1 :    this.childPos + 1;  // Prepare special data param "value" for templated values  formData.value = '';  // Prepare special function to compute the value of another field  formData.getValue = function (key) {    if (!values) {      return '';    }    var returnValue = values;    var listKey = key.split('[].');    var i;    for (i = 0; i < listKey.length - 1; i++) {      returnValue = returnValue[listKey[i]][self.arrayPath[i]];    }    return returnValue[listKey[i]];  };  if (this.formElement) {    // Compute the ID of the field (if needed)    if (this.formElement.id) {      this.id = applyArrayPath(this.formElement.id, this.arrayPath);    }    else if (this.view && this.view.array) {      this.id = escapeSelector(this.ownerTree.formDesc.prefix) +        '-elt-counter-' + _.uniqueId();    }    else if (this.parentNode && this.parentNode.view &&      this.parentNode.view.array) {      // Array items need an array to associate the right DOM element      // to the form node when the parent is rendered.      this.id = escapeSelector(this.ownerTree.formDesc.prefix) +        '-elt-counter-' + _.uniqueId();    }    else if ((this.formElement.type === 'button') ||      (this.formElement.type === 'selectfieldset') ||      (this.formElement.type === 'question') ||      (this.formElement.type === 'buttonquestion')) {      // Buttons do need an id for "onClick" purpose      this.id = escapeSelector(this.ownerTree.formDesc.prefix) +        '-elt-counter-' + _.uniqueId();    }    // Compute the actual key (the form element's key is index-free,    // i.e. it looks like foo[].bar.baz[].truc, so we need to apply    // the array path of the node to get foo[4].bar.baz[2].truc)    if (this.formElement.key) {      this.key = applyArrayPath(this.formElement.key, this.arrayPath);      this.keydash = slugify(this.key.replace(/\./g, '---'));    }    // Same idea for the field's name    this.name = applyArrayPath(this.formElement.name, this.arrayPath);    // Consider that label values are template values and apply the    // form's data appropriately (note we also apply the array path    // although that probably doesn't make much sense for labels...)    _.each([      'title',      'legend',      'description',      'append',      'prepend',      'inlinetitle',      'helpvalue',      'value',      'disabled',      'placeholder',      'readOnly'    ], function (prop) {      if (_.isString(this.formElement[prop])) {        if (this.formElement[prop].indexOf('{{values.') !== -1) {          // This label wants to use the value of another input field.          // Convert that construct into {{jsonform.getValue(key)}} for          // Underscore to call the appropriate function of formData          // when template gets called (note calling a function is not          // exactly Mustache-friendly but is supported by Underscore).          this[prop] = this.formElement[prop].replace(            /\{\{values\.([^\}]+)\}\}/g,            '{{getValue("$1")}}');        }        else {          // Note applying the array path probably doesn't make any sense,          // but some geek might want to have a label "foo[].bar[].baz",          // with the [] replaced by the appropriate array path.          this[prop] = applyArrayPath(this.formElement[prop], this.arrayPath);        }        if (this[prop]) {          this[prop] = _.template(this[prop], valueTemplateSettings)(formData);        }      }      else {        this[prop] = this.formElement[prop];      }    }, this);    // Apply templating to options created with "titleMap" as well    if (this.formElement.options) {      this.options = _.map(this.formElement.options, function (option) {        var title = null;        if (_.isObject(option) && option.title) {          // See a few lines above for more details about templating          // preparation here.          if (option.title.indexOf('{{values.') !== -1) {            title = option.title.replace(              /\{\{values\.([^\}]+)\}\}/g,              '{{getValue("$1")}}');          }          else {            title = applyArrayPath(option.title, self.arrayPath);          }          return _.extend({}, option, {            value: (isSet(option.value) ? option.value : ''),            title: _.template(title, valueTemplateSettings)(formData)          });        }        else {          return option;        }      });    }  }  if (this.view && this.view.inputfield && this.schemaElement) {    // Case 1: simple input field    if (values) {      // Form has already been submitted, use former value if defined.      // Note we won't set the field to its default value otherwise      // (since the user has already rejected it)      if (isSet(jsonform.util.getObjKey(values, this.key))) {        this.value = jsonform.util.getObjKey(values, this.key);      }    }    else if (!ignoreDefaultValues) {      // No previously submitted form result, use default value      // defined in the schema if it's available and not already      // defined in the form element      if (!isSet(this.value) && isSet(this.schemaElement['default'])) {        this.value = this.schemaElement['default'];        if (_.isString(this.value)) {          if (this.value.indexOf('{{values.') !== -1) {            // This label wants to use the value of another input field.            // Convert that construct into {{jsonform.getValue(key)}} for            // Underscore to call the appropriate function of formData            // when template gets called (note calling a function is not            // exactly Mustache-friendly but is supported by Underscore).            this.value = this.value.replace(              /\{\{values\.([^\}]+)\}\}/g,              '{{getValue("$1")}}');          }          else {            // Note applying the array path probably doesn't make any sense,            // but some geek might want to have a label "foo[].bar[].baz",            // with the [] replaced by the appropriate array path.            this.value = applyArrayPath(this.value, this.arrayPath);          }          if (this.value) {            this.value = _.template(this.value, valueTemplateSettings)(formData);          }        }        this.defaultValue = true;      }    }  }  else if (this.view && this.view.array) {    // Case 2: array-like node    nbChildren = 0;    if (values) {      nbChildren = this.getPreviousNumberOfItems(values, this.arrayPath);    }    // TODO: use default values at the array level when form has not been    // submitted before. Note it's not that easy because each value may    // be a complex structure that needs to be pushed down the subtree.    // The easiest way is probably to generate a "values" object and    // compute initial values from that object    /*    else if (this.schemaElement['default']) {      nbChildren = this.schemaElement['default'].length;    }    */    else if (nbChildren === 0) {      // If form has already been submitted with no children, the array      // needs to be rendered without children. If there are no previously      // submitted values, the array gets rendered with one empty item as      // it's more natural from a user experience perspective. That item can      // be removed with a click on the "-" button.      nbChildren = 1;    }    for (i = 0; i < nbChildren; i++) {      this.appendChild(this.childTemplate.clone());    }  }  // Case 3 and in any case: recurse through the list of children  _.each(this.children, function (child) {    child.computeInitialValues(values, ignoreDefaultValues);  });  // If the node's value is to be used as legend for its "container"  // (typically the array the node belongs to), ensure that the container  // has a direct link to the node for the corresponding tab.  if (this.formElement && this.formElement.valueInLegend) {    node = this;    while (node) {      if (node.parentNode &&        node.parentNode.view &&        node.parentNode.view.array) {        node.legendChild = this;        if (node.formElement && node.formElement.legend) {          node.legend = applyArrayPath(node.formElement.legend, node.arrayPath);          formData.idx = (node.arrayPath.length > 0) ?            node.arrayPath[node.arrayPath.length-1] + 1 :            node.childPos + 1;          formData.value = isSet(this.value) ? this.value : '';          node.legend = _.template(node.legend, valueTemplateSettings)(formData);          break;        }      }      node = node.parentNode;    }  }};/** * Returns the number of items that the array node should have based on * previously submitted values. * * The whole difficulty is that values may be hidden deep in the subtree * of the node and may actually target different arrays in the JSON schema. * * @function * @param {Object} values Previously submitted values * @param {Array(Number)} arrayPath the array path we're interested in * @return {Number} The number of items in the array */formNode.prototype.getPreviousNumberOfItems = function (values, arrayPath) {  var key = null;  var arrayValue = null;  var childNumbers = null;  var idx = 0;  if (!values) {    // No previously submitted values, no need to go any further    return 0;  }  if (this.view.inputfield && this.schemaElement) {    // Case 1: node is a simple input field that links to a key in the schema.    // The schema key looks typically like:    //  foo.bar[].baz.toto[].truc[].bidule    // The goal is to apply the array path and truncate the key to the last    // array we're interested in, e.g. with an arrayPath [4, 2]:    //  foo.bar[4].baz.toto[2]    key = truncateToArrayDepth(this.formElement.key, arrayPath.length);    key = applyArrayPath(key, arrayPath);    arrayValue = jsonform.util.getObjKey(values, key);    if (!arrayValue) {      // No key? That means this field had been left empty      // in previous submit      return 0;    }    childNumbers = _.map(this.children, function (child) {      return child.getPreviousNumberOfItems(values, arrayPath);    });    return _.max([_.max(childNumbers) || 0, arrayValue.length]);  }  else if (this.view.array) {    // Case 2: node is an array-like node, look for input fields    // in its child template    return this.childTemplate.getPreviousNumberOfItems(values, arrayPath);  }  else {    // Case 3: node is a leaf or a container,    // recurse through the list of children and return the maximum    // number of items found in each subtree    childNumbers = _.map(this.children, function (child) {      return child.getPreviousNumberOfItems(values, arrayPath);    });    return _.max(childNumbers) || 0;  }};/** * Returns the structured object that corresponds to the form values entered * by the user for the node's subtree. * * The returned object follows the structure of the JSON schema that gave * birth to the form. * * Obviously, the node must have been rendered before that function may * be called. * * @function * @param {Array(Number)} updateArrayPath Array path to use to pretend that *  the entered values were actually entered for another item in an array *  (this is used to move values around when an item is inserted/removed/moved *  in an array) * @return {Object} The object that follows the data schema and matches the *  values entered by the user. */formNode.prototype.getFormValues = function (updateArrayPath) {  // The values object that will be returned  var values = {};  if (!this.el) {    throw new Error('formNode.getFormValues can only be called on nodes that are associated with a DOM element in the tree');  }  // Form fields values  var formArray = $(':input', this.el).serializeArray();  // Set values to false for unset checkboxes and radio buttons  // because serializeArray() ignores them  formArray = formArray.concat(    $(':input[type=checkbox]:not(:disabled):not(:checked)', this.el).map( function() {      return {"name": this.name, "value": this.checked}    }).get()  );  if (updateArrayPath) {    _.each(formArray, function (param) {      param.name = applyArrayPath(param.name, updateArrayPath);    });  }  // The underlying data schema  var formSchema = this.ownerTree.formDesc.schema;  for (var i = 0; i < formArray.length; i++) {    // Retrieve the key definition from the data schema    var name = formArray[i].name;    var eltSchema = getSchemaKey(formSchema.properties, name);    var arrayMatch = null;    var cval = null;    // Skip the input field if it's not part of the schema    if (!eltSchema) continue;    // Handle multiple checkboxes separately as the idea is to generate    // an array that contains the list of enumeration items that the user    // selected.    if (eltSchema._jsonform_checkboxes_as_array) {      arrayMatch = name.match(/\[([0-9]*)\]$/);      if (arrayMatch) {        name = name.replace(/\[([0-9]*)\]$/, '');        cval = jsonform.util.getObjKey(values, name) || [];        if (formArray[i].value === '1') {          // Value selected, push the corresponding enumeration item          // to the data result          cval.push(eltSchema['enum'][parseInt(arrayMatch[1],10)]);        }        jsonform.util.setObjKey(values, name, cval);        continue;      }    }    // Type casting    if (eltSchema.type === 'boolean') {      if (formArray[i].value === '0') {        formArray[i].value = false;      } else {        formArray[i].value = !!formArray[i].value;      }    }    if ((eltSchema.type === 'number') ||      (eltSchema.type === 'integer')) {      if (_.isString(formArray[i].value)) {        if (!formArray[i].value.length) {          formArray[i].value = null;        } else if (!isNaN(Number(formArray[i].value))) {          formArray[i].value = Number(formArray[i].value);        }      }    }    if ((eltSchema.type === 'string') &&      (formArray[i].value === '') &&      !eltSchema._jsonform_allowEmpty) {      formArray[i].value=null;    }    if ((eltSchema.type === 'object') &&      _.isString(formArray[i].value) &&      (formArray[i].value.substring(0,1) === '{')) {      try {        formArray[i].value = JSON.parse(formArray[i].value);      } catch (e) {        formArray[i].value = {};      }    }    //TODO is this due to a serialization bug?    if ((eltSchema.type === 'object') &&      (formArray[i].value === 'null' || formArray[i].value === '')) {      formArray[i].value = null;    }    if (formArray[i].name && (formArray[i].value !== null)) {      jsonform.util.setObjKey(values, formArray[i].name, formArray[i].value);    }  }  // console.log("Form value",values);  return values;};/** * Renders the node. * * Rendering is done in three steps: HTML generation, DOM element creation * and insertion, and an enhance step to bind event handlers. * * @function * @param {Node} el The DOM element where the node is to be rendered. The *  node is inserted at the right position based on its "childPos" property. */formNode.prototype.render = function (el) {  var html = this.generate();  this.setContent(html, el);  this.enhance();};/** * Inserts/Updates the HTML content of the node in the DOM. * * If the HTML is an update, the new HTML content replaces the old one. * The new HTML content is not moved around in the DOM in particular. * * The HTML is inserted at the right position in its parent's DOM subtree * otherwise (well, provided there are enough children, but that should always * be the case). * * @function * @param {string} html The HTML content to render * @param {Node} parentEl The DOM element that is to contain the DOM node. *  This parameter is optional (the node's parent is used otherwise) and *  is ignored if the node to render is already in the DOM tree. */formNode.prototype.setContent = function (html, parentEl) {  var node = $(html);  var parentNode = parentEl ||    (this.parentNode ? this.parentNode.el : this.ownerTree.domRoot);  var nextSibling = null;  if (this.el) {    // Replace the contents of the DOM element if the node is already in the tree    $(this.el).replaceWith(node);  }  else {    // Insert the node in the DOM if it's not already there    nextSibling = $(parentNode).children().get(this.childPos);    if (nextSibling) {      $(nextSibling).before(node);    }    else {      $(parentNode).append(node);    }  }  // Save the link between the form node and the generated HTML  this.el = node;  // Update the node's subtree, extracting DOM elements that match the nodes  // from the generated HTML  this.updateElement(this.el);};/** * Updates the DOM element associated with the node. * * Only nodes that have ID are directly associated with a DOM element. * * @function */formNode.prototype.updateElement = function (domNode) {  if (this.id) {    this.el = $('#' + escapeSelector(this.id), domNode).get(0);    if (this.view && this.view.getElement) {      this.el = this.view.getElement(this.el);    }    if ((this.fieldtemplate !== false) &&      this.view && this.view.fieldtemplate) {      // The field template wraps the element two or three level deep      // in the DOM tree, depending on whether there is anything prepended      // or appended to the input field      this.el = $(this.el).parent().parent();      if (this.prepend || this.prepend) {        this.el = this.el.parent();      }      this.el = this.el.get(0);    }    if (this.parentNode && this.parentNode.view &&      this.parentNode.view.childTemplate) {      // TODO: the child template may introduce more than one level,      // so the number of levels introduced should rather be exposed      // somehow in jsonform.fieldtemplate.      this.el = $(this.el).parent().get(0);    }  }  _.each(this.children, function (child) {    child.updateElement(this.el || domNode);  });};/** * Generates the view's HTML content for the underlying model. * * @function */formNode.prototype.generate = function () {  var data = {    id: this.id,    keydash: this.keydash,    elt: this.formElement,    schema: this.schemaElement,    node: this,    value: isSet(this.value) ? this.value : '',    escape: escapeHTML  };  var template = null;  var html = '';  // Complete the data context if needed  if (this.ownerTree.formDesc.onBeforeRender) {    this.ownerTree.formDesc.onBeforeRender(data, this);  }  if (this.view.onBeforeRender) {    this.view.onBeforeRender(data, this);  }  // Use the template that 'onBeforeRender' may have set,  // falling back to that of the form element otherwise  if (this.template) {    template = this.template;  }  else if (this.formElement && this.formElement.template) {    template = this.formElement.template;  }  else {    template = this.view.template;  }  // Wrap the view template in the generic field template  // (note the strict equality to 'false', needed as we fallback  // to the view's setting otherwise)  if ((this.fieldtemplate !== false) &&    (this.fieldtemplate || this.view.fieldtemplate)) {    template = jsonform.fieldTemplate(template);  }  // Wrap the content in the child template of its parent if necessary.  if (this.parentNode && this.parentNode.view &&    this.parentNode.view.childTemplate) {    template = this.parentNode.view.childTemplate(template);  }  // Prepare the HTML of the children  var childrenhtml = '';  _.each(this.children, function (child) {    childrenhtml += child.generate();  });  data.children = childrenhtml;  data.fieldHtmlClass = '';  if (this.ownerTree &&      this.ownerTree.formDesc &&      this.ownerTree.formDesc.params &&      this.ownerTree.formDesc.params.fieldHtmlClass) {    data.fieldHtmlClass = this.ownerTree.formDesc.params.fieldHtmlClass;  }  if (this.formElement &&      (typeof this.formElement.fieldHtmlClass !== 'undefined')) {    data.fieldHtmlClass = this.formElement.fieldHtmlClass;  }  // Apply the HTML template  html = _.template(template, fieldTemplateSettings)(data);  return html;};/** * Enhances the view with additional logic, binding event handlers * in particular. * * The function also runs the "insert" event handler of the view and * form element if they exist (starting with that of the view) * * @function */formNode.prototype.enhance = function () {  var node = this;  var handlers = null;  var handler = null;  var formData = _.clone(this.ownerTree.formDesc.tpldata) || {};  if (this.formElement) {    // Check the view associated with the node as it may define an "onInsert"    // event handler to be run right away    if (this.view.onInsert) {      this.view.onInsert({ target: $(this.el) }, this);    }    handlers = this.handlers || this.formElement.handlers;    // Trigger the "insert" event handler    handler = this.onInsert || this.formElement.onInsert;    if (handler) {      handler({ target: $(this.el) }, this);    }    if (handlers) {      _.each(handlers, function (handler, onevent) {        if (onevent === 'insert') {          handler({ target: $(this.el) }, this);        }      }, this);    }    // No way to register event handlers if the DOM element is unknown    // TODO: find some way to register event handlers even when this.el is not set.    if (this.el) {      // Register specific event handlers      // TODO: Add support for other event handlers      if (this.onChange)        $(this.el).bind('change', function(evt) { node.onChange(evt, node); });      if (this.view.onChange)        $(this.el).bind('change', function(evt) { node.view.onChange(evt, node); });      if (this.formElement.onChange)        $(this.el).bind('change', function(evt) { node.formElement.onChange(evt, node); });      if (this.onClick)        $(this.el).bind('click', function(evt) { node.onClick(evt, node); });      if (this.view.onClick)        $(this.el).bind('click', function(evt) { node.view.onClick(evt, node); });      if (this.formElement.onClick)        $(this.el).bind('click', function(evt) { node.formElement.onClick(evt, node); });      if (this.onKeyUp)        $(this.el).bind('keyup', function(evt) { node.onKeyUp(evt, node); });      if (this.view.onKeyUp)        $(this.el).bind('keyup', function(evt) { node.view.onKeyUp(evt, node); });      if (this.formElement.onKeyUp)        $(this.el).bind('keyup', function(evt) { node.formElement.onKeyUp(evt, node); });      if (handlers) {        _.each(handlers, function (handler, onevent) {          if (onevent !== 'insert') {            $(this.el).bind(onevent, function(evt) { handler(evt, node); });          }        }, this);      }    }    // Auto-update legend based on the input field that's associated with it    if (this.legendChild && this.legendChild.formElement) {      var onChangeHandler = function (evt) {        if (node.formElement && node.formElement.legend && node.parentNode) {          node.legend = applyArrayPath(node.formElement.legend, node.arrayPath);          formData.idx = (node.arrayPath.length > 0) ?              node.arrayPath[node.arrayPath.length - 1] + 1 :              node.childPos + 1;          formData.value = $(evt.target).val();          node.legend = _.template(node.legend, valueTemplateSettings)(formData);          $(node.parentNode.el).trigger('legendUpdated');        }      };      $(this.legendChild.el).bind('change', onChangeHandler);      $(this.legendChild.el).bind('keyup', onChangeHandler);    }  }  // Recurse down the tree to enhance children  _.each(this.children, function (child) {    child.enhance();  });};/** * Inserts an item in the array at the requested position and renders the item. * * @function * @param {Number} idx Insertion index */formNode.prototype.insertArrayItem = function (idx, domElement) {  var i = 0;  // Insert element at the end of the array if index is not given  if (idx === undefined) {    idx = this.children.length;  }  // Create the additional array item at the end of the list,  // using the item template created when tree was initialized  // (the call to resetValues ensures that 'arrayPath' is correctly set)  var child = this.childTemplate.clone();  this.appendChild(child);  child.resetValues();  // To create a blank array item at the requested position,  // shift values down starting at the requested position  // one to insert (note we start with the end of the array on purpose)  for (i = this.children.length-2; i >= idx; i--) {    this.children[i].moveValuesTo(this.children[i+1]);  }  // Initialize the blank node we've created with default values  this.children[idx].resetValues();  this.children[idx].computeInitialValues();  // Re-render all children that have changed  for (i = idx; i < this.children.length; i++) {    this.children[i].render(domElement);  }};/** * Remove an item from an array * * @function * @param {Number} idx The index number of the item to remove */formNode.prototype.deleteArrayItem = function (idx) {  var i = 0;  var child = null;  // Delete last item if no index is given  if (idx === undefined) {    idx = this.children.length - 1;  }  // Move values up in the array  for (i = idx; i < this.children.length-1; i++) {    this.children[i+1].moveValuesTo(this.children[i]);    this.children[i].render();  }  // Remove the last array item from the DOM tree and from the form tree  this.removeChild();};/** * Returns the minimum/maximum number of items that an array field * is allowed to have according to the schema definition of the fields * it contains. * * The function parses the schema definitions of the array items that * compose the current "array" node and returns the minimum value of * "maxItems" it encounters as the maximum number of items, and the * maximum value of "minItems" as the minimum number of items. * * The function reports a -1 for either of the boundaries if the schema * does not put any constraint on the number of elements the current * array may have of if the current node is not an array. * * Note that array boundaries should be defined in the JSON Schema using * "minItems" and "maxItems". The code also supports "minLength" and * "maxLength" as a fallback, mostly because it used to by mistake (see #22) * and because other people could make the same mistake. * * @function * @return {Object} An object with properties "minItems" and "maxItems" *  that reports the corresponding number of items that the array may *  have (value is -1 when there is no constraint for that boundary) */formNode.prototype.getArrayBoundaries = function () {  var boundaries = {    minItems: -1,    maxItems: -1  };  if (!this.view || !this.view.array) return boundaries;  var getNodeBoundaries = function (node, initialNode) {    var schemaKey = null;    var arrayKey = null;    var boundaries = {      minItems: -1,      maxItems: -1    };    initialNode = initialNode || node;    if (node.view && node.view.array && (node !== initialNode)) {      // New array level not linked to an array in the schema,      // so no size constraints      return boundaries;    }    if (node.key) {      // Note the conversion to target the actual array definition in the      // schema where minItems/maxItems may be defined. If we're still looking      // at the initial node, the goal is to convert from:      //  foo[0].bar[3].baz to foo[].bar[].baz      // If we're not looking at the initial node, the goal is to look at the      // closest array parent:      //  foo[0].bar[3].baz to foo[].bar      arrayKey = node.key.replace(/\[[0-9]+\]/g, '[]');      if (node !== initialNode) {        arrayKey = arrayKey.replace(/\[\][^\[\]]*$/, '');      }      schemaKey = getSchemaKey(        node.ownerTree.formDesc.schema.properties,        arrayKey      );      if (!schemaKey) return boundaries;      return {        minItems: schemaKey.minItems || schemaKey.minLength || -1,        maxItems: schemaKey.maxItems || schemaKey.maxLength || -1      };    }    else {      _.each(node.children, function (child) {        var subBoundaries = getNodeBoundaries(child, initialNode);        if (subBoundaries.minItems !== -1) {          if (boundaries.minItems !== -1) {            boundaries.minItems = Math.max(              boundaries.minItems,              subBoundaries.minItems            );          }          else {            boundaries.minItems = subBoundaries.minItems;          }        }        if (subBoundaries.maxItems !== -1) {          if (boundaries.maxItems !== -1) {            boundaries.maxItems = Math.min(              boundaries.maxItems,              subBoundaries.maxItems            );          }          else {            boundaries.maxItems = subBoundaries.maxItems;          }        }      });    }    return boundaries;  };  return getNodeBoundaries(this);};/** * Form tree class. * * Holds the internal representation of the form. * The tree is always in sync with the rendered form, this allows to parse * it easily. * * @class */var formTree = function () {  this.eventhandlers = [];  this.root = null;  this.formDesc = null;};/** * Initializes the form tree structure from the JSONForm object * * This function is the main entry point of the JSONForm library. * * Initialization steps: * 1. the internal tree structure that matches the JSONForm object *  gets created (call to buildTree) * 2. initial values are computed from previously submitted values *  or from the default values defined in the JSON schema. * * When the function returns, the tree is ready to be rendered through * a call to "render". * * @function */formTree.prototype.initialize = function (formDesc) {  formDesc = formDesc || {};  // Keep a pointer to the initial JSONForm  // (note clone returns a shallow copy, only first-level is cloned)  this.formDesc = _.clone(formDesc);  // Compute form prefix if no prefix is given.  this.formDesc.prefix = this.formDesc.prefix ||    'jsonform-' + _.uniqueId();  // JSON schema shorthand  if (this.formDesc.schema && !this.formDesc.schema.properties) {    this.formDesc.schema = {      properties: this.formDesc.schema    };  }  // Ensure layout is set  this.formDesc.form = this.formDesc.form || [    '*',    {      type: 'actions',      items: [        {          type: 'submit',          value: 'Submit'        }      ]    }  ];  this.formDesc.form = (_.isArray(this.formDesc.form) ?    this.formDesc.form :    [this.formDesc.form]);  this.formDesc.params = this.formDesc.params || {};  // Create the root of the tree  this.root = new formNode();  this.root.ownerTree = this;  this.root.view = jsonform.elementTypes['root'];  // Generate the tree from the form description  this.buildTree();  // Compute the values associated with each node  // (for arrays, the computation actually creates the form nodes)  this.computeInitialValues();};/** * Constructs the tree from the form description. * * The function must be called once when the tree is first created. * * @function */formTree.prototype.buildTree = function () {  // Parse and generate the form structure based on the elements encountered:  // - '*' means "generate all possible fields using default layout"  // - a key reference to target a specific data element  // - a more complex object to generate specific form sections  _.each(this.formDesc.form, function (formElement) {    if (formElement === '*') {      _.each(this.formDesc.schema.properties, function (element, key) {        this.root.appendChild(this.buildFromLayout({          key: key        }));      }, this);    }    else {      if (_.isString(formElement)) {        formElement = {          key: formElement        };      }      this.root.appendChild(this.buildFromLayout(formElement));    }  }, this);};/** * Builds the internal form tree representation from the requested layout. * * The function is recursive, generating the node children as necessary. * The function extracts the values from the previously submitted values * (this.formDesc.value) or from default values defined in the schema. * * @function * @param {Object} formElement JSONForm element to render * @param {Object} context The parsing context (the array depth in particular) * @return {Object} The node that matches the element. */formTree.prototype.buildFromLayout = function (formElement, context) {  var schemaElement = null;  var node = new formNode();  var view = null;  var key = null;  // The form element parameter directly comes from the initial  // JSONForm object. We'll make a shallow copy of it and of its children  // not to pollute the original object.  // (note JSON.parse(JSON.stringify()) cannot be used since there may be  // event handlers in there!)  formElement = _.clone(formElement);  if (formElement.items) {    if (_.isArray(formElement.items)) {      formElement.items = _.map(formElement.items, _.clone);    }    else {      formElement.items = [ _.clone(formElement.items) ];    }  }  if (formElement.key) {    // The form element is directly linked to an element in the JSON    // schema. The properties of the form element override those of the    // element in the JSON schema. Properties from the JSON schema complete    // those of the form element otherwise.    // Retrieve the element from the JSON schema    schemaElement = getSchemaKey(      this.formDesc.schema.properties,      formElement.key);    if (!schemaElement) {      // The JSON Form is invalid!      throw new Error('The JSONForm object references the schema key "' +        formElement.key + '" but that key does not exist in the JSON schema');    }    // Schema element has just been found, let's trigger the    // "onElementSchema" event    // (tidoust: not sure what the use case for this is, keeping the    // code for backward compatibility)    if (this.formDesc.onElementSchema) {      this.formDesc.onElementSchema(formElement, schemaElement);    }    formElement.name =      formElement.name ||      formElement.key;    formElement.title =      formElement.title ||      schemaElement.title;    formElement.description =      formElement.description ||      schemaElement.description;    formElement.readOnly =      formElement.readOnly ||      schemaElement.readOnly ||      formElement.readonly ||      schemaElement.readonly;    // Compute the ID of the input field    if (!formElement.id) {      formElement.id = escapeSelector(this.formDesc.prefix) +        '-elt-' + slugify(formElement.key);    }    // Should empty strings be included in the final value?    // TODO: it's rather unclean to pass it through the schema.    if (formElement.allowEmpty) {      schemaElement._jsonform_allowEmpty = true;    }    // If the form element does not define its type, use the type of    // the schema element.    if (!formElement.type) {      // If schema type is an array containing only a type and "null",      // remove null and make the element non-required      if (_.isArray(schemaElement.type)) {        if (_.contains(schemaElement.type, "null")) {          schemaElement.type = _.without(schemaElement.type, "null");          schemaElement.required = false;        }        if (schemaElement.type.length > 1) {          throw new Error("Cannot process schema element with multiple types.");        }        schemaElement.type = _.first(schemaElement.type);      }      if ((schemaElement.type === 'string') &&        (schemaElement.format === 'color')) {        formElement.type = 'color';      } else if ((schemaElement.type === 'number' ||        schemaElement.type === 'integer') &&        !schemaElement['enum']) {       formElement.type = 'number';       if (schemaElement.type === 'number') schemaElement.step = 'any';      } else if ((schemaElement.type === 'string' ||        schemaElement.type === 'any') &&        !schemaElement['enum']) {        formElement.type = 'text';      } else if (schemaElement.type === 'boolean') {        formElement.type = 'checkbox';      } else if (schemaElement.type === 'object') {        if (schemaElement.properties) {          formElement.type = 'fieldset';        } else {          formElement.type = 'textarea';        }      } else if (!_.isUndefined(schemaElement['enum'])) {        formElement.type = 'select';      } else {        formElement.type = schemaElement.type;      }    }    // Unless overridden in the definition of the form element (or unless    // there's a titleMap defined), use the enumeration list defined in    // the schema    if (!formElement.options && schemaElement['enum']) {      if (formElement.titleMap) {        formElement.options = _.map(schemaElement['enum'], function (value) {          return {            value: value,            title: hasOwnProperty(formElement.titleMap, value) ? formElement.titleMap[value] : value          };        });      }      else {        formElement.options = schemaElement['enum'];      }    }    // Flag a list of checkboxes with multiple choices    if ((formElement.type === 'checkboxes') && schemaElement.items) {      var itemsEnum = schemaElement.items['enum'];      if (itemsEnum) {        schemaElement.items._jsonform_checkboxes_as_array = true;      }      if (!itemsEnum && schemaElement.items[0]) {        itemsEnum = schemaElement.items[0]['enum'];        if (itemsEnum) {          schemaElement.items[0]._jsonform_checkboxes_as_array = true;        }      }    }    // If the form element targets an "object" in the JSON schema,    // we need to recurse through the list of children to create an    // input field per child property of the object in the JSON schema    if (schemaElement.type === 'object') {      _.each(schemaElement.properties, function (prop, propName) {        node.appendChild(this.buildFromLayout({          key: formElement.key + '.' + propName        }));      }, this);    }  }  if (!formElement.type) {    formElement.type = 'none';  }  view = jsonform.elementTypes[formElement.type];  if (!view) {    throw new Error('The JSONForm contains an element whose type is unknown: "' +      formElement.type + '"');  }  if (schemaElement) {    // The form element is linked to an element in the schema.    // Let's make sure the types are compatible.    // In particular, the element must not be a "container"    // (or must be an "object" or "array" container)    if (!view.inputfield && !view.array &&      (formElement.type !== 'selectfieldset') &&      (schemaElement.type !== 'object')) {      throw new Error('The JSONForm contains an element that links to an ' +        'element in the JSON schema (key: "' + formElement.key + '") ' +        'and that should not based on its type ("' + formElement.type + '")');    }  }  else {    // The form element is not linked to an element in the schema.    // This means the form element must be a "container" element,    // and must not define an input field.    if (view.inputfield && (formElement.type !== 'selectfieldset')) {      throw new Error('The JSONForm defines an element of type ' +        '"' + formElement.type + '" ' +        'but no "key" property to link the input field to the JSON schema');    }  }  // A few characters need to be escaped to use the ID as jQuery selector  formElement.iddot = escapeSelector(formElement.id || '');  // Initialize the form node from the form element and schema element  node.formElement = formElement;  node.schemaElement = schemaElement;  node.view = view;  node.ownerTree = this;  // Set event handlers  if (!formElement.handlers) {    formElement.handlers = {};  }  // Parse children recursively  if (node.view.array) {    // The form element is an array. The number of items in an array    // is by definition dynamic, up to the form user (through "Add more",    // "Delete" commands). The positions of the items in the array may    // also change over time (through "Move up", "Move down" commands).    //    // The form node stores a "template" node that serves as basis for    // the creation of an item in the array.    //    // Array items may be complex forms themselves, allowing for nesting.    //    // The initial values set the initial number of items in the array.    // Note a form element contains at least one item when it is rendered.    if (formElement.items) {      key = formElement.items[0] || formElement.items;    }    else {      key = formElement.key + '[]';    }    if (_.isString(key)) {      key = { key: key };    }    node.setChildTemplate(this.buildFromLayout(key));  }  else if (formElement.items) {    // The form element defines children elements    _.each(formElement.items, function (item) {      if (_.isString(item)) {        item = { key: item };      }      node.appendChild(this.buildFromLayout(item));    }, this);  }  return node;};/** * Computes the values associated with each input field in the tree based * on previously submitted values or default values in the JSON schema. * * For arrays, the function actually creates and inserts additional * nodes in the tree based on previously submitted values (also ensuring * that the array has at least one item). * * The function sets the array path on all nodes. * It should be called once in the lifetime of a form tree right after * the tree structure has been created. * * @function */formTree.prototype.computeInitialValues = function () {  this.root.computeInitialValues(this.formDesc.value);};/** * Renders the form tree * * @function * @param {Node} domRoot The "form" element in the DOM tree that serves as *  root for the form */formTree.prototype.render = function (domRoot) {  if (!domRoot) return;  this.domRoot = domRoot;  this.root.render();  // If the schema defines required fields, flag the form with the  // "jsonform-hasrequired" class for styling purpose  // (typically so that users may display a legend)  if (this.hasRequiredField()) {    $(domRoot).addClass('jsonform-hasrequired');  }};/** * Walks down the element tree with a callback * * @function * @param {Function} callback The callback to call on each element */formTree.prototype.forEachElement = function (callback) {  var f = function(root) {    for (var i=0;i<root.children.length;i++) {      callback(root.children[i]);      f(root.children[i]);    }  };  f(this.root);};formTree.prototype.validate = function(noErrorDisplay) {  var values = jsonform.getFormValue(this.domRoot);  var errors = false;  var options = this.formDesc;  if (options.validate!==false) {    var validator = false;    if (typeof options.validate!="object") {      if (global.JSONFormValidator) {        validator = global.JSONFormValidator.createEnvironment("json-schema-draft-03");      }    } else {      validator = options.validate;    }    if (validator) {      var v = validator.validate(values, this.formDesc.schema);      $(this.domRoot).jsonFormErrors(false,options);      if (v.errors.length) {        if (!errors) errors = [];        errors = errors.concat(v.errors);      }    }  }  if (errors && !noErrorDisplay) {    if (options.displayErrors) {      options.displayErrors(errors,this.domRoot);    } else {      $(this.domRoot).jsonFormErrors(errors,options);    }  }  return {"errors":errors}}formTree.prototype.submit = function(evt) {  var stopEvent = function() {    if (evt) {      evt.preventDefault();      evt.stopPropagation();    }    return false;  };  var values = jsonform.getFormValue(this.domRoot);  var options = this.formDesc;  var brk=false;  this.forEachElement(function(elt) {    if (brk) return;    if (elt.view.onSubmit) {      brk = !elt.view.onSubmit(evt, elt); //may be called multiple times!!    }  });  if (brk) return stopEvent();  var validated = this.validate();  if (options.onSubmit && !options.onSubmit(validated.errors,values)) {    return stopEvent();  }  if (validated.errors) return stopEvent();  if (options.onSubmitValid && !options.onSubmitValid(values)) {    return stopEvent();  }  return false;};/** * Returns true if the form displays a "required" field. * * To keep things simple, the function parses the form's schema and returns * true as soon as it finds a "required" flag even though, in theory, that * schema key may not appear in the final form. * * Note that a "required" constraint on a boolean type is always enforced, * the code skips such definitions. * * @function * @return {boolean} True when the form has some required field, *  false otherwise. */formTree.prototype.hasRequiredField = function () {  var parseElement = function (element) {    if (!element) return null;    if (element.required && (element.type !== 'boolean')) {      return element;    }    var prop = _.find(element.properties, function (property) {      return parseElement(property);    });    if (prop) {      return prop;    }    if (element.items) {      if (_.isArray(element.items)) {        prop = _.find(element.items, function (item) {          return parseElement(item);        });      }      else {        prop = parseElement(element.items);      }      if (prop) {        return prop;      }    }  };  return parseElement(this.formDesc.schema);};/** * Returns the structured object that corresponds to the form values entered * by the use for the given form. * * The form must have been previously rendered through a call to jsonform. * * @function * @param {Node} The <form> tag in the DOM * @return {Object} The object that follows the data schema and matches the *  values entered by the user. */jsonform.getFormValue = function (formelt) {  var form = $(formelt).data('jsonform-tree');  if (!form) return null;  return form.root.getFormValues();};/** * Highlights errors reported by the JSON schema validator in the document. * * @function * @param {Object} errors List of errors reported by the JSON schema validator * @param {Object} options The JSON Form object that describes the form *  (unused for the time being, could be useful to store example values or *   specific error messages) */$.fn.jsonFormErrors = function(errors, options) {  $(".error", this).removeClass("error");  $(".warning", this).removeClass("warning");  $(".jsonform-errortext", this).hide();  if (!errors) return;  var errorSelectors = [];  for (var i = 0; i < errors.length; i++) {    // Compute the address of the input field in the form from the URI    // returned by the JSON schema validator.    // These URIs typically look like:    //  urn:uuid:cccc265e-ffdd-4e40-8c97-977f7a512853#/pictures/1/thumbnail    // What we need from that is the path in the value object:    //  pictures[1].thumbnail    // ... and the jQuery-friendly class selector of the input field:    //  .jsonform-error-pictures\[1\]---thumbnail    var key = errors[i].uri      .replace(/.*#\//, '')      .replace(/\//g, '.')      .replace(/\.([0-9]+)(?=\.|$)/g, '[$1]');    var errormarkerclass = ".jsonform-error-" +      escapeSelector(key.replace(/\./g,"---"));    errorSelectors.push(errormarkerclass);    var errorType = errors[i].type || "error";    $(errormarkerclass, this).addClass(errorType);    $(errormarkerclass + " .jsonform-errortext", this).html(errors[i].message).show();  }  // Look for the first error in the DOM and ensure the element  // is visible so that the user understands that something went wrong  errorSelectors = errorSelectors.join(',');  var firstError = $(errorSelectors).get(0);  if (firstError && firstError.scrollIntoView) {    firstError.scrollIntoView(true, {      behavior: 'smooth'    });  }};/** * Generates the HTML form from the given JSON Form object and renders the form. * * Main entry point of the library. Defined as a jQuery function that typically * needs to be applied to a <form> element in the document. * * The function handles the following properties for the JSON Form object it * receives as parameter: * - schema (required): The JSON Schema that describes the form to render * - form: The options form layout description, overrides default layout * - prefix: String to use to prefix computed IDs. Default is an empty string. *  Use this option if JSON Form is used multiple times in an application with *  schemas that have overlapping parameter names to avoid running into multiple *  IDs issues. Default value is "jsonform-[counter]". * - transloadit: Transloadit parameters when transloadit is used * - validate: Validates form against schema upon submission. Uses the value * of the "validate" property as validator if it is an object. * - displayErrors: Function to call with errors upon form submission. *  Default is to render the errors next to the input fields. * - submitEvent: Name of the form submission event to bind to. *  Default is "submit". Set this option to false to avoid event binding. * - onSubmit: Callback function to call when form is submitted * - onSubmitValid: Callback function to call when form is submitted without *  errors. * * @function * @param {Object} options The JSON Form object to use as basis for the form */$.fn.jsonForm = function(options) {  var formElt = this;  options = _.defaults({}, options, {submitEvent: 'submit'});  var form = new formTree();  form.initialize(options);  form.render(formElt.get(0));  // TODO: move that to formTree.render  if (options.transloadit) {    formElt.append('<input type="hidden" name="params" value=\'' +      escapeHTML(JSON.stringify(options.transloadit.params)) +      '\'>');  }  // Keep a direct pointer to the JSON schema for form submission purpose  formElt.data("jsonform-tree", form);  if (options.submitEvent) {    formElt.unbind((options.submitEvent)+'.jsonform');    formElt.bind((options.submitEvent)+'.jsonform', function(evt) {      form.submit(evt);    });  }  // Initialize tabs sections, if any  initializeTabs(formElt);  // Initialize expandable sections, if any  $('.expandable > div, .expandable > fieldset', formElt).hide();  formElt.on('click', '.expandable > legend', function () {    var parent = $(this).parent();    parent.toggleClass('expanded');    $('> div', parent).slideToggle(100);  });  return form;};/** * Retrieves the structured values object generated from the values * entered by the user and the data schema that gave birth to the form. * * Defined as a jQuery function that typically needs to be applied to * a <form> element whose content has previously been generated by a * call to "jsonForm". * * Unless explicitly disabled, the values are automatically validated * against the constraints expressed in the schema. * * @function * @return {Object} Structured values object that matches the user inputs *  and the data schema. */$.fn.jsonFormValue = function() {  return jsonform.getFormValue(this);};// Expose the getFormValue method to the global object// (other methods exposed as jQuery functions)global.JSONForm = global.JSONForm || {util:{}};global.JSONForm.getFormValue = jsonform.getFormValue;global.JSONForm.fieldTemplate = jsonform.fieldTemplate;global.JSONForm.fieldTypes = jsonform.elementTypes;global.JSONForm.getInitialValue = getInitialValue;global.JSONForm.util.getObjKey = jsonform.util.getObjKey;global.JSONForm.util.setObjKey = jsonform.util.setObjKey;})((typeof exports !== 'undefined'),  ((typeof exports !== 'undefined') ? exports : window),  ((typeof jQuery !== 'undefined') ? jQuery : { fn: {} }),  ((typeof _ !== 'undefined') ? _ : null),  JSON);},{"underscore":2}],2:[function(require,module,exports){(function (global){(function (){(function (global, factory) {  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :  typeof define === 'function' && define.amd ? define('underscore', factory) :  (global = global || self, (function () {    var current = global._;    var exports = global._ = factory();    exports.noConflict = function () { global._ = current; return exports; };  }()));}(this, (function () {  //     Underscore.js 1.12.0  //     https://underscorejs.org  //     (c) 2009-2020 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors  //     Underscore may be freely distributed under the MIT license.  // Current version.  var VERSION = '1.12.0';  // Establish the root object, `window` (`self`) in the browser, `global`  // on the server, or `this` in some virtual machines. We use `self`  // instead of `window` for `WebWorker` support.  var root = typeof self == 'object' && self.self === self && self ||            typeof global == 'object' && global.global === global && global ||            Function('return this')() ||            {};  // Save bytes in the minified (but not gzipped) version:  var ArrayProto = Array.prototype, ObjProto = Object.prototype;  var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null;  // Create quick reference variables for speed access to core prototypes.  var push = ArrayProto.push,      slice = ArrayProto.slice,      toString = ObjProto.toString,      hasOwnProperty = ObjProto.hasOwnProperty;  // Modern feature detection.  var supportsArrayBuffer = typeof ArrayBuffer !== 'undefined',      supportsDataView = typeof DataView !== 'undefined';  // All **ECMAScript 5+** native function implementations that we hope to use  // are declared here.  var nativeIsArray = Array.isArray,      nativeKeys = Object.keys,      nativeCreate = Object.create,      nativeIsView = supportsArrayBuffer && ArrayBuffer.isView;  // Create references to these builtin functions because we override them.  var _isNaN = isNaN,      _isFinite = isFinite;  // Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed.  var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString');  var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString',    'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];  // The largest integer that can be represented exactly.  var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;  // Some functions take a variable number of arguments, or a few expected  // arguments at the beginning and then a variable number of values to operate  // on. This helper accumulates all remaining arguments past the function’s  // argument length (or an explicit `startIndex`), into an array that becomes  // the last argument. Similar to ES6’s "rest parameter".  function restArguments(func, startIndex) {    startIndex = startIndex == null ? func.length - 1 : +startIndex;    return function() {      var length = Math.max(arguments.length - startIndex, 0),          rest = Array(length),          index = 0;      for (; index < length; index++) {        rest[index] = arguments[index + startIndex];      }      switch (startIndex) {        case 0: return func.call(this, rest);        case 1: return func.call(this, arguments[0], rest);        case 2: return func.call(this, arguments[0], arguments[1], rest);      }      var args = Array(startIndex + 1);      for (index = 0; index < startIndex; index++) {        args[index] = arguments[index];      }      args[startIndex] = rest;      return func.apply(this, args);    };  }  // Is a given variable an object?  function isObject(obj) {    var type = typeof obj;    return type === 'function' || type === 'object' && !!obj;  }  // Is a given value equal to null?  function isNull(obj) {    return obj === null;  }  // Is a given variable undefined?  function isUndefined(obj) {    return obj === void 0;  }  // Is a given value a boolean?  function isBoolean(obj) {    return obj === true || obj === false || toString.call(obj) === '[object Boolean]';  }  // Is a given value a DOM element?  function isElement(obj) {    return !!(obj && obj.nodeType === 1);  }  // Internal function for creating a `toString`-based type tester.  function tagTester(name) {    var tag = '[object ' + name + ']';    return function(obj) {      return toString.call(obj) === tag;    };  }  var isString = tagTester('String');  var isNumber = tagTester('Number');  var isDate = tagTester('Date');  var isRegExp = tagTester('RegExp');  var isError = tagTester('Error');  var isSymbol = tagTester('Symbol');  var isArrayBuffer = tagTester('ArrayBuffer');  var isFunction = tagTester('Function');  // Optimize `isFunction` if appropriate. Work around some `typeof` bugs in old  // v8, IE 11 (#1621), Safari 8 (#1929), and PhantomJS (#2236).  var nodelist = root.document && root.document.childNodes;  if (typeof /./ != 'function' && typeof Int8Array != 'object' && typeof nodelist != 'function') {    isFunction = function(obj) {      return typeof obj == 'function' || false;    };  }  var isFunction$1 = isFunction;  var hasObjectTag = tagTester('Object');  // In IE 10 - Edge 13, `DataView` has string tag `'[object Object]'`.  // In IE 11, the most common among them, this problem also applies to  // `Map`, `WeakMap` and `Set`.  var hasStringTagBug = (        supportsDataView && hasObjectTag(new DataView(new ArrayBuffer(8)))      ),      isIE11 = (typeof Map !== 'undefined' && hasObjectTag(new Map));  var isDataView = tagTester('DataView');  // In IE 10 - Edge 13, we need a different heuristic  // to determine whether an object is a `DataView`.  function ie10IsDataView(obj) {    return obj != null && isFunction$1(obj.getInt8) && isArrayBuffer(obj.buffer);  }  var isDataView$1 = (hasStringTagBug ? ie10IsDataView : isDataView);  // Is a given value an array?  // Delegates to ECMA5's native `Array.isArray`.  var isArray = nativeIsArray || tagTester('Array');  // Internal function to check whether `key` is an own property name of `obj`.  function has(obj, key) {    return obj != null && hasOwnProperty.call(obj, key);  }  var isArguments = tagTester('Arguments');  // Define a fallback version of the method in browsers (ahem, IE < 9), where  // there isn't any inspectable "Arguments" type.  (function() {    if (!isArguments(arguments)) {      isArguments = function(obj) {        return has(obj, 'callee');      };    }  }());  var isArguments$1 = isArguments;  // Is a given object a finite number?  function isFinite$1(obj) {    return !isSymbol(obj) && _isFinite(obj) && !isNaN(parseFloat(obj));  }  // Is the given value `NaN`?  function isNaN$1(obj) {    return isNumber(obj) && _isNaN(obj);  }  // Predicate-generating function. Often useful outside of Underscore.  function constant(value) {    return function() {      return value;    };  }  // Common internal logic for `isArrayLike` and `isBufferLike`.  function createSizePropertyCheck(getSizeProperty) {    return function(collection) {      var sizeProperty = getSizeProperty(collection);      return typeof sizeProperty == 'number' && sizeProperty >= 0 && sizeProperty <= MAX_ARRAY_INDEX;    }  }  // Internal helper to generate a function to obtain property `key` from `obj`.  function shallowProperty(key) {    return function(obj) {      return obj == null ? void 0 : obj[key];    };  }  // Internal helper to obtain the `byteLength` property of an object.  var getByteLength = shallowProperty('byteLength');  // Internal helper to determine whether we should spend extensive checks against  // `ArrayBuffer` et al.  var isBufferLike = createSizePropertyCheck(getByteLength);  // Is a given value a typed array?  var typedArrayPattern = /\[object ((I|Ui)nt(8|16|32)|Float(32|64)|Uint8Clamped|Big(I|Ui)nt64)Array\]/;  function isTypedArray(obj) {    // `ArrayBuffer.isView` is the most future-proof, so use it when available.    // Otherwise, fall back on the above regular expression.    return nativeIsView ? (nativeIsView(obj) && !isDataView$1(obj)) :                  isBufferLike(obj) && typedArrayPattern.test(toString.call(obj));  }  var isTypedArray$1 = supportsArrayBuffer ? isTypedArray : constant(false);  // Internal helper to obtain the `length` property of an object.  var getLength = shallowProperty('length');  // Internal helper to create a simple lookup structure.  // `collectNonEnumProps` used to depend on `_.contains`, but this led to  // circular imports. `emulatedSet` is a one-off solution that only works for  // arrays of strings.  function emulatedSet(keys) {    var hash = {};    for (var l = keys.length, i = 0; i < l; ++i) hash[keys[i]] = true;    return {      contains: function(key) { return hash[key]; },      push: function(key) {        hash[key] = true;        return keys.push(key);      }    };  }  // Internal helper. Checks `keys` for the presence of keys in IE < 9 that won't  // be iterated by `for key in ...` and thus missed. Extends `keys` in place if  // needed.  function collectNonEnumProps(obj, keys) {    keys = emulatedSet(keys);    var nonEnumIdx = nonEnumerableProps.length;    var constructor = obj.constructor;    var proto = isFunction$1(constructor) && constructor.prototype || ObjProto;    // Constructor is a special case.    var prop = 'constructor';    if (has(obj, prop) && !keys.contains(prop)) keys.push(prop);    while (nonEnumIdx--) {      prop = nonEnumerableProps[nonEnumIdx];      if (prop in obj && obj[prop] !== proto[prop] && !keys.contains(prop)) {        keys.push(prop);      }    }  }  // Retrieve the names of an object's own properties.  // Delegates to **ECMAScript 5**'s native `Object.keys`.  function keys(obj) {    if (!isObject(obj)) return [];    if (nativeKeys) return nativeKeys(obj);    var keys = [];    for (var key in obj) if (has(obj, key)) keys.push(key);    // Ahem, IE < 9.    if (hasEnumBug) collectNonEnumProps(obj, keys);    return keys;  }  // Is a given array, string, or object empty?  // An "empty" object has no enumerable own-properties.  function isEmpty(obj) {    if (obj == null) return true;    // Skip the more expensive `toString`-based type checks if `obj` has no    // `.length`.    var length = getLength(obj);    if (typeof length == 'number' && (      isArray(obj) || isString(obj) || isArguments$1(obj)    )) return length === 0;    return getLength(keys(obj)) === 0;  }  // Returns whether an object has a given set of `key:value` pairs.  function isMatch(object, attrs) {    var _keys = keys(attrs), length = _keys.length;    if (object == null) return !length;    var obj = Object(object);    for (var i = 0; i < length; i++) {      var key = _keys[i];      if (attrs[key] !== obj[key] || !(key in obj)) return false;    }    return true;  }  // If Underscore is called as a function, it returns a wrapped object that can  // be used OO-style. This wrapper holds altered versions of all functions added  // through `_.mixin`. Wrapped objects may be chained.  function _(obj) {    if (obj instanceof _) return obj;    if (!(this instanceof _)) return new _(obj);    this._wrapped = obj;  }  _.VERSION = VERSION;  // Extracts the result from a wrapped and chained object.  _.prototype.value = function() {    return this._wrapped;  };  // Provide unwrapping proxies for some methods used in engine operations  // such as arithmetic and JSON stringification.  _.prototype.valueOf = _.prototype.toJSON = _.prototype.value;  _.prototype.toString = function() {    return String(this._wrapped);  };  // Internal function to wrap or shallow-copy an ArrayBuffer,  // typed array or DataView to a new view, reusing the buffer.  function toBufferView(bufferSource) {    return new Uint8Array(      bufferSource.buffer || bufferSource,      bufferSource.byteOffset || 0,      getByteLength(bufferSource)    );  }  // We use this string twice, so give it a name for minification.  var tagDataView = '[object DataView]';  // Internal recursive comparison function for `_.isEqual`.  function eq(a, b, aStack, bStack) {    // Identical objects are equal. `0 === -0`, but they aren't identical.    // See the [Harmony `egal` proposal](https://wiki.ecmascript.org/doku.php?id=harmony:egal).    if (a === b) return a !== 0 || 1 / a === 1 / b;    // `null` or `undefined` only equal to itself (strict comparison).    if (a == null || b == null) return false;    // `NaN`s are equivalent, but non-reflexive.    if (a !== a) return b !== b;    // Exhaust primitive checks    var type = typeof a;    if (type !== 'function' && type !== 'object' && typeof b != 'object') return false;    return deepEq(a, b, aStack, bStack);  }  // Internal recursive comparison function for `_.isEqual`.  function deepEq(a, b, aStack, bStack) {    // Unwrap any wrapped objects.    if (a instanceof _) a = a._wrapped;    if (b instanceof _) b = b._wrapped;    // Compare `[[Class]]` names.    var className = toString.call(a);    if (className !== toString.call(b)) return false;    // Work around a bug in IE 10 - Edge 13.    if (hasStringTagBug && className == '[object Object]' && isDataView$1(a)) {      if (!isDataView$1(b)) return false;      className = tagDataView;    }    switch (className) {      // These types are compared by value.      case '[object RegExp]':        // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')      case '[object String]':        // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is        // equivalent to `new String("5")`.        return '' + a === '' + b;      case '[object Number]':        // `NaN`s are equivalent, but non-reflexive.        // Object(NaN) is equivalent to NaN.        if (+a !== +a) return +b !== +b;        // An `egal` comparison is performed for other numeric values.        return +a === 0 ? 1 / +a === 1 / b : +a === +b;      case '[object Date]':      case '[object Boolean]':        // Coerce dates and booleans to numeric primitive values. Dates are compared by their        // millisecond representations. Note that invalid dates with millisecond representations        // of `NaN` are not equivalent.        return +a === +b;      case '[object Symbol]':        return SymbolProto.valueOf.call(a) === SymbolProto.valueOf.call(b);      case '[object ArrayBuffer]':      case tagDataView:        // Coerce to typed array so we can fall through.        return deepEq(toBufferView(a), toBufferView(b), aStack, bStack);    }    var areArrays = className === '[object Array]';    if (!areArrays && isTypedArray$1(a)) {        var byteLength = getByteLength(a);        if (byteLength !== getByteLength(b)) return false;        if (a.buffer === b.buffer && a.byteOffset === b.byteOffset) return true;        areArrays = true;    }    if (!areArrays) {      if (typeof a != 'object' || typeof b != 'object') return false;      // Objects with different constructors are not equivalent, but `Object`s or `Array`s      // from different frames are.      var aCtor = a.constructor, bCtor = b.constructor;      if (aCtor !== bCtor && !(isFunction$1(aCtor) && aCtor instanceof aCtor &&                               isFunction$1(bCtor) && bCtor instanceof bCtor)                          && ('constructor' in a && 'constructor' in b)) {        return false;      }    }    // Assume equality for cyclic structures. The algorithm for detecting cyclic    // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.    // Initializing stack of traversed objects.    // It's done here since we only need them for objects and arrays comparison.    aStack = aStack || [];    bStack = bStack || [];    var length = aStack.length;    while (length--) {      // Linear search. Performance is inversely proportional to the number of      // unique nested structures.      if (aStack[length] === a) return bStack[length] === b;    }    // Add the first object to the stack of traversed objects.    aStack.push(a);    bStack.push(b);    // Recursively compare objects and arrays.    if (areArrays) {      // Compare array lengths to determine if a deep comparison is necessary.      length = a.length;      if (length !== b.length) return false;      // Deep compare the contents, ignoring non-numeric properties.      while (length--) {        if (!eq(a[length], b[length], aStack, bStack)) return false;      }    } else {      // Deep compare objects.      var _keys = keys(a), key;      length = _keys.length;      // Ensure that both objects contain the same number of properties before comparing deep equality.      if (keys(b).length !== length) return false;      while (length--) {        // Deep compare each member        key = _keys[length];        if (!(has(b, key) && eq(a[key], b[key], aStack, bStack))) return false;      }    }    // Remove the first object from the stack of traversed objects.    aStack.pop();    bStack.pop();    return true;  }  // Perform a deep comparison to check if two objects are equal.  function isEqual(a, b) {    return eq(a, b);  }  // Retrieve all the enumerable property names of an object.  function allKeys(obj) {    if (!isObject(obj)) return [];    var keys = [];    for (var key in obj) keys.push(key);    // Ahem, IE < 9.    if (hasEnumBug) collectNonEnumProps(obj, keys);    return keys;  }  // Since the regular `Object.prototype.toString` type tests don't work for  // some types in IE 11, we use a fingerprinting heuristic instead, based  // on the methods. It's not great, but it's the best we got.  // The fingerprint method lists are defined below.  function ie11fingerprint(methods) {    var length = getLength(methods);    return function(obj) {      if (obj == null) return false;      // `Map`, `WeakMap` and `Set` have no enumerable keys.      var keys = allKeys(obj);      if (getLength(keys)) return false;      for (var i = 0; i < length; i++) {        if (!isFunction$1(obj[methods[i]])) return false;      }      // If we are testing against `WeakMap`, we need to ensure that      // `obj` doesn't have a `forEach` method in order to distinguish      // it from a regular `Map`.      return methods !== weakMapMethods || !isFunction$1(obj[forEachName]);    };  }  // In the interest of compact minification, we write  // each string in the fingerprints only once.  var forEachName = 'forEach',      hasName = 'has',      commonInit = ['clear', 'delete'],      mapTail = ['get', hasName, 'set'];  // `Map`, `WeakMap` and `Set` each have slightly different  // combinations of the above sublists.  var mapMethods = commonInit.concat(forEachName, mapTail),      weakMapMethods = commonInit.concat(mapTail),      setMethods = ['add'].concat(commonInit, forEachName, hasName);  var isMap = isIE11 ? ie11fingerprint(mapMethods) : tagTester('Map');  var isWeakMap = isIE11 ? ie11fingerprint(weakMapMethods) : tagTester('WeakMap');  var isSet = isIE11 ? ie11fingerprint(setMethods) : tagTester('Set');  var isWeakSet = tagTester('WeakSet');  // Retrieve the values of an object's properties.  function values(obj) {    var _keys = keys(obj);    var length = _keys.length;    var values = Array(length);    for (var i = 0; i < length; i++) {      values[i] = obj[_keys[i]];    }    return values;  }  // Convert an object into a list of `[key, value]` pairs.  // The opposite of `_.object` with one argument.  function pairs(obj) {    var _keys = keys(obj);    var length = _keys.length;    var pairs = Array(length);    for (var i = 0; i < length; i++) {      pairs[i] = [_keys[i], obj[_keys[i]]];    }    return pairs;  }  // Invert the keys and values of an object. The values must be serializable.  function invert(obj) {    var result = {};    var _keys = keys(obj);    for (var i = 0, length = _keys.length; i < length; i++) {      result[obj[_keys[i]]] = _keys[i];    }    return result;  }  // Return a sorted list of the function names available on the object.  function functions(obj) {    var names = [];    for (var key in obj) {      if (isFunction$1(obj[key])) names.push(key);    }    return names.sort();  }  // An internal function for creating assigner functions.  function createAssigner(keysFunc, defaults) {    return function(obj) {      var length = arguments.length;      if (defaults) obj = Object(obj);      if (length < 2 || obj == null) return obj;      for (var index = 1; index < length; index++) {        var source = arguments[index],            keys = keysFunc(source),            l = keys.length;        for (var i = 0; i < l; i++) {          var key = keys[i];          if (!defaults || obj[key] === void 0) obj[key] = source[key];        }      }      return obj;    };  }  // Extend a given object with all the properties in passed-in object(s).  var extend = createAssigner(allKeys);  // Assigns a given object with all the own properties in the passed-in  // object(s).  // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)  var extendOwn = createAssigner(keys);  // Fill in a given object with default properties.  var defaults = createAssigner(allKeys, true);  // Create a naked function reference for surrogate-prototype-swapping.  function ctor() {    return function(){};  }  // An internal function for creating a new object that inherits from another.  function baseCreate(prototype) {    if (!isObject(prototype)) return {};    if (nativeCreate) return nativeCreate(prototype);    var Ctor = ctor();    Ctor.prototype = prototype;    var result = new Ctor;    Ctor.prototype = null;    return result;  }  // Creates an object that inherits from the given prototype object.  // If additional properties are provided then they will be added to the  // created object.  function create(prototype, props) {    var result = baseCreate(prototype);    if (props) extendOwn(result, props);    return result;  }  // Create a (shallow-cloned) duplicate of an object.  function clone(obj) {    if (!isObject(obj)) return obj;    return isArray(obj) ? obj.slice() : extend({}, obj);  }  // Invokes `interceptor` with the `obj` and then returns `obj`.  // The primary purpose of this method is to "tap into" a method chain, in  // order to perform operations on intermediate results within the chain.  function tap(obj, interceptor) {    interceptor(obj);    return obj;  }  // Normalize a (deep) property `path` to array.  // Like `_.iteratee`, this function can be customized.  function toPath(path) {    return isArray(path) ? path : [path];  }  _.toPath = toPath;  // Internal wrapper for `_.toPath` to enable minification.  // Similar to `cb` for `_.iteratee`.  function toPath$1(path) {    return _.toPath(path);  }  // Internal function to obtain a nested property in `obj` along `path`.  function deepGet(obj, path) {    var length = path.length;    for (var i = 0; i < length; i++) {      if (obj == null) return void 0;      obj = obj[path[i]];    }    return length ? obj : void 0;  }  // Get the value of the (deep) property on `path` from `object`.  // If any property in `path` does not exist or if the value is  // `undefined`, return `defaultValue` instead.  // The `path` is normalized through `_.toPath`.  function get(object, path, defaultValue) {    var value = deepGet(object, toPath$1(path));    return isUndefined(value) ? defaultValue : value;  }  // Shortcut function for checking if an object has a given property directly on  // itself (in other words, not on a prototype). Unlike the internal `has`  // function, this public version can also traverse nested properties.  function has$1(obj, path) {    path = toPath$1(path);    var length = path.length;    for (var i = 0; i < length; i++) {      var key = path[i];      if (!has(obj, key)) return false;      obj = obj[key];    }    return !!length;  }  // Keep the identity function around for default iteratees.  function identity(value) {    return value;  }  // Returns a predicate for checking whether an object has a given set of  // `key:value` pairs.  function matcher(attrs) {    attrs = extendOwn({}, attrs);    return function(obj) {      return isMatch(obj, attrs);    };  }  // Creates a function that, when passed an object, will traverse that object’s  // properties down the given `path`, specified as an array of keys or indices.  function property(path) {    path = toPath$1(path);    return function(obj) {      return deepGet(obj, path);    };  }  // Internal function that returns an efficient (for current engines) version  // of the passed-in callback, to be repeatedly applied in other Underscore  // functions.  function optimizeCb(func, context, argCount) {    if (context === void 0) return func;    switch (argCount == null ? 3 : argCount) {      case 1: return function(value) {        return func.call(context, value);      };      // The 2-argument case is omitted because we’re not using it.      case 3: return function(value, index, collection) {        return func.call(context, value, index, collection);      };      case 4: return function(accumulator, value, index, collection) {        return func.call(context, accumulator, value, index, collection);      };    }    return function() {      return func.apply(context, arguments);    };  }  // An internal function to generate callbacks that can be applied to each  // element in a collection, returning the desired result — either `_.identity`,  // an arbitrary callback, a property matcher, or a property accessor.  function baseIteratee(value, context, argCount) {    if (value == null) return identity;    if (isFunction$1(value)) return optimizeCb(value, context, argCount);    if (isObject(value) && !isArray(value)) return matcher(value);    return property(value);  }  // External wrapper for our callback generator. Users may customize  // `_.iteratee` if they want additional predicate/iteratee shorthand styles.  // This abstraction hides the internal-only `argCount` argument.  function iteratee(value, context) {    return baseIteratee(value, context, Infinity);  }  _.iteratee = iteratee;  // The function we call internally to generate a callback. It invokes  // `_.iteratee` if overridden, otherwise `baseIteratee`.  function cb(value, context, argCount) {    if (_.iteratee !== iteratee) return _.iteratee(value, context);    return baseIteratee(value, context, argCount);  }  // Returns the results of applying the `iteratee` to each element of `obj`.  // In contrast to `_.map` it returns an object.  function mapObject(obj, iteratee, context) {    iteratee = cb(iteratee, context);    var _keys = keys(obj),        length = _keys.length,        results = {};    for (var index = 0; index < length; index++) {      var currentKey = _keys[index];      results[currentKey] = iteratee(obj[currentKey], currentKey, obj);    }    return results;  }  // Predicate-generating function. Often useful outside of Underscore.  function noop(){}  // Generates a function for a given object that returns a given property.  function propertyOf(obj) {    if (obj == null) return noop;    return function(path) {      return get(obj, path);    };  }  // Run a function **n** times.  function times(n, iteratee, context) {    var accum = Array(Math.max(0, n));    iteratee = optimizeCb(iteratee, context, 1);    for (var i = 0; i < n; i++) accum[i] = iteratee(i);    return accum;  }  // Return a random integer between `min` and `max` (inclusive).  function random(min, max) {    if (max == null) {      max = min;      min = 0;    }    return min + Math.floor(Math.random() * (max - min + 1));  }  // A (possibly faster) way to get the current timestamp as an integer.  var now = Date.now || function() {    return new Date().getTime();  };  // Internal helper to generate functions for escaping and unescaping strings  // to/from HTML interpolation.  function createEscaper(map) {    var escaper = function(match) {      return map[match];    };    // Regexes for identifying a key that needs to be escaped.    var source = '(?:' + keys(map).join('|') + ')';    var testRegexp = RegExp(source);    var replaceRegexp = RegExp(source, 'g');    return function(string) {      string = string == null ? '' : '' + string;      return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;    };  }  // Internal list of HTML entities for escaping.  var escapeMap = {    '&': '&',    '<': '<',    '>': '>',    '"': '"',    "'": ''',    '`': '`'  };  // Function for escaping strings to HTML interpolation.  var _escape = createEscaper(escapeMap);  // Internal list of HTML entities for unescaping.  var unescapeMap = invert(escapeMap);  // Function for unescaping strings from HTML interpolation.  var _unescape = createEscaper(unescapeMap);  // By default, Underscore uses ERB-style template delimiters. Change the  // following template settings to use alternative delimiters.  var templateSettings = _.templateSettings = {    evaluate: /<%([\s\S]+?)%>/g,    interpolate: /<%=([\s\S]+?)%>/g,    escape: /<%-([\s\S]+?)%>/g  };  // When customizing `_.templateSettings`, if you don't want to define an  // interpolation, evaluation or escaping regex, we need one that is  // guaranteed not to match.  var noMatch = /(.)^/;  // Certain characters need to be escaped so that they can be put into a  // string literal.  var escapes = {    "'": "'",    '\\': '\\',    '\r': 'r',    '\n': 'n',    '\u2028': 'u2028',    '\u2029': 'u2029'  };  var escapeRegExp = /\\|'|\r|\n|\u2028|\u2029/g;  function escapeChar(match) {    return '\\' + escapes[match];  }  // JavaScript micro-templating, similar to John Resig's implementation.  // Underscore templating handles arbitrary delimiters, preserves whitespace,  // and correctly escapes quotes within interpolated code.  // NB: `oldSettings` only exists for backwards compatibility.  function template(text, settings, oldSettings) {    if (!settings && oldSettings) settings = oldSettings;    settings = defaults({}, settings, _.templateSettings);    // Combine delimiters into one regular expression via alternation.    var matcher = RegExp([      (settings.escape || noMatch).source,      (settings.interpolate || noMatch).source,      (settings.evaluate || noMatch).source    ].join('|') + '|$', 'g');    // Compile the template source, escaping string literals appropriately.    var index = 0;    var source = "__p+='";    text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {      source += text.slice(index, offset).replace(escapeRegExp, escapeChar);      index = offset + match.length;      if (escape) {        source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";      } else if (interpolate) {        source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";      } else if (evaluate) {        source += "';\n" + evaluate + "\n__p+='";      }      // Adobe VMs need the match returned to produce the correct offset.      return match;    });    source += "';\n";    // If a variable is not specified, place data values in local scope.    if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';    source = "var __t,__p='',__j=Array.prototype.join," +      "print=function(){__p+=__j.call(arguments,'');};\n" +      source + 'return __p;\n';    var render;    try {      render = new Function(settings.variable || 'obj', '_', source);    } catch (e) {      e.source = source;      throw e;    }    var template = function(data) {      return render.call(this, data, _);    };    // Provide the compiled source as a convenience for precompilation.    var argument = settings.variable || 'obj';    template.source = 'function(' + argument + '){\n' + source + '}';    return template;  }  // Traverses the children of `obj` along `path`. If a child is a function, it  // is invoked with its parent as context. Returns the value of the final  // child, or `fallback` if any child is undefined.  function result(obj, path, fallback) {    path = toPath$1(path);    var length = path.length;    if (!length) {      return isFunction$1(fallback) ? fallback.call(obj) : fallback;    }    for (var i = 0; i < length; i++) {      var prop = obj == null ? void 0 : obj[path[i]];      if (prop === void 0) {        prop = fallback;        i = length; // Ensure we don't continue iterating.      }      obj = isFunction$1(prop) ? prop.call(obj) : prop;    }    return obj;  }  // Generate a unique integer id (unique within the entire client session).  // Useful for temporary DOM ids.  var idCounter = 0;  function uniqueId(prefix) {    var id = ++idCounter + '';    return prefix ? prefix + id : id;  }  // Start chaining a wrapped Underscore object.  function chain(obj) {    var instance = _(obj);    instance._chain = true;    return instance;  }  // Internal function to execute `sourceFunc` bound to `context` with optional  // `args`. Determines whether to execute a function as a constructor or as a  // normal function.  function executeBound(sourceFunc, boundFunc, context, callingContext, args) {    if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);    var self = baseCreate(sourceFunc.prototype);    var result = sourceFunc.apply(self, args);    if (isObject(result)) return result;    return self;  }  // Partially apply a function by creating a version that has had some of its  // arguments pre-filled, without changing its dynamic `this` context. `_` acts  // as a placeholder by default, allowing any combination of arguments to be  // pre-filled. Set `_.partial.placeholder` for a custom placeholder argument.  var partial = restArguments(function(func, boundArgs) {    var placeholder = partial.placeholder;    var bound = function() {      var position = 0, length = boundArgs.length;      var args = Array(length);      for (var i = 0; i < length; i++) {        args[i] = boundArgs[i] === placeholder ? arguments[position++] : boundArgs[i];      }      while (position < arguments.length) args.push(arguments[position++]);      return executeBound(func, bound, this, this, args);    };    return bound;  });  partial.placeholder = _;  // Create a function bound to a given object (assigning `this`, and arguments,  // optionally).  var bind = restArguments(function(func, context, args) {    if (!isFunction$1(func)) throw new TypeError('Bind must be called on a function');    var bound = restArguments(function(callArgs) {      return executeBound(func, bound, context, this, args.concat(callArgs));    });    return bound;  });  // Internal helper for collection methods to determine whether a collection  // should be iterated as an array or as an object.  // Related: https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength  // Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094  var isArrayLike = createSizePropertyCheck(getLength);  // Internal implementation of a recursive `flatten` function.  function flatten(input, depth, strict, output) {    output = output || [];    if (!depth && depth !== 0) {      depth = Infinity;    } else if (depth <= 0) {      return output.concat(input);    }    var idx = output.length;    for (var i = 0, length = getLength(input); i < length; i++) {      var value = input[i];      if (isArrayLike(value) && (isArray(value) || isArguments$1(value))) {        // Flatten current level of array or arguments object.        if (depth > 1) {          flatten(value, depth - 1, strict, output);          idx = output.length;        } else {          var j = 0, len = value.length;          while (j < len) output[idx++] = value[j++];        }      } else if (!strict) {        output[idx++] = value;      }    }    return output;  }  // Bind a number of an object's methods to that object. Remaining arguments  // are the method names to be bound. Useful for ensuring that all callbacks  // defined on an object belong to it.  var bindAll = restArguments(function(obj, keys) {    keys = flatten(keys, false, false);    var index = keys.length;    if (index < 1) throw new Error('bindAll must be passed function names');    while (index--) {      var key = keys[index];      obj[key] = bind(obj[key], obj);    }    return obj;  });  // Memoize an expensive function by storing its results.  function memoize(func, hasher) {    var memoize = function(key) {      var cache = memoize.cache;      var address = '' + (hasher ? hasher.apply(this, arguments) : key);      if (!has(cache, address)) cache[address] = func.apply(this, arguments);      return cache[address];    };    memoize.cache = {};    return memoize;  }  // Delays a function for the given number of milliseconds, and then calls  // it with the arguments supplied.  var delay = restArguments(function(func, wait, args) {    return setTimeout(function() {      return func.apply(null, args);    }, wait);  });  // Defers a function, scheduling it to run after the current call stack has  // cleared.  var defer = partial(delay, _, 1);  // Returns a function, that, when invoked, will only be triggered at most once  // during a given window of time. Normally, the throttled function will run  // as much as it can, without ever going more than once per `wait` duration;  // but if you'd like to disable the execution on the leading edge, pass  // `{leading: false}`. To disable execution on the trailing edge, ditto.  function throttle(func, wait, options) {    var timeout, context, args, result;    var previous = 0;    if (!options) options = {};    var later = function() {      previous = options.leading === false ? 0 : now();      timeout = null;      result = func.apply(context, args);      if (!timeout) context = args = null;    };    var throttled = function() {      var _now = now();      if (!previous && options.leading === false) previous = _now;      var remaining = wait - (_now - previous);      context = this;      args = arguments;      if (remaining <= 0 || remaining > wait) {        if (timeout) {          clearTimeout(timeout);          timeout = null;        }        previous = _now;        result = func.apply(context, args);        if (!timeout) context = args = null;      } else if (!timeout && options.trailing !== false) {        timeout = setTimeout(later, remaining);      }      return result;    };    throttled.cancel = function() {      clearTimeout(timeout);      previous = 0;      timeout = context = args = null;    };    return throttled;  }  // When a sequence of calls of the returned function ends, the argument  // function is triggered. The end of a sequence is defined by the `wait`  // parameter. If `immediate` is passed, the argument function will be  // triggered at the beginning of the sequence instead of at the end.  function debounce(func, wait, immediate) {    var timeout, result;    var later = function(context, args) {      timeout = null;      if (args) result = func.apply(context, args);    };    var debounced = restArguments(function(args) {      if (timeout) clearTimeout(timeout);      if (immediate) {        var callNow = !timeout;        timeout = setTimeout(later, wait);        if (callNow) result = func.apply(this, args);      } else {        timeout = delay(later, wait, this, args);      }      return result;    });    debounced.cancel = function() {      clearTimeout(timeout);      timeout = null;    };    return debounced;  }  // Returns the first function passed as an argument to the second,  // allowing you to adjust arguments, run code before and after, and  // conditionally execute the original function.  function wrap(func, wrapper) {    return partial(wrapper, func);  }  // Returns a negated version of the passed-in predicate.  function negate(predicate) {    return function() {      return !predicate.apply(this, arguments);    };  }  // Returns a function that is the composition of a list of functions, each  // consuming the return value of the function that follows.  function compose() {    var args = arguments;    var start = args.length - 1;    return function() {      var i = start;      var result = args[start].apply(this, arguments);      while (i--) result = args[i].call(this, result);      return result;    };  }  // Returns a function that will only be executed on and after the Nth call.  function after(times, func) {    return function() {      if (--times < 1) {        return func.apply(this, arguments);      }    };  }  // Returns a function that will only be executed up to (but not including) the  // Nth call.  function before(times, func) {    var memo;    return function() {      if (--times > 0) {        memo = func.apply(this, arguments);      }      if (times <= 1) func = null;      return memo;    };  }  // Returns a function that will be executed at most one time, no matter how  // often you call it. Useful for lazy initialization.  var once = partial(before, 2);  // Returns the first key on an object that passes a truth test.  function findKey(obj, predicate, context) {    predicate = cb(predicate, context);    var _keys = keys(obj), key;    for (var i = 0, length = _keys.length; i < length; i++) {      key = _keys[i];      if (predicate(obj[key], key, obj)) return key;    }  }  // Internal function to generate `_.findIndex` and `_.findLastIndex`.  function createPredicateIndexFinder(dir) {    return function(array, predicate, context) {      predicate = cb(predicate, context);      var length = getLength(array);      var index = dir > 0 ? 0 : length - 1;      for (; index >= 0 && index < length; index += dir) {        if (predicate(array[index], index, array)) return index;      }      return -1;    };  }  // Returns the first index on an array-like that passes a truth test.  var findIndex = createPredicateIndexFinder(1);  // Returns the last index on an array-like that passes a truth test.  var findLastIndex = createPredicateIndexFinder(-1);  // Use a comparator function to figure out the smallest index at which  // an object should be inserted so as to maintain order. Uses binary search.  function sortedIndex(array, obj, iteratee, context) {    iteratee = cb(iteratee, context, 1);    var value = iteratee(obj);    var low = 0, high = getLength(array);    while (low < high) {      var mid = Math.floor((low + high) / 2);      if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;    }    return low;  }  // Internal function to generate the `_.indexOf` and `_.lastIndexOf` functions.  function createIndexFinder(dir, predicateFind, sortedIndex) {    return function(array, item, idx) {      var i = 0, length = getLength(array);      if (typeof idx == 'number') {        if (dir > 0) {          i = idx >= 0 ? idx : Math.max(idx + length, i);        } else {          length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;        }      } else if (sortedIndex && idx && length) {        idx = sortedIndex(array, item);        return array[idx] === item ? idx : -1;      }      if (item !== item) {        idx = predicateFind(slice.call(array, i, length), isNaN$1);        return idx >= 0 ? idx + i : -1;      }      for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {        if (array[idx] === item) return idx;      }      return -1;    };  }  // Return the position of the first occurrence of an item in an array,  // or -1 if the item is not included in the array.  // If the array is large and already in sort order, pass `true`  // for **isSorted** to use binary search.  var indexOf = createIndexFinder(1, findIndex, sortedIndex);  // Return the position of the last occurrence of an item in an array,  // or -1 if the item is not included in the array.  var lastIndexOf = createIndexFinder(-1, findLastIndex);  // Return the first value which passes a truth test.  function find(obj, predicate, context) {    var keyFinder = isArrayLike(obj) ? findIndex : findKey;    var key = keyFinder(obj, predicate, context);    if (key !== void 0 && key !== -1) return obj[key];  }  // Convenience version of a common use case of `_.find`: getting the first  // object containing specific `key:value` pairs.  function findWhere(obj, attrs) {    return find(obj, matcher(attrs));  }  // The cornerstone for collection functions, an `each`  // implementation, aka `forEach`.  // Handles raw objects in addition to array-likes. Treats all  // sparse array-likes as if they were dense.  function each(obj, iteratee, context) {    iteratee = optimizeCb(iteratee, context);    var i, length;    if (isArrayLike(obj)) {      for (i = 0, length = obj.length; i < length; i++) {        iteratee(obj[i], i, obj);      }    } else {      var _keys = keys(obj);      for (i = 0, length = _keys.length; i < length; i++) {        iteratee(obj[_keys[i]], _keys[i], obj);      }    }    return obj;  }  // Return the results of applying the iteratee to each element.  function map(obj, iteratee, context) {    iteratee = cb(iteratee, context);    var _keys = !isArrayLike(obj) && keys(obj),        length = (_keys || obj).length,        results = Array(length);    for (var index = 0; index < length; index++) {      var currentKey = _keys ? _keys[index] : index;      results[index] = iteratee(obj[currentKey], currentKey, obj);    }    return results;  }  // Internal helper to create a reducing function, iterating left or right.  function createReduce(dir) {    // Wrap code that reassigns argument variables in a separate function than    // the one that accesses `arguments.length` to avoid a perf hit. (#1991)    var reducer = function(obj, iteratee, memo, initial) {      var _keys = !isArrayLike(obj) && keys(obj),          length = (_keys || obj).length,          index = dir > 0 ? 0 : length - 1;      if (!initial) {        memo = obj[_keys ? _keys[index] : index];        index += dir;      }      for (; index >= 0 && index < length; index += dir) {        var currentKey = _keys ? _keys[index] : index;        memo = iteratee(memo, obj[currentKey], currentKey, obj);      }      return memo;    };    return function(obj, iteratee, memo, context) {      var initial = arguments.length >= 3;      return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial);    };  }  // **Reduce** builds up a single result from a list of values, aka `inject`,  // or `foldl`.  var reduce = createReduce(1);  // The right-associative version of reduce, also known as `foldr`.  var reduceRight = createReduce(-1);  // Return all the elements that pass a truth test.  function filter(obj, predicate, context) {    var results = [];    predicate = cb(predicate, context);    each(obj, function(value, index, list) {      if (predicate(value, index, list)) results.push(value);    });    return results;  }  // Return all the elements for which a truth test fails.  function reject(obj, predicate, context) {    return filter(obj, negate(cb(predicate)), context);  }  // Determine whether all of the elements pass a truth test.  function every(obj, predicate, context) {    predicate = cb(predicate, context);    var _keys = !isArrayLike(obj) && keys(obj),        length = (_keys || obj).length;    for (var index = 0; index < length; index++) {      var currentKey = _keys ? _keys[index] : index;      if (!predicate(obj[currentKey], currentKey, obj)) return false;    }    return true;  }  // Determine if at least one element in the object passes a truth test.  function some(obj, predicate, context) {    predicate = cb(predicate, context);    var _keys = !isArrayLike(obj) && keys(obj),        length = (_keys || obj).length;    for (var index = 0; index < length; index++) {      var currentKey = _keys ? _keys[index] : index;      if (predicate(obj[currentKey], currentKey, obj)) return true;    }    return false;  }  // Determine if the array or object contains a given item (using `===`).  function contains(obj, item, fromIndex, guard) {    if (!isArrayLike(obj)) obj = values(obj);    if (typeof fromIndex != 'number' || guard) fromIndex = 0;    return indexOf(obj, item, fromIndex) >= 0;  }  // Invoke a method (with arguments) on every item in a collection.  var invoke = restArguments(function(obj, path, args) {    var contextPath, func;    if (isFunction$1(path)) {      func = path;    } else {      path = toPath$1(path);      contextPath = path.slice(0, -1);      path = path[path.length - 1];    }    return map(obj, function(context) {      var method = func;      if (!method) {        if (contextPath && contextPath.length) {          context = deepGet(context, contextPath);        }        if (context == null) return void 0;        method = context[path];      }      return method == null ? method : method.apply(context, args);    });  });  // Convenience version of a common use case of `_.map`: fetching a property.  function pluck(obj, key) {    return map(obj, property(key));  }  // Convenience version of a common use case of `_.filter`: selecting only  // objects containing specific `key:value` pairs.  function where(obj, attrs) {    return filter(obj, matcher(attrs));  }  // Return the maximum element (or element-based computation).  function max(obj, iteratee, context) {    var result = -Infinity, lastComputed = -Infinity,        value, computed;    if (iteratee == null || typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null) {      obj = isArrayLike(obj) ? obj : values(obj);      for (var i = 0, length = obj.length; i < length; i++) {        value = obj[i];        if (value != null && value > result) {          result = value;        }      }    } else {      iteratee = cb(iteratee, context);      each(obj, function(v, index, list) {        computed = iteratee(v, index, list);        if (computed > lastComputed || computed === -Infinity && result === -Infinity) {          result = v;          lastComputed = computed;        }      });    }    return result;  }  // Return the minimum element (or element-based computation).  function min(obj, iteratee, context) {    var result = Infinity, lastComputed = Infinity,        value, computed;    if (iteratee == null || typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null) {      obj = isArrayLike(obj) ? obj : values(obj);      for (var i = 0, length = obj.length; i < length; i++) {        value = obj[i];        if (value != null && value < result) {          result = value;        }      }    } else {      iteratee = cb(iteratee, context);      each(obj, function(v, index, list) {        computed = iteratee(v, index, list);        if (computed < lastComputed || computed === Infinity && result === Infinity) {          result = v;          lastComputed = computed;        }      });    }    return result;  }  // Sample **n** random values from a collection using the modern version of the  // [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher–Yates_shuffle).  // If **n** is not specified, returns a single random element.  // The internal `guard` argument allows it to work with `_.map`.  function sample(obj, n, guard) {    if (n == null || guard) {      if (!isArrayLike(obj)) obj = values(obj);      return obj[random(obj.length - 1)];    }    var sample = isArrayLike(obj) ? clone(obj) : values(obj);    var length = getLength(sample);    n = Math.max(Math.min(n, length), 0);    var last = length - 1;    for (var index = 0; index < n; index++) {      var rand = random(index, last);      var temp = sample[index];      sample[index] = sample[rand];      sample[rand] = temp;    }    return sample.slice(0, n);  }  // Shuffle a collection.  function shuffle(obj) {    return sample(obj, Infinity);  }  // Sort the object's values by a criterion produced by an iteratee.  function sortBy(obj, iteratee, context) {    var index = 0;    iteratee = cb(iteratee, context);    return pluck(map(obj, function(value, key, list) {      return {        value: value,        index: index++,        criteria: iteratee(value, key, list)      };    }).sort(function(left, right) {      var a = left.criteria;      var b = right.criteria;      if (a !== b) {        if (a > b || a === void 0) return 1;        if (a < b || b === void 0) return -1;      }      return left.index - right.index;    }), 'value');  }  // An internal function used for aggregate "group by" operations.  function group(behavior, partition) {    return function(obj, iteratee, context) {      var result = partition ? [[], []] : {};      iteratee = cb(iteratee, context);      each(obj, function(value, index) {        var key = iteratee(value, index, obj);        behavior(result, value, key);      });      return result;    };  }  // Groups the object's values by a criterion. Pass either a string attribute  // to group by, or a function that returns the criterion.  var groupBy = group(function(result, value, key) {    if (has(result, key)) result[key].push(value); else result[key] = [value];  });  // Indexes the object's values by a criterion, similar to `_.groupBy`, but for  // when you know that your index values will be unique.  var indexBy = group(function(result, value, key) {    result[key] = value;  });  // Counts instances of an object that group by a certain criterion. Pass  // either a string attribute to count by, or a function that returns the  // criterion.  var countBy = group(function(result, value, key) {    if (has(result, key)) result[key]++; else result[key] = 1;  });  // Split a collection into two arrays: one whose elements all pass the given  // truth test, and one whose elements all do not pass the truth test.  var partition = group(function(result, value, pass) {    result[pass ? 0 : 1].push(value);  }, true);  // Safely create a real, live array from anything iterable.  var reStrSymbol = /[^\ud800-\udfff]|[\ud800-\udbff][\udc00-\udfff]|[\ud800-\udfff]/g;  function toArray(obj) {    if (!obj) return [];    if (isArray(obj)) return slice.call(obj);    if (isString(obj)) {      // Keep surrogate pair characters together.      return obj.match(reStrSymbol);    }    if (isArrayLike(obj)) return map(obj, identity);    return values(obj);  }  // Return the number of elements in a collection.  function size(obj) {    if (obj == null) return 0;    return isArrayLike(obj) ? obj.length : keys(obj).length;  }  // Internal `_.pick` helper function to determine whether `key` is an enumerable  // property name of `obj`.  function keyInObj(value, key, obj) {    return key in obj;  }  // Return a copy of the object only containing the allowed properties.  var pick = restArguments(function(obj, keys) {    var result = {}, iteratee = keys[0];    if (obj == null) return result;    if (isFunction$1(iteratee)) {      if (keys.length > 1) iteratee = optimizeCb(iteratee, keys[1]);      keys = allKeys(obj);    } else {      iteratee = keyInObj;      keys = flatten(keys, false, false);      obj = Object(obj);    }    for (var i = 0, length = keys.length; i < length; i++) {      var key = keys[i];      var value = obj[key];      if (iteratee(value, key, obj)) result[key] = value;    }    return result;  });  // Return a copy of the object without the disallowed properties.  var omit = restArguments(function(obj, keys) {    var iteratee = keys[0], context;    if (isFunction$1(iteratee)) {      iteratee = negate(iteratee);      if (keys.length > 1) context = keys[1];    } else {      keys = map(flatten(keys, false, false), String);      iteratee = function(value, key) {        return !contains(keys, key);      };    }    return pick(obj, iteratee, context);  });  // Returns everything but the last entry of the array. Especially useful on  // the arguments object. Passing **n** will return all the values in  // the array, excluding the last N.  function initial(array, n, guard) {    return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));  }  // Get the first element of an array. Passing **n** will return the first N  // values in the array. The **guard** check allows it to work with `_.map`.  function first(array, n, guard) {    if (array == null || array.length < 1) return n == null || guard ? void 0 : [];    if (n == null || guard) return array[0];    return initial(array, array.length - n);  }  // Returns everything but the first entry of the `array`. Especially useful on  // the `arguments` object. Passing an **n** will return the rest N values in the  // `array`.  function rest(array, n, guard) {    return slice.call(array, n == null || guard ? 1 : n);  }  // Get the last element of an array. Passing **n** will return the last N  // values in the array.  function last(array, n, guard) {    if (array == null || array.length < 1) return n == null || guard ? void 0 : [];    if (n == null || guard) return array[array.length - 1];    return rest(array, Math.max(0, array.length - n));  }  // Trim out all falsy values from an array.  function compact(array) {    return filter(array, Boolean);  }  // Flatten out an array, either recursively (by default), or up to `depth`.  // Passing `true` or `false` as `depth` means `1` or `Infinity`, respectively.  function flatten$1(array, depth) {    return flatten(array, depth, false);  }  // Take the difference between one array and a number of other arrays.  // Only the elements present in just the first array will remain.  var difference = restArguments(function(array, rest) {    rest = flatten(rest, true, true);    return filter(array, function(value){      return !contains(rest, value);    });  });  // Return a version of the array that does not contain the specified value(s).  var without = restArguments(function(array, otherArrays) {    return difference(array, otherArrays);  });  // Produce a duplicate-free version of the array. If the array has already  // been sorted, you have the option of using a faster algorithm.  // The faster algorithm will not work with an iteratee if the iteratee  // is not a one-to-one function, so providing an iteratee will disable  // the faster algorithm.  function uniq(array, isSorted, iteratee, context) {    if (!isBoolean(isSorted)) {      context = iteratee;      iteratee = isSorted;      isSorted = false;    }    if (iteratee != null) iteratee = cb(iteratee, context);    var result = [];    var seen = [];    for (var i = 0, length = getLength(array); i < length; i++) {      var value = array[i],          computed = iteratee ? iteratee(value, i, array) : value;      if (isSorted && !iteratee) {        if (!i || seen !== computed) result.push(value);        seen = computed;      } else if (iteratee) {        if (!contains(seen, computed)) {          seen.push(computed);          result.push(value);        }      } else if (!contains(result, value)) {        result.push(value);      }    }    return result;  }  // Produce an array that contains the union: each distinct element from all of  // the passed-in arrays.  var union = restArguments(function(arrays) {    return uniq(flatten(arrays, true, true));  });  // Produce an array that contains every item shared between all the  // passed-in arrays.  function intersection(array) {    var result = [];    var argsLength = arguments.length;    for (var i = 0, length = getLength(array); i < length; i++) {      var item = array[i];      if (contains(result, item)) continue;      var j;      for (j = 1; j < argsLength; j++) {        if (!contains(arguments[j], item)) break;      }      if (j === argsLength) result.push(item);    }    return result;  }  // Complement of zip. Unzip accepts an array of arrays and groups  // each array's elements on shared indices.  function unzip(array) {    var length = array && max(array, getLength).length || 0;    var result = Array(length);    for (var index = 0; index < length; index++) {      result[index] = pluck(array, index);    }    return result;  }  // Zip together multiple lists into a single array -- elements that share  // an index go together.  var zip = restArguments(unzip);  // Converts lists into objects. Pass either a single array of `[key, value]`  // pairs, or two parallel arrays of the same length -- one of keys, and one of  // the corresponding values. Passing by pairs is the reverse of `_.pairs`.  function object(list, values) {    var result = {};    for (var i = 0, length = getLength(list); i < length; i++) {      if (values) {        result[list[i]] = values[i];      } else {        result[list[i][0]] = list[i][1];      }    }    return result;  }  // Generate an integer Array containing an arithmetic progression. A port of  // the native Python `range()` function. See  // [the Python documentation](https://docs.python.org/library/functions.html#range).  function range(start, stop, step) {    if (stop == null) {      stop = start || 0;      start = 0;    }    if (!step) {      step = stop < start ? -1 : 1;    }    var length = Math.max(Math.ceil((stop - start) / step), 0);    var range = Array(length);    for (var idx = 0; idx < length; idx++, start += step) {      range[idx] = start;    }    return range;  }  // Chunk a single array into multiple arrays, each containing `count` or fewer  // items.  function chunk(array, count) {    if (count == null || count < 1) return [];    var result = [];    var i = 0, length = array.length;    while (i < length) {      result.push(slice.call(array, i, i += count));    }    return result;  }  // Helper function to continue chaining intermediate results.  function chainResult(instance, obj) {    return instance._chain ? _(obj).chain() : obj;  }  // Add your own custom functions to the Underscore object.  function mixin(obj) {    each(functions(obj), function(name) {      var func = _[name] = obj[name];      _.prototype[name] = function() {        var args = [this._wrapped];        push.apply(args, arguments);        return chainResult(this, func.apply(_, args));      };    });    return _;  }  // Add all mutator `Array` functions to the wrapper.  each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {    var method = ArrayProto[name];    _.prototype[name] = function() {      var obj = this._wrapped;      if (obj != null) {        method.apply(obj, arguments);        if ((name === 'shift' || name === 'splice') && obj.length === 0) {          delete obj[0];        }      }      return chainResult(this, obj);    };  });  // Add all accessor `Array` functions to the wrapper.  each(['concat', 'join', 'slice'], function(name) {    var method = ArrayProto[name];    _.prototype[name] = function() {      var obj = this._wrapped;      if (obj != null) obj = method.apply(obj, arguments);      return chainResult(this, obj);    };  });  // Named Exports  var allExports = {    __proto__: null,    VERSION: VERSION,    restArguments: restArguments,    isObject: isObject,    isNull: isNull,    isUndefined: isUndefined,    isBoolean: isBoolean,    isElement: isElement,    isString: isString,    isNumber: isNumber,    isDate: isDate,    isRegExp: isRegExp,    isError: isError,    isSymbol: isSymbol,    isArrayBuffer: isArrayBuffer,    isDataView: isDataView$1,    isArray: isArray,    isFunction: isFunction$1,    isArguments: isArguments$1,    isFinite: isFinite$1,    isNaN: isNaN$1,    isTypedArray: isTypedArray$1,    isEmpty: isEmpty,    isMatch: isMatch,    isEqual: isEqual,    isMap: isMap,    isWeakMap: isWeakMap,    isSet: isSet,    isWeakSet: isWeakSet,    keys: keys,    allKeys: allKeys,    values: values,    pairs: pairs,    invert: invert,    functions: functions,    methods: functions,    extend: extend,    extendOwn: extendOwn,    assign: extendOwn,    defaults: defaults,    create: create,    clone: clone,    tap: tap,    get: get,    has: has$1,    mapObject: mapObject,    identity: identity,    constant: constant,    noop: noop,    toPath: toPath,    property: property,    propertyOf: propertyOf,    matcher: matcher,    matches: matcher,    times: times,    random: random,    now: now,    escape: _escape,    unescape: _unescape,    templateSettings: templateSettings,    template: template,    result: result,    uniqueId: uniqueId,    chain: chain,    iteratee: iteratee,    partial: partial,    bind: bind,    bindAll: bindAll,    memoize: memoize,    delay: delay,    defer: defer,    throttle: throttle,    debounce: debounce,    wrap: wrap,    negate: negate,    compose: compose,    after: after,    before: before,    once: once,    findKey: findKey,    findIndex: findIndex,    findLastIndex: findLastIndex,    sortedIndex: sortedIndex,    indexOf: indexOf,    lastIndexOf: lastIndexOf,    find: find,    detect: find,    findWhere: findWhere,    each: each,    forEach: each,    map: map,    collect: map,    reduce: reduce,    foldl: reduce,    inject: reduce,    reduceRight: reduceRight,    foldr: reduceRight,    filter: filter,    select: filter,    reject: reject,    every: every,    all: every,    some: some,    any: some,    contains: contains,    includes: contains,    include: contains,    invoke: invoke,    pluck: pluck,    where: where,    max: max,    min: min,    shuffle: shuffle,    sample: sample,    sortBy: sortBy,    groupBy: groupBy,    indexBy: indexBy,    countBy: countBy,    partition: partition,    toArray: toArray,    size: size,    pick: pick,    omit: omit,    first: first,    head: first,    take: first,    initial: initial,    last: last,    rest: rest,    tail: rest,    drop: rest,    compact: compact,    flatten: flatten$1,    without: without,    uniq: uniq,    unique: uniq,    union: union,    intersection: intersection,    difference: difference,    unzip: unzip,    transpose: unzip,    zip: zip,    object: object,    range: range,    chunk: chunk,    mixin: mixin,    'default': _  };  // Default Export  // Add all of the Underscore functions to the wrapper object.  var _$1 = mixin(allExports);  // Legacy Node.js API.  _$1._ = _$1;  return _$1;})));}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})},{}],3:[function(require,module,exports){require('jsonform');},{"jsonform":1}]},{},[3]);
 |