| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927149281492914930149311493214933149341493514936149371493814939149401494114942149431494414945149461494714948149491495014951149521495314954149551495614957149581495914960149611496214963149641496514966149671496814969149701497114972149731497414975 | 
							- // TODO: in future try to replace most inline compability checks with polyfills for code readability 
 
- // element.textContent polyfill.
 
- // Unsupporting browsers: IE8
 
- if (Object.defineProperty && Object.getOwnPropertyDescriptor && Object.getOwnPropertyDescriptor(Element.prototype, "textContent") && !Object.getOwnPropertyDescriptor(Element.prototype, "textContent").get) {
 
- 	(function() {
 
- 		var innerText = Object.getOwnPropertyDescriptor(Element.prototype, "innerText");
 
- 		Object.defineProperty(Element.prototype, "textContent",
 
- 			{
 
- 				get: function() {
 
- 					return innerText.get.call(this);
 
- 				},
 
- 				set: function(s) {
 
- 					return innerText.set.call(this, s);
 
- 				}
 
- 			}
 
- 		);
 
- 	})();
 
- }
 
- // isArray polyfill for ie8
 
- if(!Array.isArray) {
 
-   Array.isArray = function(arg) {
 
-     return Object.prototype.toString.call(arg) === '[object Array]';
 
-   };
 
- };/**
 
-  * @license wysihtml5x v0.4.15
 
-  * https://github.com/Edicy/wysihtml5
 
-  *
 
-  * Author: Christopher Blum (https://github.com/tiff)
 
-  * Secondary author of extended features: Oliver Pulges (https://github.com/pulges)
 
-  *
 
-  * Copyright (C) 2012 XING AG
 
-  * Licensed under the MIT license (MIT)
 
-  *
 
-  */
 
- var wysihtml5 = {
 
-   version: "0.4.15",
 
-   // namespaces
 
-   commands:   {},
 
-   dom:        {},
 
-   quirks:     {},
 
-   toolbar:    {},
 
-   lang:       {},
 
-   selection:  {},
 
-   views:      {},
 
-   INVISIBLE_SPACE: "\uFEFF",
 
-   EMPTY_FUNCTION: function() {},
 
-   ELEMENT_NODE: 1,
 
-   TEXT_NODE:    3,
 
-   BACKSPACE_KEY:  8,
 
-   ENTER_KEY:      13,
 
-   ESCAPE_KEY:     27,
 
-   SPACE_KEY:      32,
 
-   DELETE_KEY:     46
 
- };
 
- ;/**
 
-  * Rangy, a cross-browser JavaScript range and selection library
 
-  * http://code.google.com/p/rangy/
 
-  *
 
-  * Copyright 2014, Tim Down
 
-  * Licensed under the MIT license.
 
-  * Version: 1.3alpha.20140804
 
-  * Build date: 4 August 2014
 
-  */
 
- (function(factory, global) {
 
-     if (typeof define == "function" && define.amd) {
 
-         // AMD. Register as an anonymous module.
 
-         define(factory);
 
- /*
 
-     TODO: look into this properly.
 
-     
 
-     } else if (typeof exports == "object") {
 
-         // Node/CommonJS style for Browserify
 
-         module.exports = factory;
 
- */
 
-     } else {
 
-         // No AMD or CommonJS support so we place Rangy in a global variable
 
-         global.rangy = factory();
 
-     }
 
- })(function() {
 
-     var OBJECT = "object", FUNCTION = "function", UNDEFINED = "undefined";
 
-     // Minimal set of properties required for DOM Level 2 Range compliance. Comparison constants such as START_TO_START
 
-     // are omitted because ranges in KHTML do not have them but otherwise work perfectly well. See issue 113.
 
-     var domRangeProperties = ["startContainer", "startOffset", "endContainer", "endOffset", "collapsed",
 
-         "commonAncestorContainer"];
 
-     // Minimal set of methods required for DOM Level 2 Range compliance
 
-     var domRangeMethods = ["setStart", "setStartBefore", "setStartAfter", "setEnd", "setEndBefore",
 
-         "setEndAfter", "collapse", "selectNode", "selectNodeContents", "compareBoundaryPoints", "deleteContents",
 
-         "extractContents", "cloneContents", "insertNode", "surroundContents", "cloneRange", "toString", "detach"];
 
-     var textRangeProperties = ["boundingHeight", "boundingLeft", "boundingTop", "boundingWidth", "htmlText", "text"];
 
-     // Subset of TextRange's full set of methods that we're interested in
 
-     var textRangeMethods = ["collapse", "compareEndPoints", "duplicate", "moveToElementText", "parentElement", "select",
 
-         "setEndPoint", "getBoundingClientRect"];
 
-     /*----------------------------------------------------------------------------------------------------------------*/
 
-     // Trio of functions taken from Peter Michaux's article:
 
-     // http://peter.michaux.ca/articles/feature-detection-state-of-the-art-browser-scripting
 
-     function isHostMethod(o, p) {
 
-         var t = typeof o[p];
 
-         return t == FUNCTION || (!!(t == OBJECT && o[p])) || t == "unknown";
 
-     }
 
-     function isHostObject(o, p) {
 
-         return !!(typeof o[p] == OBJECT && o[p]);
 
-     }
 
-     function isHostProperty(o, p) {
 
-         return typeof o[p] != UNDEFINED;
 
-     }
 
-     // Creates a convenience function to save verbose repeated calls to tests functions
 
-     function createMultiplePropertyTest(testFunc) {
 
-         return function(o, props) {
 
-             var i = props.length;
 
-             while (i--) {
 
-                 if (!testFunc(o, props[i])) {
 
-                     return false;
 
-                 }
 
-             }
 
-             return true;
 
-         };
 
-     }
 
-     // Next trio of functions are a convenience to save verbose repeated calls to previous two functions
 
-     var areHostMethods = createMultiplePropertyTest(isHostMethod);
 
-     var areHostObjects = createMultiplePropertyTest(isHostObject);
 
-     var areHostProperties = createMultiplePropertyTest(isHostProperty);
 
-     function isTextRange(range) {
 
-         return range && areHostMethods(range, textRangeMethods) && areHostProperties(range, textRangeProperties);
 
-     }
 
-     function getBody(doc) {
 
-         return isHostObject(doc, "body") ? doc.body : doc.getElementsByTagName("body")[0];
 
-     }
 
-     var modules = {};
 
-     var api = {
 
-         version: "1.3alpha.20140804",
 
-         initialized: false,
 
-         supported: true,
 
-         util: {
 
-             isHostMethod: isHostMethod,
 
-             isHostObject: isHostObject,
 
-             isHostProperty: isHostProperty,
 
-             areHostMethods: areHostMethods,
 
-             areHostObjects: areHostObjects,
 
-             areHostProperties: areHostProperties,
 
-             isTextRange: isTextRange,
 
-             getBody: getBody
 
-         },
 
-         features: {},
 
-         modules: modules,
 
-         config: {
 
-             alertOnFail: true,
 
-             alertOnWarn: false,
 
-             preferTextRange: false,
 
-             autoInitialize: (typeof rangyAutoInitialize == UNDEFINED) ? true : rangyAutoInitialize
 
-         }
 
-     };
 
-     function consoleLog(msg) {
 
-         if (isHostObject(window, "console") && isHostMethod(window.console, "log")) {
 
-             window.console.log(msg);
 
-         }
 
-     }
 
-     function alertOrLog(msg, shouldAlert) {
 
-         if (shouldAlert) {
 
-             window.alert(msg);
 
-         } else  {
 
-             consoleLog(msg);
 
-         }
 
-     }
 
-     function fail(reason) {
 
-         api.initialized = true;
 
-         api.supported = false;
 
-         alertOrLog("Rangy is not supported on this page in your browser. Reason: " + reason, api.config.alertOnFail);
 
-     }
 
-     api.fail = fail;
 
-     function warn(msg) {
 
-         alertOrLog("Rangy warning: " + msg, api.config.alertOnWarn);
 
-     }
 
-     api.warn = warn;
 
-     // Add utility extend() method
 
-     if ({}.hasOwnProperty) {
 
-         api.util.extend = function(obj, props, deep) {
 
-             var o, p;
 
-             for (var i in props) {
 
-                 if (props.hasOwnProperty(i)) {
 
-                     o = obj[i];
 
-                     p = props[i];
 
-                     if (deep && o !== null && typeof o == "object" && p !== null && typeof p == "object") {
 
-                         api.util.extend(o, p, true);
 
-                     }
 
-                     obj[i] = p;
 
-                 }
 
-             }
 
-             // Special case for toString, which does not show up in for...in loops in IE <= 8
 
-             if (props.hasOwnProperty("toString")) {
 
-                 obj.toString = props.toString;
 
-             }
 
-             return obj;
 
-         };
 
-     } else {
 
-         fail("hasOwnProperty not supported");
 
-     }
 
-     // Test whether Array.prototype.slice can be relied on for NodeLists and use an alternative toArray() if not
 
-     (function() {
 
-         var el = document.createElement("div");
 
-         el.appendChild(document.createElement("span"));
 
-         var slice = [].slice;
 
-         var toArray;
 
-         try {
 
-             if (slice.call(el.childNodes, 0)[0].nodeType == 1) {
 
-                 toArray = function(arrayLike) {
 
-                     return slice.call(arrayLike, 0);
 
-                 };
 
-             }
 
-         } catch (e) {}
 
-         if (!toArray) {
 
-             toArray = function(arrayLike) {
 
-                 var arr = [];
 
-                 for (var i = 0, len = arrayLike.length; i < len; ++i) {
 
-                     arr[i] = arrayLike[i];
 
-                 }
 
-                 return arr;
 
-             };
 
-         }
 
-         api.util.toArray = toArray;
 
-     })();
 
-     // Very simple event handler wrapper function that doesn't attempt to solve issues such as "this" handling or
 
-     // normalization of event properties
 
-     var addListener;
 
-     if (isHostMethod(document, "addEventListener")) {
 
-         addListener = function(obj, eventType, listener) {
 
-             obj.addEventListener(eventType, listener, false);
 
-         };
 
-     } else if (isHostMethod(document, "attachEvent")) {
 
-         addListener = function(obj, eventType, listener) {
 
-             obj.attachEvent("on" + eventType, listener);
 
-         };
 
-     } else {
 
-         fail("Document does not have required addEventListener or attachEvent method");
 
-     }
 
-     api.util.addListener = addListener;
 
-     var initListeners = [];
 
-     function getErrorDesc(ex) {
 
-         return ex.message || ex.description || String(ex);
 
-     }
 
-     // Initialization
 
-     function init() {
 
-         if (api.initialized) {
 
-             return;
 
-         }
 
-         var testRange;
 
-         var implementsDomRange = false, implementsTextRange = false;
 
-         // First, perform basic feature tests
 
-         if (isHostMethod(document, "createRange")) {
 
-             testRange = document.createRange();
 
-             if (areHostMethods(testRange, domRangeMethods) && areHostProperties(testRange, domRangeProperties)) {
 
-                 implementsDomRange = true;
 
-             }
 
-         }
 
-         var body = getBody(document);
 
-         if (!body || body.nodeName.toLowerCase() != "body") {
 
-             fail("No body element found");
 
-             return;
 
-         }
 
-         if (body && isHostMethod(body, "createTextRange")) {
 
-             testRange = body.createTextRange();
 
-             if (isTextRange(testRange)) {
 
-                 implementsTextRange = true;
 
-             }
 
-         }
 
-         if (!implementsDomRange && !implementsTextRange) {
 
-             fail("Neither Range nor TextRange are available");
 
-             return;
 
-         }
 
-         api.initialized = true;
 
-         api.features = {
 
-             implementsDomRange: implementsDomRange,
 
-             implementsTextRange: implementsTextRange
 
-         };
 
-         // Initialize modules
 
-         var module, errorMessage;
 
-         for (var moduleName in modules) {
 
-             if ( (module = modules[moduleName]) instanceof Module ) {
 
-                 module.init(module, api);
 
-             }
 
-         }
 
-         // Call init listeners
 
-         for (var i = 0, len = initListeners.length; i < len; ++i) {
 
-             try {
 
-                 initListeners[i](api);
 
-             } catch (ex) {
 
-                 errorMessage = "Rangy init listener threw an exception. Continuing. Detail: " + getErrorDesc(ex);
 
-                 consoleLog(errorMessage);
 
-             }
 
-         }
 
-     }
 
-     // Allow external scripts to initialize this library in case it's loaded after the document has loaded
 
-     api.init = init;
 
-     // Execute listener immediately if already initialized
 
-     api.addInitListener = function(listener) {
 
-         if (api.initialized) {
 
-             listener(api);
 
-         } else {
 
-             initListeners.push(listener);
 
-         }
 
-     };
 
-     var shimListeners = [];
 
-     api.addShimListener = function(listener) {
 
-         shimListeners.push(listener);
 
-     };
 
-     function shim(win) {
 
-         win = win || window;
 
-         init();
 
-         // Notify listeners
 
-         for (var i = 0, len = shimListeners.length; i < len; ++i) {
 
-             shimListeners[i](win);
 
-         }
 
-     }
 
-     api.shim = api.createMissingNativeApi = shim;
 
-     function Module(name, dependencies, initializer) {
 
-         this.name = name;
 
-         this.dependencies = dependencies;
 
-         this.initialized = false;
 
-         this.supported = false;
 
-         this.initializer = initializer;
 
-     }
 
-     Module.prototype = {
 
-         init: function() {
 
-             var requiredModuleNames = this.dependencies || [];
 
-             for (var i = 0, len = requiredModuleNames.length, requiredModule, moduleName; i < len; ++i) {
 
-                 moduleName = requiredModuleNames[i];
 
-                 requiredModule = modules[moduleName];
 
-                 if (!requiredModule || !(requiredModule instanceof Module)) {
 
-                     throw new Error("required module '" + moduleName + "' not found");
 
-                 }
 
-                 requiredModule.init();
 
-                 if (!requiredModule.supported) {
 
-                     throw new Error("required module '" + moduleName + "' not supported");
 
-                 }
 
-             }
 
-             
 
-             // Now run initializer
 
-             this.initializer(this);
 
-         },
 
-         
 
-         fail: function(reason) {
 
-             this.initialized = true;
 
-             this.supported = false;
 
-             throw new Error("Module '" + this.name + "' failed to load: " + reason);
 
-         },
 
-         warn: function(msg) {
 
-             api.warn("Module " + this.name + ": " + msg);
 
-         },
 
-         deprecationNotice: function(deprecated, replacement) {
 
-             api.warn("DEPRECATED: " + deprecated + " in module " + this.name + "is deprecated. Please use " +
 
-                 replacement + " instead");
 
-         },
 
-         createError: function(msg) {
 
-             return new Error("Error in Rangy " + this.name + " module: " + msg);
 
-         }
 
-     };
 
-     
 
-     function createModule(isCore, name, dependencies, initFunc) {
 
-         var newModule = new Module(name, dependencies, function(module) {
 
-             if (!module.initialized) {
 
-                 module.initialized = true;
 
-                 try {
 
-                     initFunc(api, module);
 
-                     module.supported = true;
 
-                 } catch (ex) {
 
-                     var errorMessage = "Module '" + name + "' failed to load: " + getErrorDesc(ex);
 
-                     consoleLog(errorMessage);
 
-                 }
 
-             }
 
-         });
 
-         modules[name] = newModule;
 
-     }
 
-     api.createModule = function(name) {
 
-         // Allow 2 or 3 arguments (second argument is an optional array of dependencies)
 
-         var initFunc, dependencies;
 
-         if (arguments.length == 2) {
 
-             initFunc = arguments[1];
 
-             dependencies = [];
 
-         } else {
 
-             initFunc = arguments[2];
 
-             dependencies = arguments[1];
 
-         }
 
-         var module = createModule(false, name, dependencies, initFunc);
 
-         // Initialize the module immediately if the core is already initialized
 
-         if (api.initialized) {
 
-             module.init();
 
-         }
 
-     };
 
-     api.createCoreModule = function(name, dependencies, initFunc) {
 
-         createModule(true, name, dependencies, initFunc);
 
-     };
 
-     /*----------------------------------------------------------------------------------------------------------------*/
 
-     // Ensure rangy.rangePrototype and rangy.selectionPrototype are available immediately
 
-     function RangePrototype() {}
 
-     api.RangePrototype = RangePrototype;
 
-     api.rangePrototype = new RangePrototype();
 
-     function SelectionPrototype() {}
 
-     api.selectionPrototype = new SelectionPrototype();
 
-     /*----------------------------------------------------------------------------------------------------------------*/
 
-     // Wait for document to load before running tests
 
-     var docReady = false;
 
-     var loadHandler = function(e) {
 
-         if (!docReady) {
 
-             docReady = true;
 
-             if (!api.initialized && api.config.autoInitialize) {
 
-                 init();
 
-             }
 
-         }
 
-     };
 
-     // Test whether we have window and document objects that we will need
 
-     if (typeof window == UNDEFINED) {
 
-         fail("No window found");
 
-         return;
 
-     }
 
-     if (typeof document == UNDEFINED) {
 
-         fail("No document found");
 
-         return;
 
-     }
 
-     if (isHostMethod(document, "addEventListener")) {
 
-         document.addEventListener("DOMContentLoaded", loadHandler, false);
 
-     }
 
-     // Add a fallback in case the DOMContentLoaded event isn't supported
 
-     addListener(window, "load", loadHandler);
 
-     /*----------------------------------------------------------------------------------------------------------------*/
 
-     
 
-     // DOM utility methods used by Rangy
 
-     api.createCoreModule("DomUtil", [], function(api, module) {
 
-         var UNDEF = "undefined";
 
-         var util = api.util;
 
-         // Perform feature tests
 
-         if (!util.areHostMethods(document, ["createDocumentFragment", "createElement", "createTextNode"])) {
 
-             module.fail("document missing a Node creation method");
 
-         }
 
-         if (!util.isHostMethod(document, "getElementsByTagName")) {
 
-             module.fail("document missing getElementsByTagName method");
 
-         }
 
-         var el = document.createElement("div");
 
-         if (!util.areHostMethods(el, ["insertBefore", "appendChild", "cloneNode"] ||
 
-                 !util.areHostObjects(el, ["previousSibling", "nextSibling", "childNodes", "parentNode"]))) {
 
-             module.fail("Incomplete Element implementation");
 
-         }
 
-         // innerHTML is required for Range's createContextualFragment method
 
-         if (!util.isHostProperty(el, "innerHTML")) {
 
-             module.fail("Element is missing innerHTML property");
 
-         }
 
-         var textNode = document.createTextNode("test");
 
-         if (!util.areHostMethods(textNode, ["splitText", "deleteData", "insertData", "appendData", "cloneNode"] ||
 
-                 !util.areHostObjects(el, ["previousSibling", "nextSibling", "childNodes", "parentNode"]) ||
 
-                 !util.areHostProperties(textNode, ["data"]))) {
 
-             module.fail("Incomplete Text Node implementation");
 
-         }
 
-         /*----------------------------------------------------------------------------------------------------------------*/
 
-         // Removed use of indexOf because of a bizarre bug in Opera that is thrown in one of the Acid3 tests. I haven't been
 
-         // able to replicate it outside of the test. The bug is that indexOf returns -1 when called on an Array that
 
-         // contains just the document as a single element and the value searched for is the document.
 
-         var arrayContains = /*Array.prototype.indexOf ?
 
-             function(arr, val) {
 
-                 return arr.indexOf(val) > -1;
 
-             }:*/
 
-             function(arr, val) {
 
-                 var i = arr.length;
 
-                 while (i--) {
 
-                     if (arr[i] === val) {
 
-                         return true;
 
-                     }
 
-                 }
 
-                 return false;
 
-             };
 
-         // Opera 11 puts HTML elements in the null namespace, it seems, and IE 7 has undefined namespaceURI
 
-         function isHtmlNamespace(node) {
 
-             var ns;
 
-             return typeof node.namespaceURI == UNDEF || ((ns = node.namespaceURI) === null || ns == "http://www.w3.org/1999/xhtml");
 
-         }
 
-         function parentElement(node) {
 
-             var parent = node.parentNode;
 
-             return (parent.nodeType == 1) ? parent : null;
 
-         }
 
-         function getNodeIndex(node) {
 
-             var i = 0;
 
-             while( (node = node.previousSibling) ) {
 
-                 ++i;
 
-             }
 
-             return i;
 
-         }
 
-         function getNodeLength(node) {
 
-             switch (node.nodeType) {
 
-                 case 7:
 
-                 case 10:
 
-                     return 0;
 
-                 case 3:
 
-                 case 8:
 
-                     return node.length;
 
-                 default:
 
-                     return node.childNodes.length;
 
-             }
 
-         }
 
-         function getCommonAncestor(node1, node2) {
 
-             var ancestors = [], n;
 
-             for (n = node1; n; n = n.parentNode) {
 
-                 ancestors.push(n);
 
-             }
 
-             for (n = node2; n; n = n.parentNode) {
 
-                 if (arrayContains(ancestors, n)) {
 
-                     return n;
 
-                 }
 
-             }
 
-             return null;
 
-         }
 
-         function isAncestorOf(ancestor, descendant, selfIsAncestor) {
 
-             var n = selfIsAncestor ? descendant : descendant.parentNode;
 
-             while (n) {
 
-                 if (n === ancestor) {
 
-                     return true;
 
-                 } else {
 
-                     n = n.parentNode;
 
-                 }
 
-             }
 
-             return false;
 
-         }
 
-         function isOrIsAncestorOf(ancestor, descendant) {
 
-             return isAncestorOf(ancestor, descendant, true);
 
-         }
 
-         function getClosestAncestorIn(node, ancestor, selfIsAncestor) {
 
-             var p, n = selfIsAncestor ? node : node.parentNode;
 
-             while (n) {
 
-                 p = n.parentNode;
 
-                 if (p === ancestor) {
 
-                     return n;
 
-                 }
 
-                 n = p;
 
-             }
 
-             return null;
 
-         }
 
-         function isCharacterDataNode(node) {
 
-             var t = node.nodeType;
 
-             return t == 3 || t == 4 || t == 8 ; // Text, CDataSection or Comment
 
-         }
 
-         function isTextOrCommentNode(node) {
 
-             if (!node) {
 
-                 return false;
 
-             }
 
-             var t = node.nodeType;
 
-             return t == 3 || t == 8 ; // Text or Comment
 
-         }
 
-         function insertAfter(node, precedingNode) {
 
-             var nextNode = precedingNode.nextSibling, parent = precedingNode.parentNode;
 
-             if (nextNode) {
 
-                 parent.insertBefore(node, nextNode);
 
-             } else {
 
-                 parent.appendChild(node);
 
-             }
 
-             return node;
 
-         }
 
-         // Note that we cannot use splitText() because it is bugridden in IE 9.
 
-         function splitDataNode(node, index, positionsToPreserve) {
 
-             var newNode = node.cloneNode(false);
 
-             newNode.deleteData(0, index);
 
-             node.deleteData(index, node.length - index);
 
-             insertAfter(newNode, node);
 
-             // Preserve positions
 
-             if (positionsToPreserve) {
 
-                 for (var i = 0, position; position = positionsToPreserve[i++]; ) {
 
-                     // Handle case where position was inside the portion of node after the split point
 
-                     if (position.node == node && position.offset > index) {
 
-                         position.node = newNode;
 
-                         position.offset -= index;
 
-                     }
 
-                     // Handle the case where the position is a node offset within node's parent
 
-                     else if (position.node == node.parentNode && position.offset > getNodeIndex(node)) {
 
-                         ++position.offset;
 
-                     }
 
-                 }
 
-             }
 
-             return newNode;
 
-         }
 
-         function getDocument(node) {
 
-             if (node.nodeType == 9) {
 
-                 return node;
 
-             } else if (typeof node.ownerDocument != UNDEF) {
 
-                 return node.ownerDocument;
 
-             } else if (typeof node.document != UNDEF) {
 
-                 return node.document;
 
-             } else if (node.parentNode) {
 
-                 return getDocument(node.parentNode);
 
-             } else {
 
-                 throw module.createError("getDocument: no document found for node");
 
-             }
 
-         }
 
-         function getWindow(node) {
 
-             var doc = getDocument(node);
 
-             if (typeof doc.defaultView != UNDEF) {
 
-                 return doc.defaultView;
 
-             } else if (typeof doc.parentWindow != UNDEF) {
 
-                 return doc.parentWindow;
 
-             } else {
 
-                 throw module.createError("Cannot get a window object for node");
 
-             }
 
-         }
 
-         function getIframeDocument(iframeEl) {
 
-             if (typeof iframeEl.contentDocument != UNDEF) {
 
-                 return iframeEl.contentDocument;
 
-             } else if (typeof iframeEl.contentWindow != UNDEF) {
 
-                 return iframeEl.contentWindow.document;
 
-             } else {
 
-                 throw module.createError("getIframeDocument: No Document object found for iframe element");
 
-             }
 
-         }
 
-         function getIframeWindow(iframeEl) {
 
-             if (typeof iframeEl.contentWindow != UNDEF) {
 
-                 return iframeEl.contentWindow;
 
-             } else if (typeof iframeEl.contentDocument != UNDEF) {
 
-                 return iframeEl.contentDocument.defaultView;
 
-             } else {
 
-                 throw module.createError("getIframeWindow: No Window object found for iframe element");
 
-             }
 
-         }
 
-         // This looks bad. Is it worth it?
 
-         function isWindow(obj) {
 
-             return obj && util.isHostMethod(obj, "setTimeout") && util.isHostObject(obj, "document");
 
-         }
 
-         function getContentDocument(obj, module, methodName) {
 
-             var doc;
 
-             if (!obj) {
 
-                 doc = document;
 
-             }
 
-             // Test if a DOM node has been passed and obtain a document object for it if so
 
-             else if (util.isHostProperty(obj, "nodeType")) {
 
-                 doc = (obj.nodeType == 1 && obj.tagName.toLowerCase() == "iframe") ?
 
-                     getIframeDocument(obj) : getDocument(obj);
 
-             }
 
-             // Test if the doc parameter appears to be a Window object
 
-             else if (isWindow(obj)) {
 
-                 doc = obj.document;
 
-             }
 
-             if (!doc) {
 
-                 throw module.createError(methodName + "(): Parameter must be a Window object or DOM node");
 
-             }
 
-             return doc;
 
-         }
 
-         function getRootContainer(node) {
 
-             var parent;
 
-             while ( (parent = node.parentNode) ) {
 
-                 node = parent;
 
-             }
 
-             return node;
 
-         }
 
-         function comparePoints(nodeA, offsetA, nodeB, offsetB) {
 
-             // See http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Comparing
 
-             var nodeC, root, childA, childB, n;
 
-             if (nodeA == nodeB) {
 
-                 // Case 1: nodes are the same
 
-                 return offsetA === offsetB ? 0 : (offsetA < offsetB) ? -1 : 1;
 
-             } else if ( (nodeC = getClosestAncestorIn(nodeB, nodeA, true)) ) {
 
-                 // Case 2: node C (container B or an ancestor) is a child node of A
 
-                 return offsetA <= getNodeIndex(nodeC) ? -1 : 1;
 
-             } else if ( (nodeC = getClosestAncestorIn(nodeA, nodeB, true)) ) {
 
-                 // Case 3: node C (container A or an ancestor) is a child node of B
 
-                 return getNodeIndex(nodeC) < offsetB  ? -1 : 1;
 
-             } else {
 
-                 root = getCommonAncestor(nodeA, nodeB);
 
-                 if (!root) {
 
-                     throw new Error("comparePoints error: nodes have no common ancestor");
 
-                 }
 
-                 // Case 4: containers are siblings or descendants of siblings
 
-                 childA = (nodeA === root) ? root : getClosestAncestorIn(nodeA, root, true);
 
-                 childB = (nodeB === root) ? root : getClosestAncestorIn(nodeB, root, true);
 
-                 if (childA === childB) {
 
-                     // This shouldn't be possible
 
-                     throw module.createError("comparePoints got to case 4 and childA and childB are the same!");
 
-                 } else {
 
-                     n = root.firstChild;
 
-                     while (n) {
 
-                         if (n === childA) {
 
-                             return -1;
 
-                         } else if (n === childB) {
 
-                             return 1;
 
-                         }
 
-                         n = n.nextSibling;
 
-                     }
 
-                 }
 
-             }
 
-         }
 
-         /*----------------------------------------------------------------------------------------------------------------*/
 
-         // Test for IE's crash (IE 6/7) or exception (IE >= 8) when a reference to garbage-collected text node is queried
 
-         var crashyTextNodes = false;
 
-         function isBrokenNode(node) {
 
-             var n;
 
-             try {
 
-                 n = node.parentNode;
 
-                 return false;
 
-             } catch (e) {
 
-                 return true;
 
-             }
 
-         }
 
-         (function() {
 
-             var el = document.createElement("b");
 
-             el.innerHTML = "1";
 
-             var textNode = el.firstChild;
 
-             el.innerHTML = "<br>";
 
-             crashyTextNodes = isBrokenNode(textNode);
 
-             api.features.crashyTextNodes = crashyTextNodes;
 
-         })();
 
-         /*----------------------------------------------------------------------------------------------------------------*/
 
-         function inspectNode(node) {
 
-             if (!node) {
 
-                 return "[No node]";
 
-             }
 
-             if (crashyTextNodes && isBrokenNode(node)) {
 
-                 return "[Broken node]";
 
-             }
 
-             if (isCharacterDataNode(node)) {
 
-                 return '"' + node.data + '"';
 
-             }
 
-             if (node.nodeType == 1) {
 
-                 var idAttr = node.id ? ' id="' + node.id + '"' : "";
 
-                 return "<" + node.nodeName + idAttr + ">[index:" + getNodeIndex(node) + ",length:" + node.childNodes.length + "][" + (node.innerHTML || "[innerHTML not supported]").slice(0, 25) + "]";
 
-             }
 
-             return node.nodeName;
 
-         }
 
-         function fragmentFromNodeChildren(node) {
 
-             var fragment = getDocument(node).createDocumentFragment(), child;
 
-             while ( (child = node.firstChild) ) {
 
-                 fragment.appendChild(child);
 
-             }
 
-             return fragment;
 
-         }
 
-         var getComputedStyleProperty;
 
-         if (typeof window.getComputedStyle != UNDEF) {
 
-             getComputedStyleProperty = function(el, propName) {
 
-                 return getWindow(el).getComputedStyle(el, null)[propName];
 
-             };
 
-         } else if (typeof document.documentElement.currentStyle != UNDEF) {
 
-             getComputedStyleProperty = function(el, propName) {
 
-                 return el.currentStyle[propName];
 
-             };
 
-         } else {
 
-             module.fail("No means of obtaining computed style properties found");
 
-         }
 
-         function NodeIterator(root) {
 
-             this.root = root;
 
-             this._next = root;
 
-         }
 
-         NodeIterator.prototype = {
 
-             _current: null,
 
-             hasNext: function() {
 
-                 return !!this._next;
 
-             },
 
-             next: function() {
 
-                 var n = this._current = this._next;
 
-                 var child, next;
 
-                 if (this._current) {
 
-                     child = n.firstChild;
 
-                     if (child) {
 
-                         this._next = child;
 
-                     } else {
 
-                         next = null;
 
-                         while ((n !== this.root) && !(next = n.nextSibling)) {
 
-                             n = n.parentNode;
 
-                         }
 
-                         this._next = next;
 
-                     }
 
-                 }
 
-                 return this._current;
 
-             },
 
-             detach: function() {
 
-                 this._current = this._next = this.root = null;
 
-             }
 
-         };
 
-         function createIterator(root) {
 
-             return new NodeIterator(root);
 
-         }
 
-         function DomPosition(node, offset) {
 
-             this.node = node;
 
-             this.offset = offset;
 
-         }
 
-         DomPosition.prototype = {
 
-             equals: function(pos) {
 
-                 return !!pos && this.node === pos.node && this.offset == pos.offset;
 
-             },
 
-             inspect: function() {
 
-                 return "[DomPosition(" + inspectNode(this.node) + ":" + this.offset + ")]";
 
-             },
 
-             toString: function() {
 
-                 return this.inspect();
 
-             }
 
-         };
 
-         function DOMException(codeName) {
 
-             this.code = this[codeName];
 
-             this.codeName = codeName;
 
-             this.message = "DOMException: " + this.codeName;
 
-         }
 
-         DOMException.prototype = {
 
-             INDEX_SIZE_ERR: 1,
 
-             HIERARCHY_REQUEST_ERR: 3,
 
-             WRONG_DOCUMENT_ERR: 4,
 
-             NO_MODIFICATION_ALLOWED_ERR: 7,
 
-             NOT_FOUND_ERR: 8,
 
-             NOT_SUPPORTED_ERR: 9,
 
-             INVALID_STATE_ERR: 11,
 
-             INVALID_NODE_TYPE_ERR: 24
 
-         };
 
-         DOMException.prototype.toString = function() {
 
-             return this.message;
 
-         };
 
-         api.dom = {
 
-             arrayContains: arrayContains,
 
-             isHtmlNamespace: isHtmlNamespace,
 
-             parentElement: parentElement,
 
-             getNodeIndex: getNodeIndex,
 
-             getNodeLength: getNodeLength,
 
-             getCommonAncestor: getCommonAncestor,
 
-             isAncestorOf: isAncestorOf,
 
-             isOrIsAncestorOf: isOrIsAncestorOf,
 
-             getClosestAncestorIn: getClosestAncestorIn,
 
-             isCharacterDataNode: isCharacterDataNode,
 
-             isTextOrCommentNode: isTextOrCommentNode,
 
-             insertAfter: insertAfter,
 
-             splitDataNode: splitDataNode,
 
-             getDocument: getDocument,
 
-             getWindow: getWindow,
 
-             getIframeWindow: getIframeWindow,
 
-             getIframeDocument: getIframeDocument,
 
-             getBody: util.getBody,
 
-             isWindow: isWindow,
 
-             getContentDocument: getContentDocument,
 
-             getRootContainer: getRootContainer,
 
-             comparePoints: comparePoints,
 
-             isBrokenNode: isBrokenNode,
 
-             inspectNode: inspectNode,
 
-             getComputedStyleProperty: getComputedStyleProperty,
 
-             fragmentFromNodeChildren: fragmentFromNodeChildren,
 
-             createIterator: createIterator,
 
-             DomPosition: DomPosition
 
-         };
 
-         api.DOMException = DOMException;
 
-     });
 
-     /*----------------------------------------------------------------------------------------------------------------*/
 
-     // Pure JavaScript implementation of DOM Range
 
-     api.createCoreModule("DomRange", ["DomUtil"], function(api, module) {
 
-         var dom = api.dom;
 
-         var util = api.util;
 
-         var DomPosition = dom.DomPosition;
 
-         var DOMException = api.DOMException;
 
-         var isCharacterDataNode = dom.isCharacterDataNode;
 
-         var getNodeIndex = dom.getNodeIndex;
 
-         var isOrIsAncestorOf = dom.isOrIsAncestorOf;
 
-         var getDocument = dom.getDocument;
 
-         var comparePoints = dom.comparePoints;
 
-         var splitDataNode = dom.splitDataNode;
 
-         var getClosestAncestorIn = dom.getClosestAncestorIn;
 
-         var getNodeLength = dom.getNodeLength;
 
-         var arrayContains = dom.arrayContains;
 
-         var getRootContainer = dom.getRootContainer;
 
-         var crashyTextNodes = api.features.crashyTextNodes;
 
-         /*----------------------------------------------------------------------------------------------------------------*/
 
-         // Utility functions
 
-         function isNonTextPartiallySelected(node, range) {
 
-             return (node.nodeType != 3) &&
 
-                    (isOrIsAncestorOf(node, range.startContainer) || isOrIsAncestorOf(node, range.endContainer));
 
-         }
 
-         function getRangeDocument(range) {
 
-             return range.document || getDocument(range.startContainer);
 
-         }
 
-         function getBoundaryBeforeNode(node) {
 
-             return new DomPosition(node.parentNode, getNodeIndex(node));
 
-         }
 
-         function getBoundaryAfterNode(node) {
 
-             return new DomPosition(node.parentNode, getNodeIndex(node) + 1);
 
-         }
 
-         function insertNodeAtPosition(node, n, o) {
 
-             var firstNodeInserted = node.nodeType == 11 ? node.firstChild : node;
 
-             if (isCharacterDataNode(n)) {
 
-                 if (o == n.length) {
 
-                     dom.insertAfter(node, n);
 
-                 } else {
 
-                     n.parentNode.insertBefore(node, o == 0 ? n : splitDataNode(n, o));
 
-                 }
 
-             } else if (o >= n.childNodes.length) {
 
-                 n.appendChild(node);
 
-             } else {
 
-                 n.insertBefore(node, n.childNodes[o]);
 
-             }
 
-             return firstNodeInserted;
 
-         }
 
-         function rangesIntersect(rangeA, rangeB, touchingIsIntersecting) {
 
-             assertRangeValid(rangeA);
 
-             assertRangeValid(rangeB);
 
-             if (getRangeDocument(rangeB) != getRangeDocument(rangeA)) {
 
-                 throw new DOMException("WRONG_DOCUMENT_ERR");
 
-             }
 
-             var startComparison = comparePoints(rangeA.startContainer, rangeA.startOffset, rangeB.endContainer, rangeB.endOffset),
 
-                 endComparison = comparePoints(rangeA.endContainer, rangeA.endOffset, rangeB.startContainer, rangeB.startOffset);
 
-             return touchingIsIntersecting ? startComparison <= 0 && endComparison >= 0 : startComparison < 0 && endComparison > 0;
 
-         }
 
-         function cloneSubtree(iterator) {
 
-             var partiallySelected;
 
-             for (var node, frag = getRangeDocument(iterator.range).createDocumentFragment(), subIterator; node = iterator.next(); ) {
 
-                 partiallySelected = iterator.isPartiallySelectedSubtree();
 
-                 node = node.cloneNode(!partiallySelected);
 
-                 if (partiallySelected) {
 
-                     subIterator = iterator.getSubtreeIterator();
 
-                     node.appendChild(cloneSubtree(subIterator));
 
-                     subIterator.detach();
 
-                 }
 
-                 if (node.nodeType == 10) { // DocumentType
 
-                     throw new DOMException("HIERARCHY_REQUEST_ERR");
 
-                 }
 
-                 frag.appendChild(node);
 
-             }
 
-             return frag;
 
-         }
 
-         function iterateSubtree(rangeIterator, func, iteratorState) {
 
-             var it, n;
 
-             iteratorState = iteratorState || { stop: false };
 
-             for (var node, subRangeIterator; node = rangeIterator.next(); ) {
 
-                 if (rangeIterator.isPartiallySelectedSubtree()) {
 
-                     if (func(node) === false) {
 
-                         iteratorState.stop = true;
 
-                         return;
 
-                     } else {
 
-                         // The node is partially selected by the Range, so we can use a new RangeIterator on the portion of
 
-                         // the node selected by the Range.
 
-                         subRangeIterator = rangeIterator.getSubtreeIterator();
 
-                         iterateSubtree(subRangeIterator, func, iteratorState);
 
-                         subRangeIterator.detach();
 
-                         if (iteratorState.stop) {
 
-                             return;
 
-                         }
 
-                     }
 
-                 } else {
 
-                     // The whole node is selected, so we can use efficient DOM iteration to iterate over the node and its
 
-                     // descendants
 
-                     it = dom.createIterator(node);
 
-                     while ( (n = it.next()) ) {
 
-                         if (func(n) === false) {
 
-                             iteratorState.stop = true;
 
-                             return;
 
-                         }
 
-                     }
 
-                 }
 
-             }
 
-         }
 
-         function deleteSubtree(iterator) {
 
-             var subIterator;
 
-             while (iterator.next()) {
 
-                 if (iterator.isPartiallySelectedSubtree()) {
 
-                     subIterator = iterator.getSubtreeIterator();
 
-                     deleteSubtree(subIterator);
 
-                     subIterator.detach();
 
-                 } else {
 
-                     iterator.remove();
 
-                 }
 
-             }
 
-         }
 
-         function extractSubtree(iterator) {
 
-             for (var node, frag = getRangeDocument(iterator.range).createDocumentFragment(), subIterator; node = iterator.next(); ) {
 
-                 if (iterator.isPartiallySelectedSubtree()) {
 
-                     node = node.cloneNode(false);
 
-                     subIterator = iterator.getSubtreeIterator();
 
-                     node.appendChild(extractSubtree(subIterator));
 
-                     subIterator.detach();
 
-                 } else {
 
-                     iterator.remove();
 
-                 }
 
-                 if (node.nodeType == 10) { // DocumentType
 
-                     throw new DOMException("HIERARCHY_REQUEST_ERR");
 
-                 }
 
-                 frag.appendChild(node);
 
-             }
 
-             return frag;
 
-         }
 
-         function getNodesInRange(range, nodeTypes, filter) {
 
-             var filterNodeTypes = !!(nodeTypes && nodeTypes.length), regex;
 
-             var filterExists = !!filter;
 
-             if (filterNodeTypes) {
 
-                 regex = new RegExp("^(" + nodeTypes.join("|") + ")$");
 
-             }
 
-             var nodes = [];
 
-             iterateSubtree(new RangeIterator(range, false), function(node) {
 
-                 if (filterNodeTypes && !regex.test(node.nodeType)) {
 
-                     return;
 
-                 }
 
-                 if (filterExists && !filter(node)) {
 
-                     return;
 
-                 }
 
-                 // Don't include a boundary container if it is a character data node and the range does not contain any
 
-                 // of its character data. See issue 190.
 
-                 var sc = range.startContainer;
 
-                 if (node == sc && isCharacterDataNode(sc) && range.startOffset == sc.length) {
 
-                     return;
 
-                 }
 
-                 var ec = range.endContainer;
 
-                 if (node == ec && isCharacterDataNode(ec) && range.endOffset == 0) {
 
-                     return;
 
-                 }
 
-                 nodes.push(node);
 
-             });
 
-             return nodes;
 
-         }
 
-         function inspect(range) {
 
-             var name = (typeof range.getName == "undefined") ? "Range" : range.getName();
 
-             return "[" + name + "(" + dom.inspectNode(range.startContainer) + ":" + range.startOffset + ", " +
 
-                     dom.inspectNode(range.endContainer) + ":" + range.endOffset + ")]";
 
-         }
 
-         /*----------------------------------------------------------------------------------------------------------------*/
 
-         // RangeIterator code partially borrows from IERange by Tim Ryan (http://github.com/timcameronryan/IERange)
 
-         function RangeIterator(range, clonePartiallySelectedTextNodes) {
 
-             this.range = range;
 
-             this.clonePartiallySelectedTextNodes = clonePartiallySelectedTextNodes;
 
-             if (!range.collapsed) {
 
-                 this.sc = range.startContainer;
 
-                 this.so = range.startOffset;
 
-                 this.ec = range.endContainer;
 
-                 this.eo = range.endOffset;
 
-                 var root = range.commonAncestorContainer;
 
-                 if (this.sc === this.ec && isCharacterDataNode(this.sc)) {
 
-                     this.isSingleCharacterDataNode = true;
 
-                     this._first = this._last = this._next = this.sc;
 
-                 } else {
 
-                     this._first = this._next = (this.sc === root && !isCharacterDataNode(this.sc)) ?
 
-                         this.sc.childNodes[this.so] : getClosestAncestorIn(this.sc, root, true);
 
-                     this._last = (this.ec === root && !isCharacterDataNode(this.ec)) ?
 
-                         this.ec.childNodes[this.eo - 1] : getClosestAncestorIn(this.ec, root, true);
 
-                 }
 
-             }
 
-         }
 
-         RangeIterator.prototype = {
 
-             _current: null,
 
-             _next: null,
 
-             _first: null,
 
-             _last: null,
 
-             isSingleCharacterDataNode: false,
 
-             reset: function() {
 
-                 this._current = null;
 
-                 this._next = this._first;
 
-             },
 
-             hasNext: function() {
 
-                 return !!this._next;
 
-             },
 
-             next: function() {
 
-                 // Move to next node
 
-                 var current = this._current = this._next;
 
-                 if (current) {
 
-                     this._next = (current !== this._last) ? current.nextSibling : null;
 
-                     // Check for partially selected text nodes
 
-                     if (isCharacterDataNode(current) && this.clonePartiallySelectedTextNodes) {
 
-                         if (current === this.ec) {
 
-                             (current = current.cloneNode(true)).deleteData(this.eo, current.length - this.eo);
 
-                         }
 
-                         if (this._current === this.sc) {
 
-                             (current = current.cloneNode(true)).deleteData(0, this.so);
 
-                         }
 
-                     }
 
-                 }
 
-                 return current;
 
-             },
 
-             remove: function() {
 
-                 var current = this._current, start, end;
 
-                 if (isCharacterDataNode(current) && (current === this.sc || current === this.ec)) {
 
-                     start = (current === this.sc) ? this.so : 0;
 
-                     end = (current === this.ec) ? this.eo : current.length;
 
-                     if (start != end) {
 
-                         current.deleteData(start, end - start);
 
-                     }
 
-                 } else {
 
-                     if (current.parentNode) {
 
-                         current.parentNode.removeChild(current);
 
-                     } else {
 
-                     }
 
-                 }
 
-             },
 
-             // Checks if the current node is partially selected
 
-             isPartiallySelectedSubtree: function() {
 
-                 var current = this._current;
 
-                 return isNonTextPartiallySelected(current, this.range);
 
-             },
 
-             getSubtreeIterator: function() {
 
-                 var subRange;
 
-                 if (this.isSingleCharacterDataNode) {
 
-                     subRange = this.range.cloneRange();
 
-                     subRange.collapse(false);
 
-                 } else {
 
-                     subRange = new Range(getRangeDocument(this.range));
 
-                     var current = this._current;
 
-                     var startContainer = current, startOffset = 0, endContainer = current, endOffset = getNodeLength(current);
 
-                     if (isOrIsAncestorOf(current, this.sc)) {
 
-                         startContainer = this.sc;
 
-                         startOffset = this.so;
 
-                     }
 
-                     if (isOrIsAncestorOf(current, this.ec)) {
 
-                         endContainer = this.ec;
 
-                         endOffset = this.eo;
 
-                     }
 
-                     updateBoundaries(subRange, startContainer, startOffset, endContainer, endOffset);
 
-                 }
 
-                 return new RangeIterator(subRange, this.clonePartiallySelectedTextNodes);
 
-             },
 
-             detach: function() {
 
-                 this.range = this._current = this._next = this._first = this._last = this.sc = this.so = this.ec = this.eo = null;
 
-             }
 
-         };
 
-         /*----------------------------------------------------------------------------------------------------------------*/
 
-         var beforeAfterNodeTypes = [1, 3, 4, 5, 7, 8, 10];
 
-         var rootContainerNodeTypes = [2, 9, 11];
 
-         var readonlyNodeTypes = [5, 6, 10, 12];
 
-         var insertableNodeTypes = [1, 3, 4, 5, 7, 8, 10, 11];
 
-         var surroundNodeTypes = [1, 3, 4, 5, 7, 8];
 
-         function createAncestorFinder(nodeTypes) {
 
-             return function(node, selfIsAncestor) {
 
-                 var t, n = selfIsAncestor ? node : node.parentNode;
 
-                 while (n) {
 
-                     t = n.nodeType;
 
-                     if (arrayContains(nodeTypes, t)) {
 
-                         return n;
 
-                     }
 
-                     n = n.parentNode;
 
-                 }
 
-                 return null;
 
-             };
 
-         }
 
-         var getDocumentOrFragmentContainer = createAncestorFinder( [9, 11] );
 
-         var getReadonlyAncestor = createAncestorFinder(readonlyNodeTypes);
 
-         var getDocTypeNotationEntityAncestor = createAncestorFinder( [6, 10, 12] );
 
-         function assertNoDocTypeNotationEntityAncestor(node, allowSelf) {
 
-             if (getDocTypeNotationEntityAncestor(node, allowSelf)) {
 
-                 throw new DOMException("INVALID_NODE_TYPE_ERR");
 
-             }
 
-         }
 
-         function assertValidNodeType(node, invalidTypes) {
 
-             if (!arrayContains(invalidTypes, node.nodeType)) {
 
-                 throw new DOMException("INVALID_NODE_TYPE_ERR");
 
-             }
 
-         }
 
-         function assertValidOffset(node, offset) {
 
-             if (offset < 0 || offset > (isCharacterDataNode(node) ? node.length : node.childNodes.length)) {
 
-                 throw new DOMException("INDEX_SIZE_ERR");
 
-             }
 
-         }
 
-         function assertSameDocumentOrFragment(node1, node2) {
 
-             if (getDocumentOrFragmentContainer(node1, true) !== getDocumentOrFragmentContainer(node2, true)) {
 
-                 throw new DOMException("WRONG_DOCUMENT_ERR");
 
-             }
 
-         }
 
-         function assertNodeNotReadOnly(node) {
 
-             if (getReadonlyAncestor(node, true)) {
 
-                 throw new DOMException("NO_MODIFICATION_ALLOWED_ERR");
 
-             }
 
-         }
 
-         function assertNode(node, codeName) {
 
-             if (!node) {
 
-                 throw new DOMException(codeName);
 
-             }
 
-         }
 
-         function isOrphan(node) {
 
-             return (crashyTextNodes && dom.isBrokenNode(node)) ||
 
-                 !arrayContains(rootContainerNodeTypes, node.nodeType) && !getDocumentOrFragmentContainer(node, true);
 
-         }
 
-         function isValidOffset(node, offset) {
 
-             return offset <= (isCharacterDataNode(node) ? node.length : node.childNodes.length);
 
-         }
 
-         function isRangeValid(range) {
 
-             return (!!range.startContainer && !!range.endContainer &&
 
-                     !isOrphan(range.startContainer) &&
 
-                     !isOrphan(range.endContainer) &&
 
-                     isValidOffset(range.startContainer, range.startOffset) &&
 
-                     isValidOffset(range.endContainer, range.endOffset));
 
-         }
 
-         function assertRangeValid(range) {
 
-             if (!isRangeValid(range)) {
 
-                 throw new Error("Range error: Range is no longer valid after DOM mutation (" + range.inspect() + ")");
 
-             }
 
-         }
 
-         /*----------------------------------------------------------------------------------------------------------------*/
 
-         // Test the browser's innerHTML support to decide how to implement createContextualFragment
 
-         var styleEl = document.createElement("style");
 
-         var htmlParsingConforms = false;
 
-         try {
 
-             styleEl.innerHTML = "<b>x</b>";
 
-             htmlParsingConforms = (styleEl.firstChild.nodeType == 3); // Opera incorrectly creates an element node
 
-         } catch (e) {
 
-             // IE 6 and 7 throw
 
-         }
 
-         api.features.htmlParsingConforms = htmlParsingConforms;
 
-         var createContextualFragment = htmlParsingConforms ?
 
-             // Implementation as per HTML parsing spec, trusting in the browser's implementation of innerHTML. See
 
-             // discussion and base code for this implementation at issue 67.
 
-             // Spec: http://html5.org/specs/dom-parsing.html#extensions-to-the-range-interface
 
-             // Thanks to Aleks Williams.
 
-             function(fragmentStr) {
 
-                 // "Let node the context object's start's node."
 
-                 var node = this.startContainer;
 
-                 var doc = getDocument(node);
 
-                 // "If the context object's start's node is null, raise an INVALID_STATE_ERR
 
-                 // exception and abort these steps."
 
-                 if (!node) {
 
-                     throw new DOMException("INVALID_STATE_ERR");
 
-                 }
 
-                 // "Let element be as follows, depending on node's interface:"
 
-                 // Document, Document Fragment: null
 
-                 var el = null;
 
-                 // "Element: node"
 
-                 if (node.nodeType == 1) {
 
-                     el = node;
 
-                 // "Text, Comment: node's parentElement"
 
-                 } else if (isCharacterDataNode(node)) {
 
-                     el = dom.parentElement(node);
 
-                 }
 
-                 // "If either element is null or element's ownerDocument is an HTML document
 
-                 // and element's local name is "html" and element's namespace is the HTML
 
-                 // namespace"
 
-                 if (el === null || (
 
-                     el.nodeName == "HTML" &&
 
-                     dom.isHtmlNamespace(getDocument(el).documentElement) &&
 
-                     dom.isHtmlNamespace(el)
 
-                 )) {
 
-                 // "let element be a new Element with "body" as its local name and the HTML
 
-                 // namespace as its namespace.""
 
-                     el = doc.createElement("body");
 
-                 } else {
 
-                     el = el.cloneNode(false);
 
-                 }
 
-                 // "If the node's document is an HTML document: Invoke the HTML fragment parsing algorithm."
 
-                 // "If the node's document is an XML document: Invoke the XML fragment parsing algorithm."
 
-                 // "In either case, the algorithm must be invoked with fragment as the input
 
-                 // and element as the context element."
 
-                 el.innerHTML = fragmentStr;
 
-                 // "If this raises an exception, then abort these steps. Otherwise, let new
 
-                 // children be the nodes returned."
 
-                 // "Let fragment be a new DocumentFragment."
 
-                 // "Append all new children to fragment."
 
-                 // "Return fragment."
 
-                 return dom.fragmentFromNodeChildren(el);
 
-             } :
 
-             // In this case, innerHTML cannot be trusted, so fall back to a simpler, non-conformant implementation that
 
-             // previous versions of Rangy used (with the exception of using a body element rather than a div)
 
-             function(fragmentStr) {
 
-                 var doc = getRangeDocument(this);
 
-                 var el = doc.createElement("body");
 
-                 el.innerHTML = fragmentStr;
 
-                 return dom.fragmentFromNodeChildren(el);
 
-             };
 
-         function splitRangeBoundaries(range, positionsToPreserve) {
 
-             assertRangeValid(range);
 
-             var sc = range.startContainer, so = range.startOffset, ec = range.endContainer, eo = range.endOffset;
 
-             var startEndSame = (sc === ec);
 
-             if (isCharacterDataNode(ec) && eo > 0 && eo < ec.length) {
 
-                 splitDataNode(ec, eo, positionsToPreserve);
 
-             }
 
-             if (isCharacterDataNode(sc) && so > 0 && so < sc.length) {
 
-                 sc = splitDataNode(sc, so, positionsToPreserve);
 
-                 if (startEndSame) {
 
-                     eo -= so;
 
-                     ec = sc;
 
-                 } else if (ec == sc.parentNode && eo >= getNodeIndex(sc)) {
 
-                     eo++;
 
-                 }
 
-                 so = 0;
 
-             }
 
-             range.setStartAndEnd(sc, so, ec, eo);
 
-         }
 
-         
 
-         function rangeToHtml(range) {
 
-             assertRangeValid(range);
 
-             var container = range.commonAncestorContainer.parentNode.cloneNode(false);
 
-             container.appendChild( range.cloneContents() );
 
-             return container.innerHTML;
 
-         }
 
-         /*----------------------------------------------------------------------------------------------------------------*/
 
-         var rangeProperties = ["startContainer", "startOffset", "endContainer", "endOffset", "collapsed",
 
-             "commonAncestorContainer"];
 
-         var s2s = 0, s2e = 1, e2e = 2, e2s = 3;
 
-         var n_b = 0, n_a = 1, n_b_a = 2, n_i = 3;
 
-         util.extend(api.rangePrototype, {
 
-             compareBoundaryPoints: function(how, range) {
 
-                 assertRangeValid(this);
 
-                 assertSameDocumentOrFragment(this.startContainer, range.startContainer);
 
-                 var nodeA, offsetA, nodeB, offsetB;
 
-                 var prefixA = (how == e2s || how == s2s) ? "start" : "end";
 
-                 var prefixB = (how == s2e || how == s2s) ? "start" : "end";
 
-                 nodeA = this[prefixA + "Container"];
 
-                 offsetA = this[prefixA + "Offset"];
 
-                 nodeB = range[prefixB + "Container"];
 
-                 offsetB = range[prefixB + "Offset"];
 
-                 return comparePoints(nodeA, offsetA, nodeB, offsetB);
 
-             },
 
-             insertNode: function(node) {
 
-                 assertRangeValid(this);
 
-                 assertValidNodeType(node, insertableNodeTypes);
 
-                 assertNodeNotReadOnly(this.startContainer);
 
-                 if (isOrIsAncestorOf(node, this.startContainer)) {
 
-                     throw new DOMException("HIERARCHY_REQUEST_ERR");
 
-                 }
 
-                 // No check for whether the container of the start of the Range is of a type that does not allow
 
-                 // children of the type of node: the browser's DOM implementation should do this for us when we attempt
 
-                 // to add the node
 
-                 var firstNodeInserted = insertNodeAtPosition(node, this.startContainer, this.startOffset);
 
-                 this.setStartBefore(firstNodeInserted);
 
-             },
 
-             cloneContents: function() {
 
-                 assertRangeValid(this);
 
-                 var clone, frag;
 
-                 if (this.collapsed) {
 
-                     return getRangeDocument(this).createDocumentFragment();
 
-                 } else {
 
-                     if (this.startContainer === this.endContainer && isCharacterDataNode(this.startContainer)) {
 
-                         clone = this.startContainer.cloneNode(true);
 
-                         clone.data = clone.data.slice(this.startOffset, this.endOffset);
 
-                         frag = getRangeDocument(this).createDocumentFragment();
 
-                         frag.appendChild(clone);
 
-                         return frag;
 
-                     } else {
 
-                         var iterator = new RangeIterator(this, true);
 
-                         clone = cloneSubtree(iterator);
 
-                         iterator.detach();
 
-                     }
 
-                     return clone;
 
-                 }
 
-             },
 
-             canSurroundContents: function() {
 
-                 assertRangeValid(this);
 
-                 assertNodeNotReadOnly(this.startContainer);
 
-                 assertNodeNotReadOnly(this.endContainer);
 
-                 // Check if the contents can be surrounded. Specifically, this means whether the range partially selects
 
-                 // no non-text nodes.
 
-                 var iterator = new RangeIterator(this, true);
 
-                 var boundariesInvalid = (iterator._first && (isNonTextPartiallySelected(iterator._first, this)) ||
 
-                         (iterator._last && isNonTextPartiallySelected(iterator._last, this)));
 
-                 iterator.detach();
 
-                 return !boundariesInvalid;
 
-             },
 
-             surroundContents: function(node) {
 
-                 assertValidNodeType(node, surroundNodeTypes);
 
-                 if (!this.canSurroundContents()) {
 
-                     throw new DOMException("INVALID_STATE_ERR");
 
-                 }
 
-                 // Extract the contents
 
-                 var content = this.extractContents();
 
-                 // Clear the children of the node
 
-                 if (node.hasChildNodes()) {
 
-                     while (node.lastChild) {
 
-                         node.removeChild(node.lastChild);
 
-                     }
 
-                 }
 
-                 // Insert the new node and add the extracted contents
 
-                 insertNodeAtPosition(node, this.startContainer, this.startOffset);
 
-                 node.appendChild(content);
 
-                 this.selectNode(node);
 
-             },
 
-             cloneRange: function() {
 
-                 assertRangeValid(this);
 
-                 var range = new Range(getRangeDocument(this));
 
-                 var i = rangeProperties.length, prop;
 
-                 while (i--) {
 
-                     prop = rangeProperties[i];
 
-                     range[prop] = this[prop];
 
-                 }
 
-                 return range;
 
-             },
 
-             toString: function() {
 
-                 assertRangeValid(this);
 
-                 var sc = this.startContainer;
 
-                 if (sc === this.endContainer && isCharacterDataNode(sc)) {
 
-                     return (sc.nodeType == 3 || sc.nodeType == 4) ? sc.data.slice(this.startOffset, this.endOffset) : "";
 
-                 } else {
 
-                     var textParts = [], iterator = new RangeIterator(this, true);
 
-                     iterateSubtree(iterator, function(node) {
 
-                         // Accept only text or CDATA nodes, not comments
 
-                         if (node.nodeType == 3 || node.nodeType == 4) {
 
-                             textParts.push(node.data);
 
-                         }
 
-                     });
 
-                     iterator.detach();
 
-                     return textParts.join("");
 
-                 }
 
-             },
 
-             // The methods below are all non-standard. The following batch were introduced by Mozilla but have since
 
-             // been removed from Mozilla.
 
-             compareNode: function(node) {
 
-                 assertRangeValid(this);
 
-                 var parent = node.parentNode;
 
-                 var nodeIndex = getNodeIndex(node);
 
-                 if (!parent) {
 
-                     throw new DOMException("NOT_FOUND_ERR");
 
-                 }
 
-                 var startComparison = this.comparePoint(parent, nodeIndex),
 
-                     endComparison = this.comparePoint(parent, nodeIndex + 1);
 
-                 if (startComparison < 0) { // Node starts before
 
-                     return (endComparison > 0) ? n_b_a : n_b;
 
-                 } else {
 
-                     return (endComparison > 0) ? n_a : n_i;
 
-                 }
 
-             },
 
-             comparePoint: function(node, offset) {
 
-                 assertRangeValid(this);
 
-                 assertNode(node, "HIERARCHY_REQUEST_ERR");
 
-                 assertSameDocumentOrFragment(node, this.startContainer);
 
-                 if (comparePoints(node, offset, this.startContainer, this.startOffset) < 0) {
 
-                     return -1;
 
-                 } else if (comparePoints(node, offset, this.endContainer, this.endOffset) > 0) {
 
-                     return 1;
 
-                 }
 
-                 return 0;
 
-             },
 
-             createContextualFragment: createContextualFragment,
 
-             toHtml: function() {
 
-                 return rangeToHtml(this);
 
-             },
 
-             // touchingIsIntersecting determines whether this method considers a node that borders a range intersects
 
-             // with it (as in WebKit) or not (as in Gecko pre-1.9, and the default)
 
-             intersectsNode: function(node, touchingIsIntersecting) {
 
-                 assertRangeValid(this);
 
-                 assertNode(node, "NOT_FOUND_ERR");
 
-                 if (getDocument(node) !== getRangeDocument(this)) {
 
-                     return false;
 
-                 }
 
-                 var parent = node.parentNode, offset = getNodeIndex(node);
 
-                 assertNode(parent, "NOT_FOUND_ERR");
 
-                 var startComparison = comparePoints(parent, offset, this.endContainer, this.endOffset),
 
-                     endComparison = comparePoints(parent, offset + 1, this.startContainer, this.startOffset);
 
-                 return touchingIsIntersecting ? startComparison <= 0 && endComparison >= 0 : startComparison < 0 && endComparison > 0;
 
-             },
 
-             isPointInRange: function(node, offset) {
 
-                 assertRangeValid(this);
 
-                 assertNode(node, "HIERARCHY_REQUEST_ERR");
 
-                 assertSameDocumentOrFragment(node, this.startContainer);
 
-                 return (comparePoints(node, offset, this.startContainer, this.startOffset) >= 0) &&
 
-                        (comparePoints(node, offset, this.endContainer, this.endOffset) <= 0);
 
-             },
 
-             // The methods below are non-standard and invented by me.
 
-             // Sharing a boundary start-to-end or end-to-start does not count as intersection.
 
-             intersectsRange: function(range) {
 
-                 return rangesIntersect(this, range, false);
 
-             },
 
-             // Sharing a boundary start-to-end or end-to-start does count as intersection.
 
-             intersectsOrTouchesRange: function(range) {
 
-                 return rangesIntersect(this, range, true);
 
-             },
 
-             intersection: function(range) {
 
-                 if (this.intersectsRange(range)) {
 
-                     var startComparison = comparePoints(this.startContainer, this.startOffset, range.startContainer, range.startOffset),
 
-                         endComparison = comparePoints(this.endContainer, this.endOffset, range.endContainer, range.endOffset);
 
-                     var intersectionRange = this.cloneRange();
 
-                     if (startComparison == -1) {
 
-                         intersectionRange.setStart(range.startContainer, range.startOffset);
 
-                     }
 
-                     if (endComparison == 1) {
 
-                         intersectionRange.setEnd(range.endContainer, range.endOffset);
 
-                     }
 
-                     return intersectionRange;
 
-                 }
 
-                 return null;
 
-             },
 
-             union: function(range) {
 
-                 if (this.intersectsOrTouchesRange(range)) {
 
-                     var unionRange = this.cloneRange();
 
-                     if (comparePoints(range.startContainer, range.startOffset, this.startContainer, this.startOffset) == -1) {
 
-                         unionRange.setStart(range.startContainer, range.startOffset);
 
-                     }
 
-                     if (comparePoints(range.endContainer, range.endOffset, this.endContainer, this.endOffset) == 1) {
 
-                         unionRange.setEnd(range.endContainer, range.endOffset);
 
-                     }
 
-                     return unionRange;
 
-                 } else {
 
-                     throw new DOMException("Ranges do not intersect");
 
-                 }
 
-             },
 
-             containsNode: function(node, allowPartial) {
 
-                 if (allowPartial) {
 
-                     return this.intersectsNode(node, false);
 
-                 } else {
 
-                     return this.compareNode(node) == n_i;
 
-                 }
 
-             },
 
-             containsNodeContents: function(node) {
 
-                 return this.comparePoint(node, 0) >= 0 && this.comparePoint(node, getNodeLength(node)) <= 0;
 
-             },
 
-             containsRange: function(range) {
 
-                 var intersection = this.intersection(range);
 
-                 return intersection !== null && range.equals(intersection);
 
-             },
 
-             containsNodeText: function(node) {
 
-                 var nodeRange = this.cloneRange();
 
-                 nodeRange.selectNode(node);
 
-                 var textNodes = nodeRange.getNodes([3]);
 
-                 if (textNodes.length > 0) {
 
-                     nodeRange.setStart(textNodes[0], 0);
 
-                     var lastTextNode = textNodes.pop();
 
-                     nodeRange.setEnd(lastTextNode, lastTextNode.length);
 
-                     return this.containsRange(nodeRange);
 
-                 } else {
 
-                     return this.containsNodeContents(node);
 
-                 }
 
-             },
 
-             getNodes: function(nodeTypes, filter) {
 
-                 assertRangeValid(this);
 
-                 return getNodesInRange(this, nodeTypes, filter);
 
-             },
 
-             getDocument: function() {
 
-                 return getRangeDocument(this);
 
-             },
 
-             collapseBefore: function(node) {
 
-                 this.setEndBefore(node);
 
-                 this.collapse(false);
 
-             },
 
-             collapseAfter: function(node) {
 
-                 this.setStartAfter(node);
 
-                 this.collapse(true);
 
-             },
 
-             
 
-             getBookmark: function(containerNode) {
 
-                 var doc = getRangeDocument(this);
 
-                 var preSelectionRange = api.createRange(doc);
 
-                 containerNode = containerNode || dom.getBody(doc);
 
-                 preSelectionRange.selectNodeContents(containerNode);
 
-                 var range = this.intersection(preSelectionRange);
 
-                 var start = 0, end = 0;
 
-                 if (range) {
 
-                     preSelectionRange.setEnd(range.startContainer, range.startOffset);
 
-                     start = preSelectionRange.toString().length;
 
-                     end = start + range.toString().length;
 
-                 }
 
-                 return {
 
-                     start: start,
 
-                     end: end,
 
-                     containerNode: containerNode
 
-                 };
 
-             },
 
-             
 
-             moveToBookmark: function(bookmark) {
 
-                 var containerNode = bookmark.containerNode;
 
-                 var charIndex = 0;
 
-                 this.setStart(containerNode, 0);
 
-                 this.collapse(true);
 
-                 var nodeStack = [containerNode], node, foundStart = false, stop = false;
 
-                 var nextCharIndex, i, childNodes;
 
-                 while (!stop && (node = nodeStack.pop())) {
 
-                     if (node.nodeType == 3) {
 
-                         nextCharIndex = charIndex + node.length;
 
-                         if (!foundStart && bookmark.start >= charIndex && bookmark.start <= nextCharIndex) {
 
-                             this.setStart(node, bookmark.start - charIndex);
 
-                             foundStart = true;
 
-                         }
 
-                         if (foundStart && bookmark.end >= charIndex && bookmark.end <= nextCharIndex) {
 
-                             this.setEnd(node, bookmark.end - charIndex);
 
-                             stop = true;
 
-                         }
 
-                         charIndex = nextCharIndex;
 
-                     } else {
 
-                         childNodes = node.childNodes;
 
-                         i = childNodes.length;
 
-                         while (i--) {
 
-                             nodeStack.push(childNodes[i]);
 
-                         }
 
-                     }
 
-                 }
 
-             },
 
-             getName: function() {
 
-                 return "DomRange";
 
-             },
 
-             equals: function(range) {
 
-                 return Range.rangesEqual(this, range);
 
-             },
 
-             isValid: function() {
 
-                 return isRangeValid(this);
 
-             },
 
-             
 
-             inspect: function() {
 
-                 return inspect(this);
 
-             },
 
-             
 
-             detach: function() {
 
-                 // In DOM4, detach() is now a no-op.
 
-             }
 
-         });
 
-         function copyComparisonConstantsToObject(obj) {
 
-             obj.START_TO_START = s2s;
 
-             obj.START_TO_END = s2e;
 
-             obj.END_TO_END = e2e;
 
-             obj.END_TO_START = e2s;
 
-             obj.NODE_BEFORE = n_b;
 
-             obj.NODE_AFTER = n_a;
 
-             obj.NODE_BEFORE_AND_AFTER = n_b_a;
 
-             obj.NODE_INSIDE = n_i;
 
-         }
 
-         function copyComparisonConstants(constructor) {
 
-             copyComparisonConstantsToObject(constructor);
 
-             copyComparisonConstantsToObject(constructor.prototype);
 
-         }
 
-         function createRangeContentRemover(remover, boundaryUpdater) {
 
-             return function() {
 
-                 assertRangeValid(this);
 
-                 var sc = this.startContainer, so = this.startOffset, root = this.commonAncestorContainer;
 
-                 var iterator = new RangeIterator(this, true);
 
-                 // Work out where to position the range after content removal
 
-                 var node, boundary;
 
-                 if (sc !== root) {
 
-                     node = getClosestAncestorIn(sc, root, true);
 
-                     boundary = getBoundaryAfterNode(node);
 
-                     sc = boundary.node;
 
-                     so = boundary.offset;
 
-                 }
 
-                 // Check none of the range is read-only
 
-                 iterateSubtree(iterator, assertNodeNotReadOnly);
 
-                 iterator.reset();
 
-                 // Remove the content
 
-                 var returnValue = remover(iterator);
 
-                 iterator.detach();
 
-                 // Move to the new position
 
-                 boundaryUpdater(this, sc, so, sc, so);
 
-                 return returnValue;
 
-             };
 
-         }
 
-         function createPrototypeRange(constructor, boundaryUpdater) {
 
-             function createBeforeAfterNodeSetter(isBefore, isStart) {
 
-                 return function(node) {
 
-                     assertValidNodeType(node, beforeAfterNodeTypes);
 
-                     assertValidNodeType(getRootContainer(node), rootContainerNodeTypes);
 
-                     var boundary = (isBefore ? getBoundaryBeforeNode : getBoundaryAfterNode)(node);
 
-                     (isStart ? setRangeStart : setRangeEnd)(this, boundary.node, boundary.offset);
 
-                 };
 
-             }
 
-             function setRangeStart(range, node, offset) {
 
-                 var ec = range.endContainer, eo = range.endOffset;
 
-                 if (node !== range.startContainer || offset !== range.startOffset) {
 
-                     // Check the root containers of the range and the new boundary, and also check whether the new boundary
 
-                     // is after the current end. In either case, collapse the range to the new position
 
-                     if (getRootContainer(node) != getRootContainer(ec) || comparePoints(node, offset, ec, eo) == 1) {
 
-                         ec = node;
 
-                         eo = offset;
 
-                     }
 
-                     boundaryUpdater(range, node, offset, ec, eo);
 
-                 }
 
-             }
 
-             function setRangeEnd(range, node, offset) {
 
-                 var sc = range.startContainer, so = range.startOffset;
 
-                 if (node !== range.endContainer || offset !== range.endOffset) {
 
-                     // Check the root containers of the range and the new boundary, and also check whether the new boundary
 
-                     // is after the current end. In either case, collapse the range to the new position
 
-                     if (getRootContainer(node) != getRootContainer(sc) || comparePoints(node, offset, sc, so) == -1) {
 
-                         sc = node;
 
-                         so = offset;
 
-                     }
 
-                     boundaryUpdater(range, sc, so, node, offset);
 
-                 }
 
-             }
 
-             // Set up inheritance
 
-             var F = function() {};
 
-             F.prototype = api.rangePrototype;
 
-             constructor.prototype = new F();
 
-             util.extend(constructor.prototype, {
 
-                 setStart: function(node, offset) {
 
-                     assertNoDocTypeNotationEntityAncestor(node, true);
 
-                     assertValidOffset(node, offset);
 
-                     setRangeStart(this, node, offset);
 
-                 },
 
-                 setEnd: function(node, offset) {
 
-                     assertNoDocTypeNotationEntityAncestor(node, true);
 
-                     assertValidOffset(node, offset);
 
-                     setRangeEnd(this, node, offset);
 
-                 },
 
-                 /**
 
-                  * Convenience method to set a range's start and end boundaries. Overloaded as follows:
 
-                  * - Two parameters (node, offset) creates a collapsed range at that position
 
-                  * - Three parameters (node, startOffset, endOffset) creates a range contained with node starting at
 
-                  *   startOffset and ending at endOffset
 
-                  * - Four parameters (startNode, startOffset, endNode, endOffset) creates a range starting at startOffset in
 
-                  *   startNode and ending at endOffset in endNode
 
-                  */
 
-                 setStartAndEnd: function() {
 
-                     var args = arguments;
 
-                     var sc = args[0], so = args[1], ec = sc, eo = so;
 
-                     switch (args.length) {
 
-                         case 3:
 
-                             eo = args[2];
 
-                             break;
 
-                         case 4:
 
-                             ec = args[2];
 
-                             eo = args[3];
 
-                             break;
 
-                     }
 
-                     boundaryUpdater(this, sc, so, ec, eo);
 
-                 },
 
-                 
 
-                 setBoundary: function(node, offset, isStart) {
 
-                     this["set" + (isStart ? "Start" : "End")](node, offset);
 
-                 },
 
-                 setStartBefore: createBeforeAfterNodeSetter(true, true),
 
-                 setStartAfter: createBeforeAfterNodeSetter(false, true),
 
-                 setEndBefore: createBeforeAfterNodeSetter(true, false),
 
-                 setEndAfter: createBeforeAfterNodeSetter(false, false),
 
-                 collapse: function(isStart) {
 
-                     assertRangeValid(this);
 
-                     if (isStart) {
 
-                         boundaryUpdater(this, this.startContainer, this.startOffset, this.startContainer, this.startOffset);
 
-                     } else {
 
-                         boundaryUpdater(this, this.endContainer, this.endOffset, this.endContainer, this.endOffset);
 
-                     }
 
-                 },
 
-                 selectNodeContents: function(node) {
 
-                     assertNoDocTypeNotationEntityAncestor(node, true);
 
-                     boundaryUpdater(this, node, 0, node, getNodeLength(node));
 
-                 },
 
-                 selectNode: function(node) {
 
-                     assertNoDocTypeNotationEntityAncestor(node, false);
 
-                     assertValidNodeType(node, beforeAfterNodeTypes);
 
-                     var start = getBoundaryBeforeNode(node), end = getBoundaryAfterNode(node);
 
-                     boundaryUpdater(this, start.node, start.offset, end.node, end.offset);
 
-                 },
 
-                 extractContents: createRangeContentRemover(extractSubtree, boundaryUpdater),
 
-                 deleteContents: createRangeContentRemover(deleteSubtree, boundaryUpdater),
 
-                 canSurroundContents: function() {
 
-                     assertRangeValid(this);
 
-                     assertNodeNotReadOnly(this.startContainer);
 
-                     assertNodeNotReadOnly(this.endContainer);
 
-                     // Check if the contents can be surrounded. Specifically, this means whether the range partially selects
 
-                     // no non-text nodes.
 
-                     var iterator = new RangeIterator(this, true);
 
-                     var boundariesInvalid = (iterator._first && isNonTextPartiallySelected(iterator._first, this) ||
 
-                             (iterator._last && isNonTextPartiallySelected(iterator._last, this)));
 
-                     iterator.detach();
 
-                     return !boundariesInvalid;
 
-                 },
 
-                 splitBoundaries: function() {
 
-                     splitRangeBoundaries(this);
 
-                 },
 
-                 splitBoundariesPreservingPositions: function(positionsToPreserve) {
 
-                     splitRangeBoundaries(this, positionsToPreserve);
 
-                 },
 
-                 normalizeBoundaries: function() {
 
-                     assertRangeValid(this);
 
-                     var sc = this.startContainer, so = this.startOffset, ec = this.endContainer, eo = this.endOffset;
 
-                     var mergeForward = function(node) {
 
-                         var sibling = node.nextSibling;
 
-                         if (sibling && sibling.nodeType == node.nodeType) {
 
-                             ec = node;
 
-                             eo = node.length;
 
-                             node.appendData(sibling.data);
 
-                             sibling.parentNode.removeChild(sibling);
 
-                         }
 
-                     };
 
-                     var mergeBackward = function(node) {
 
-                         var sibling = node.previousSibling;
 
-                         if (sibling && sibling.nodeType == node.nodeType) {
 
-                             sc = node;
 
-                             var nodeLength = node.length;
 
-                             so = sibling.length;
 
-                             node.insertData(0, sibling.data);
 
-                             sibling.parentNode.removeChild(sibling);
 
-                             if (sc == ec) {
 
-                                 eo += so;
 
-                                 ec = sc;
 
-                             } else if (ec == node.parentNode) {
 
-                                 var nodeIndex = getNodeIndex(node);
 
-                                 if (eo == nodeIndex) {
 
-                                     ec = node;
 
-                                     eo = nodeLength;
 
-                                 } else if (eo > nodeIndex) {
 
-                                     eo--;
 
-                                 }
 
-                             }
 
-                         }
 
-                     };
 
-                     var normalizeStart = true;
 
-                     if (isCharacterDataNode(ec)) {
 
-                         if (ec.length == eo) {
 
-                             mergeForward(ec);
 
-                         }
 
-                     } else {
 
-                         if (eo > 0) {
 
-                             var endNode = ec.childNodes[eo - 1];
 
-                             if (endNode && isCharacterDataNode(endNode)) {
 
-                                 mergeForward(endNode);
 
-                             }
 
-                         }
 
-                         normalizeStart = !this.collapsed;
 
-                     }
 
-                     if (normalizeStart) {
 
-                         if (isCharacterDataNode(sc)) {
 
-                             if (so == 0) {
 
-                                 mergeBackward(sc);
 
-                             }
 
-                         } else {
 
-                             if (so < sc.childNodes.length) {
 
-                                 var startNode = sc.childNodes[so];
 
-                                 if (startNode && isCharacterDataNode(startNode)) {
 
-                                     mergeBackward(startNode);
 
-                                 }
 
-                             }
 
-                         }
 
-                     } else {
 
-                         sc = ec;
 
-                         so = eo;
 
-                     }
 
-                     boundaryUpdater(this, sc, so, ec, eo);
 
-                 },
 
-                 collapseToPoint: function(node, offset) {
 
-                     assertNoDocTypeNotationEntityAncestor(node, true);
 
-                     assertValidOffset(node, offset);
 
-                     this.setStartAndEnd(node, offset);
 
-                 }
 
-             });
 
-             copyComparisonConstants(constructor);
 
-         }
 
-         /*----------------------------------------------------------------------------------------------------------------*/
 
-         // Updates commonAncestorContainer and collapsed after boundary change
 
-         function updateCollapsedAndCommonAncestor(range) {
 
-             range.collapsed = (range.startContainer === range.endContainer && range.startOffset === range.endOffset);
 
-             range.commonAncestorContainer = range.collapsed ?
 
-                 range.startContainer : dom.getCommonAncestor(range.startContainer, range.endContainer);
 
-         }
 
-         function updateBoundaries(range, startContainer, startOffset, endContainer, endOffset) {
 
-             range.startContainer = startContainer;
 
-             range.startOffset = startOffset;
 
-             range.endContainer = endContainer;
 
-             range.endOffset = endOffset;
 
-             range.document = dom.getDocument(startContainer);
 
-             updateCollapsedAndCommonAncestor(range);
 
-         }
 
-         function Range(doc) {
 
-             this.startContainer = doc;
 
-             this.startOffset = 0;
 
-             this.endContainer = doc;
 
-             this.endOffset = 0;
 
-             this.document = doc;
 
-             updateCollapsedAndCommonAncestor(this);
 
-         }
 
-         createPrototypeRange(Range, updateBoundaries);
 
-         util.extend(Range, {
 
-             rangeProperties: rangeProperties,
 
-             RangeIterator: RangeIterator,
 
-             copyComparisonConstants: copyComparisonConstants,
 
-             createPrototypeRange: createPrototypeRange,
 
-             inspect: inspect,
 
-             toHtml: rangeToHtml,
 
-             getRangeDocument: getRangeDocument,
 
-             rangesEqual: function(r1, r2) {
 
-                 return r1.startContainer === r2.startContainer &&
 
-                     r1.startOffset === r2.startOffset &&
 
-                     r1.endContainer === r2.endContainer &&
 
-                     r1.endOffset === r2.endOffset;
 
-             }
 
-         });
 
-         api.DomRange = Range;
 
-     });
 
-     /*----------------------------------------------------------------------------------------------------------------*/
 
-     // Wrappers for the browser's native DOM Range and/or TextRange implementation 
 
-     api.createCoreModule("WrappedRange", ["DomRange"], function(api, module) {
 
-         var WrappedRange, WrappedTextRange;
 
-         var dom = api.dom;
 
-         var util = api.util;
 
-         var DomPosition = dom.DomPosition;
 
-         var DomRange = api.DomRange;
 
-         var getBody = dom.getBody;
 
-         var getContentDocument = dom.getContentDocument;
 
-         var isCharacterDataNode = dom.isCharacterDataNode;
 
-         /*----------------------------------------------------------------------------------------------------------------*/
 
-         if (api.features.implementsDomRange) {
 
-             // This is a wrapper around the browser's native DOM Range. It has two aims:
 
-             // - Provide workarounds for specific browser bugs
 
-             // - provide convenient extensions, which are inherited from Rangy's DomRange
 
-             (function() {
 
-                 var rangeProto;
 
-                 var rangeProperties = DomRange.rangeProperties;
 
-                 function updateRangeProperties(range) {
 
-                     var i = rangeProperties.length, prop;
 
-                     while (i--) {
 
-                         prop = rangeProperties[i];
 
-                         range[prop] = range.nativeRange[prop];
 
-                     }
 
-                     // Fix for broken collapsed property in IE 9.
 
-                     range.collapsed = (range.startContainer === range.endContainer && range.startOffset === range.endOffset);
 
-                 }
 
-                 function updateNativeRange(range, startContainer, startOffset, endContainer, endOffset) {
 
-                     var startMoved = (range.startContainer !== startContainer || range.startOffset != startOffset);
 
-                     var endMoved = (range.endContainer !== endContainer || range.endOffset != endOffset);
 
-                     var nativeRangeDifferent = !range.equals(range.nativeRange);
 
-                     // Always set both boundaries for the benefit of IE9 (see issue 35)
 
-                     if (startMoved || endMoved || nativeRangeDifferent) {
 
-                         range.setEnd(endContainer, endOffset);
 
-                         range.setStart(startContainer, startOffset);
 
-                     }
 
-                 }
 
-                 var createBeforeAfterNodeSetter;
 
-                 WrappedRange = function(range) {
 
-                     if (!range) {
 
-                         throw module.createError("WrappedRange: Range must be specified");
 
-                     }
 
-                     this.nativeRange = range;
 
-                     updateRangeProperties(this);
 
-                 };
 
-                 DomRange.createPrototypeRange(WrappedRange, updateNativeRange);
 
-                 rangeProto = WrappedRange.prototype;
 
-                 rangeProto.selectNode = function(node) {
 
-                     this.nativeRange.selectNode(node);
 
-                     updateRangeProperties(this);
 
-                 };
 
-                 rangeProto.cloneContents = function() {
 
-                     return this.nativeRange.cloneContents();
 
-                 };
 
-                 // Due to a long-standing Firefox bug that I have not been able to find a reliable way to detect,
 
-                 // insertNode() is never delegated to the native range.
 
-                 rangeProto.surroundContents = function(node) {
 
-                     this.nativeRange.surroundContents(node);
 
-                     updateRangeProperties(this);
 
-                 };
 
-                 rangeProto.collapse = function(isStart) {
 
-                     this.nativeRange.collapse(isStart);
 
-                     updateRangeProperties(this);
 
-                 };
 
-                 rangeProto.cloneRange = function() {
 
-                     return new WrappedRange(this.nativeRange.cloneRange());
 
-                 };
 
-                 rangeProto.refresh = function() {
 
-                     updateRangeProperties(this);
 
-                 };
 
-                 rangeProto.toString = function() {
 
-                     return this.nativeRange.toString();
 
-                 };
 
-                 // Create test range and node for feature detection
 
-                 var testTextNode = document.createTextNode("test");
 
-                 getBody(document).appendChild(testTextNode);
 
-                 var range = document.createRange();
 
-                 /*--------------------------------------------------------------------------------------------------------*/
 
-                 // Test for Firefox 2 bug that prevents moving the start of a Range to a point after its current end and
 
-                 // correct for it
 
-                 range.setStart(testTextNode, 0);
 
-                 range.setEnd(testTextNode, 0);
 
-                 try {
 
-                     range.setStart(testTextNode, 1);
 
-                     rangeProto.setStart = function(node, offset) {
 
-                         this.nativeRange.setStart(node, offset);
 
-                         updateRangeProperties(this);
 
-                     };
 
-                     rangeProto.setEnd = function(node, offset) {
 
-                         this.nativeRange.setEnd(node, offset);
 
-                         updateRangeProperties(this);
 
-                     };
 
-                     createBeforeAfterNodeSetter = function(name) {
 
-                         return function(node) {
 
-                             this.nativeRange[name](node);
 
-                             updateRangeProperties(this);
 
-                         };
 
-                     };
 
-                 } catch(ex) {
 
-                     rangeProto.setStart = function(node, offset) {
 
-                         try {
 
-                             this.nativeRange.setStart(node, offset);
 
-                         } catch (ex) {
 
-                             this.nativeRange.setEnd(node, offset);
 
-                             this.nativeRange.setStart(node, offset);
 
-                         }
 
-                         updateRangeProperties(this);
 
-                     };
 
-                     rangeProto.setEnd = function(node, offset) {
 
-                         try {
 
-                             this.nativeRange.setEnd(node, offset);
 
-                         } catch (ex) {
 
-                             this.nativeRange.setStart(node, offset);
 
-                             this.nativeRange.setEnd(node, offset);
 
-                         }
 
-                         updateRangeProperties(this);
 
-                     };
 
-                     createBeforeAfterNodeSetter = function(name, oppositeName) {
 
-                         return function(node) {
 
-                             try {
 
-                                 this.nativeRange[name](node);
 
-                             } catch (ex) {
 
-                                 this.nativeRange[oppositeName](node);
 
-                                 this.nativeRange[name](node);
 
-                             }
 
-                             updateRangeProperties(this);
 
-                         };
 
-                     };
 
-                 }
 
-                 rangeProto.setStartBefore = createBeforeAfterNodeSetter("setStartBefore", "setEndBefore");
 
-                 rangeProto.setStartAfter = createBeforeAfterNodeSetter("setStartAfter", "setEndAfter");
 
-                 rangeProto.setEndBefore = createBeforeAfterNodeSetter("setEndBefore", "setStartBefore");
 
-                 rangeProto.setEndAfter = createBeforeAfterNodeSetter("setEndAfter", "setStartAfter");
 
-                 /*--------------------------------------------------------------------------------------------------------*/
 
-                 // Always use DOM4-compliant selectNodeContents implementation: it's simpler and less code than testing
 
-                 // whether the native implementation can be trusted
 
-                 rangeProto.selectNodeContents = function(node) {
 
-                     this.setStartAndEnd(node, 0, dom.getNodeLength(node));
 
-                 };
 
-                 /*--------------------------------------------------------------------------------------------------------*/
 
-                 // Test for and correct WebKit bug that has the behaviour of compareBoundaryPoints round the wrong way for
 
-                 // constants START_TO_END and END_TO_START: https://bugs.webkit.org/show_bug.cgi?id=20738
 
-                 range.selectNodeContents(testTextNode);
 
-                 range.setEnd(testTextNode, 3);
 
-                 var range2 = document.createRange();
 
-                 range2.selectNodeContents(testTextNode);
 
-                 range2.setEnd(testTextNode, 4);
 
-                 range2.setStart(testTextNode, 2);
 
-                 if (range.compareBoundaryPoints(range.START_TO_END, range2) == -1 &&
 
-                         range.compareBoundaryPoints(range.END_TO_START, range2) == 1) {
 
-                     // This is the wrong way round, so correct for it
 
-                     rangeProto.compareBoundaryPoints = function(type, range) {
 
-                         range = range.nativeRange || range;
 
-                         if (type == range.START_TO_END) {
 
-                             type = range.END_TO_START;
 
-                         } else if (type == range.END_TO_START) {
 
-                             type = range.START_TO_END;
 
-                         }
 
-                         return this.nativeRange.compareBoundaryPoints(type, range);
 
-                     };
 
-                 } else {
 
-                     rangeProto.compareBoundaryPoints = function(type, range) {
 
-                         return this.nativeRange.compareBoundaryPoints(type, range.nativeRange || range);
 
-                     };
 
-                 }
 
-                 /*--------------------------------------------------------------------------------------------------------*/
 
-                 // Test for IE 9 deleteContents() and extractContents() bug and correct it. See issue 107.
 
-                 var el = document.createElement("div");
 
-                 el.innerHTML = "123";
 
-                 var textNode = el.firstChild;
 
-                 var body = getBody(document);
 
-                 body.appendChild(el);
 
-                 range.setStart(textNode, 1);
 
-                 range.setEnd(textNode, 2);
 
-                 range.deleteContents();
 
-                 if (textNode.data == "13") {
 
-                     // Behaviour is correct per DOM4 Range so wrap the browser's implementation of deleteContents() and
 
-                     // extractContents()
 
-                     rangeProto.deleteContents = function() {
 
-                         this.nativeRange.deleteContents();
 
-                         updateRangeProperties(this);
 
-                     };
 
-                     rangeProto.extractContents = function() {
 
-                         var frag = this.nativeRange.extractContents();
 
-                         updateRangeProperties(this);
 
-                         return frag;
 
-                     };
 
-                 } else {
 
-                 }
 
-                 body.removeChild(el);
 
-                 body = null;
 
-                 /*--------------------------------------------------------------------------------------------------------*/
 
-                 // Test for existence of createContextualFragment and delegate to it if it exists
 
-                 if (util.isHostMethod(range, "createContextualFragment")) {
 
-                     rangeProto.createContextualFragment = function(fragmentStr) {
 
-                         return this.nativeRange.createContextualFragment(fragmentStr);
 
-                     };
 
-                 }
 
-                 /*--------------------------------------------------------------------------------------------------------*/
 
-                 // Clean up
 
-                 getBody(document).removeChild(testTextNode);
 
-                 rangeProto.getName = function() {
 
-                     return "WrappedRange";
 
-                 };
 
-                 api.WrappedRange = WrappedRange;
 
-                 api.createNativeRange = function(doc) {
 
-                     doc = getContentDocument(doc, module, "createNativeRange");
 
-                     return doc.createRange();
 
-                 };
 
-             })();
 
-         }
 
-         
 
-         if (api.features.implementsTextRange) {
 
-             /*
 
-             This is a workaround for a bug where IE returns the wrong container element from the TextRange's parentElement()
 
-             method. For example, in the following (where pipes denote the selection boundaries):
 
-             <ul id="ul"><li id="a">| a </li><li id="b"> b |</li></ul>
 
-             var range = document.selection.createRange();
 
-             alert(range.parentElement().id); // Should alert "ul" but alerts "b"
 
-             This method returns the common ancestor node of the following:
 
-             - the parentElement() of the textRange
 
-             - the parentElement() of the textRange after calling collapse(true)
 
-             - the parentElement() of the textRange after calling collapse(false)
 
-             */
 
-             var getTextRangeContainerElement = function(textRange) {
 
-                 var parentEl = textRange.parentElement();
 
-                 var range = textRange.duplicate();
 
-                 range.collapse(true);
 
-                 var startEl = range.parentElement();
 
-                 range = textRange.duplicate();
 
-                 range.collapse(false);
 
-                 var endEl = range.parentElement();
 
-                 var startEndContainer = (startEl == endEl) ? startEl : dom.getCommonAncestor(startEl, endEl);
 
-                 return startEndContainer == parentEl ? startEndContainer : dom.getCommonAncestor(parentEl, startEndContainer);
 
-             };
 
-             var textRangeIsCollapsed = function(textRange) {
 
-                 return textRange.compareEndPoints("StartToEnd", textRange) == 0;
 
-             };
 
-             // Gets the boundary of a TextRange expressed as a node and an offset within that node. This function started
 
-             // out as an improved version of code found in Tim Cameron Ryan's IERange (http://code.google.com/p/ierange/)
 
-             // but has grown, fixing problems with line breaks in preformatted text, adding workaround for IE TextRange
 
-             // bugs, handling for inputs and images, plus optimizations.
 
-             var getTextRangeBoundaryPosition = function(textRange, wholeRangeContainerElement, isStart, isCollapsed, startInfo) {
 
-                 var workingRange = textRange.duplicate();
 
-                 workingRange.collapse(isStart);
 
-                 var containerElement = workingRange.parentElement();
 
-                 // Sometimes collapsing a TextRange that's at the start of a text node can move it into the previous node, so
 
-                 // check for that
 
-                 if (!dom.isOrIsAncestorOf(wholeRangeContainerElement, containerElement)) {
 
-                     containerElement = wholeRangeContainerElement;
 
-                 }
 
-                 // Deal with nodes that cannot "contain rich HTML markup". In practice, this means form inputs, images and
 
-                 // similar. See http://msdn.microsoft.com/en-us/library/aa703950%28VS.85%29.aspx
 
-                 if (!containerElement.canHaveHTML) {
 
-                     var pos = new DomPosition(containerElement.parentNode, dom.getNodeIndex(containerElement));
 
-                     return {
 
-                         boundaryPosition: pos,
 
-                         nodeInfo: {
 
-                             nodeIndex: pos.offset,
 
-                             containerElement: pos.node
 
-                         }
 
-                     };
 
-                 }
 
-                 var workingNode = dom.getDocument(containerElement).createElement("span");
 
-                 // Workaround for HTML5 Shiv's insane violation of document.createElement(). See Rangy issue 104 and HTML5
 
-                 // Shiv issue 64: https://github.com/aFarkas/html5shiv/issues/64
 
-                 if (workingNode.parentNode) {
 
-                     workingNode.parentNode.removeChild(workingNode);
 
-                 }
 
-                 var comparison, workingComparisonType = isStart ? "StartToStart" : "StartToEnd";
 
-                 var previousNode, nextNode, boundaryPosition, boundaryNode;
 
-                 var start = (startInfo && startInfo.containerElement == containerElement) ? startInfo.nodeIndex : 0;
 
-                 var childNodeCount = containerElement.childNodes.length;
 
-                 var end = childNodeCount;
 
-                 // Check end first. Code within the loop assumes that the endth child node of the container is definitely
 
-                 // after the range boundary.
 
-                 var nodeIndex = end;
 
-                 while (true) {
 
-                     if (nodeIndex == childNodeCount) {
 
-                         containerElement.appendChild(workingNode);
 
-                     } else {
 
-                         containerElement.insertBefore(workingNode, containerElement.childNodes[nodeIndex]);
 
-                     }
 
-                     workingRange.moveToElementText(workingNode);
 
-                     comparison = workingRange.compareEndPoints(workingComparisonType, textRange);
 
-                     if (comparison == 0 || start == end) {
 
-                         break;
 
-                     } else if (comparison == -1) {
 
-                         if (end == start + 1) {
 
-                             // We know the endth child node is after the range boundary, so we must be done.
 
-                             break;
 
-                         } else {
 
-                             start = nodeIndex;
 
-                         }
 
-                     } else {
 
-                         end = (end == start + 1) ? start : nodeIndex;
 
-                     }
 
-                     nodeIndex = Math.floor((start + end) / 2);
 
-                     containerElement.removeChild(workingNode);
 
-                 }
 
-                 // We've now reached or gone past the boundary of the text range we're interested in
 
-                 // so have identified the node we want
 
-                 boundaryNode = workingNode.nextSibling;
 
-                 if (comparison == -1 && boundaryNode && isCharacterDataNode(boundaryNode)) {
 
-                     // This is a character data node (text, comment, cdata). The working range is collapsed at the start of
 
-                     // the node containing the text range's boundary, so we move the end of the working range to the
 
-                     // boundary point and measure the length of its text to get the boundary's offset within the node.
 
-                     workingRange.setEndPoint(isStart ? "EndToStart" : "EndToEnd", textRange);
 
-                     var offset;
 
-                     if (/[\r\n]/.test(boundaryNode.data)) {
 
-                         /*
 
-                         For the particular case of a boundary within a text node containing rendered line breaks (within a
 
-                         <pre> element, for example), we need a slightly complicated approach to get the boundary's offset in
 
-                         IE. The facts:
 
-                         
 
-                         - Each line break is represented as \r in the text node's data/nodeValue properties
 
-                         - Each line break is represented as \r\n in the TextRange's 'text' property
 
-                         - The 'text' property of the TextRange does not contain trailing line breaks
 
-                         
 
-                         To get round the problem presented by the final fact above, we can use the fact that TextRange's
 
-                         moveStart() and moveEnd() methods return the actual number of characters moved, which is not
 
-                         necessarily the same as the number of characters it was instructed to move. The simplest approach is
 
-                         to use this to store the characters moved when moving both the start and end of the range to the
 
-                         start of the document body and subtracting the start offset from the end offset (the
 
-                         "move-negative-gazillion" method). However, this is extremely slow when the document is large and
 
-                         the range is near the end of it. Clearly doing the mirror image (i.e. moving the range boundaries to
 
-                         the end of the document) has the same problem.
 
-                         
 
-                         Another approach that works is to use moveStart() to move the start boundary of the range up to the
 
-                         end boundary one character at a time and incrementing a counter with the value returned by the
 
-                         moveStart() call. However, the check for whether the start boundary has reached the end boundary is
 
-                         expensive, so this method is slow (although unlike "move-negative-gazillion" is largely unaffected
 
-                         by the location of the range within the document).
 
-                         
 
-                         The approach used below is a hybrid of the two methods above. It uses the fact that a string
 
-                         containing the TextRange's 'text' property with each \r\n converted to a single \r character cannot
 
-                         be longer than the text of the TextRange, so the start of the range is moved that length initially
 
-                         and then a character at a time to make up for any trailing line breaks not contained in the 'text'
 
-                         property. This has good performance in most situations compared to the previous two methods.
 
-                         */
 
-                         var tempRange = workingRange.duplicate();
 
-                         var rangeLength = tempRange.text.replace(/\r\n/g, "\r").length;
 
-                         offset = tempRange.moveStart("character", rangeLength);
 
-                         while ( (comparison = tempRange.compareEndPoints("StartToEnd", tempRange)) == -1) {
 
-                             offset++;
 
-                             tempRange.moveStart("character", 1);
 
-                         }
 
-                     } else {
 
-                         offset = workingRange.text.length;
 
-                     }
 
-                     boundaryPosition = new DomPosition(boundaryNode, offset);
 
-                 } else {
 
-                     // If the boundary immediately follows a character data node and this is the end boundary, we should favour
 
-                     // a position within that, and likewise for a start boundary preceding a character data node
 
-                     previousNode = (isCollapsed || !isStart) && workingNode.previousSibling;
 
-                     nextNode = (isCollapsed || isStart) && workingNode.nextSibling;
 
-                     if (nextNode && isCharacterDataNode(nextNode)) {
 
-                         boundaryPosition = new DomPosition(nextNode, 0);
 
-                     } else if (previousNode && isCharacterDataNode(previousNode)) {
 
-                         boundaryPosition = new DomPosition(previousNode, previousNode.data.length);
 
-                     } else {
 
-                         boundaryPosition = new DomPosition(containerElement, dom.getNodeIndex(workingNode));
 
-                     }
 
-                 }
 
-                 // Clean up
 
-                 workingNode.parentNode.removeChild(workingNode);
 
-                 return {
 
-                     boundaryPosition: boundaryPosition,
 
-                     nodeInfo: {
 
-                         nodeIndex: nodeIndex,
 
-                         containerElement: containerElement
 
-                     }
 
-                 };
 
-             };
 
-             // Returns a TextRange representing the boundary of a TextRange expressed as a node and an offset within that
 
-             // node. This function started out as an optimized version of code found in Tim Cameron Ryan's IERange
 
-             // (http://code.google.com/p/ierange/)
 
-             var createBoundaryTextRange = function(boundaryPosition, isStart) {
 
-                 var boundaryNode, boundaryParent, boundaryOffset = boundaryPosition.offset;
 
-                 var doc = dom.getDocument(boundaryPosition.node);
 
-                 var workingNode, childNodes, workingRange = getBody(doc).createTextRange();
 
-                 var nodeIsDataNode = isCharacterDataNode(boundaryPosition.node);
 
-                 if (nodeIsDataNode) {
 
-                     boundaryNode = boundaryPosition.node;
 
-                     boundaryParent = boundaryNode.parentNode;
 
-                 } else {
 
-                     childNodes = boundaryPosition.node.childNodes;
 
-                     boundaryNode = (boundaryOffset < childNodes.length) ? childNodes[boundaryOffset] : null;
 
-                     boundaryParent = boundaryPosition.node;
 
-                 }
 
-                 // Position the range immediately before the node containing the boundary
 
-                 workingNode = doc.createElement("span");
 
-                 // Making the working element non-empty element persuades IE to consider the TextRange boundary to be within
 
-                 // the element rather than immediately before or after it
 
-                 workingNode.innerHTML = "&#feff;";
 
-                 // insertBefore is supposed to work like appendChild if the second parameter is null. However, a bug report
 
-                 // for IERange suggests that it can crash the browser: http://code.google.com/p/ierange/issues/detail?id=12
 
-                 if (boundaryNode) {
 
-                     boundaryParent.insertBefore(workingNode, boundaryNode);
 
-                 } else {
 
-                     boundaryParent.appendChild(workingNode);
 
-                 }
 
-                 workingRange.moveToElementText(workingNode);
 
-                 workingRange.collapse(!isStart);
 
-                 // Clean up
 
-                 boundaryParent.removeChild(workingNode);
 
-                 // Move the working range to the text offset, if required
 
-                 if (nodeIsDataNode) {
 
-                     workingRange[isStart ? "moveStart" : "moveEnd"]("character", boundaryOffset);
 
-                 }
 
-                 return workingRange;
 
-             };
 
-             /*------------------------------------------------------------------------------------------------------------*/
 
-             // This is a wrapper around a TextRange, providing full DOM Range functionality using rangy's DomRange as a
 
-             // prototype
 
-             WrappedTextRange = function(textRange) {
 
-                 this.textRange = textRange;
 
-                 this.refresh();
 
-             };
 
-             WrappedTextRange.prototype = new DomRange(document);
 
-             WrappedTextRange.prototype.refresh = function() {
 
-                 var start, end, startBoundary;
 
-                 // TextRange's parentElement() method cannot be trusted. getTextRangeContainerElement() works around that.
 
-                 var rangeContainerElement = getTextRangeContainerElement(this.textRange);
 
-                 if (textRangeIsCollapsed(this.textRange)) {
 
-                     end = start = getTextRangeBoundaryPosition(this.textRange, rangeContainerElement, true,
 
-                         true).boundaryPosition;
 
-                 } else {
 
-                     startBoundary = getTextRangeBoundaryPosition(this.textRange, rangeContainerElement, true, false);
 
-                     start = startBoundary.boundaryPosition;
 
-                     // An optimization used here is that if the start and end boundaries have the same parent element, the
 
-                     // search scope for the end boundary can be limited to exclude the portion of the element that precedes
 
-                     // the start boundary
 
-                     end = getTextRangeBoundaryPosition(this.textRange, rangeContainerElement, false, false,
 
-                         startBoundary.nodeInfo).boundaryPosition;
 
-                 }
 
-                 this.setStart(start.node, start.offset);
 
-                 this.setEnd(end.node, end.offset);
 
-             };
 
-             WrappedTextRange.prototype.getName = function() {
 
-                 return "WrappedTextRange";
 
-             };
 
-             DomRange.copyComparisonConstants(WrappedTextRange);
 
-             var rangeToTextRange = function(range) {
 
-                 if (range.collapsed) {
 
-                     return createBoundaryTextRange(new DomPosition(range.startContainer, range.startOffset), true);
 
-                 } else {
 
-                     var startRange = createBoundaryTextRange(new DomPosition(range.startContainer, range.startOffset), true);
 
-                     var endRange = createBoundaryTextRange(new DomPosition(range.endContainer, range.endOffset), false);
 
-                     var textRange = getBody( DomRange.getRangeDocument(range) ).createTextRange();
 
-                     textRange.setEndPoint("StartToStart", startRange);
 
-                     textRange.setEndPoint("EndToEnd", endRange);
 
-                     return textRange;
 
-                 }
 
-             };
 
-             WrappedTextRange.rangeToTextRange = rangeToTextRange;
 
-             WrappedTextRange.prototype.toTextRange = function() {
 
-                 return rangeToTextRange(this);
 
-             };
 
-             api.WrappedTextRange = WrappedTextRange;
 
-             // IE 9 and above have both implementations and Rangy makes both available. The next few lines sets which
 
-             // implementation to use by default.
 
-             if (!api.features.implementsDomRange || api.config.preferTextRange) {
 
-                 // Add WrappedTextRange as the Range property of the global object to allow expression like Range.END_TO_END to work
 
-                 var globalObj = (function() { return this; })();
 
-                 if (typeof globalObj.Range == "undefined") {
 
-                     globalObj.Range = WrappedTextRange;
 
-                 }
 
-                 api.createNativeRange = function(doc) {
 
-                     doc = getContentDocument(doc, module, "createNativeRange");
 
-                     return getBody(doc).createTextRange();
 
-                 };
 
-                 api.WrappedRange = WrappedTextRange;
 
-             }
 
-         }
 
-         api.createRange = function(doc) {
 
-             doc = getContentDocument(doc, module, "createRange");
 
-             return new api.WrappedRange(api.createNativeRange(doc));
 
-         };
 
-         api.createRangyRange = function(doc) {
 
-             doc = getContentDocument(doc, module, "createRangyRange");
 
-             return new DomRange(doc);
 
-         };
 
-         api.createIframeRange = function(iframeEl) {
 
-             module.deprecationNotice("createIframeRange()", "createRange(iframeEl)");
 
-             return api.createRange(iframeEl);
 
-         };
 
-         api.createIframeRangyRange = function(iframeEl) {
 
-             module.deprecationNotice("createIframeRangyRange()", "createRangyRange(iframeEl)");
 
-             return api.createRangyRange(iframeEl);
 
-         };
 
-         api.addShimListener(function(win) {
 
-             var doc = win.document;
 
-             if (typeof doc.createRange == "undefined") {
 
-                 doc.createRange = function() {
 
-                     return api.createRange(doc);
 
-                 };
 
-             }
 
-             doc = win = null;
 
-         });
 
-     });
 
-     /*----------------------------------------------------------------------------------------------------------------*/
 
-     // This module creates a selection object wrapper that conforms as closely as possible to the Selection specification
 
-     // in the HTML Editing spec (http://dvcs.w3.org/hg/editing/raw-file/tip/editing.html#selections)
 
-     api.createCoreModule("WrappedSelection", ["DomRange", "WrappedRange"], function(api, module) {
 
-         api.config.checkSelectionRanges = true;
 
-         var BOOLEAN = "boolean";
 
-         var NUMBER = "number";
 
-         var dom = api.dom;
 
-         var util = api.util;
 
-         var isHostMethod = util.isHostMethod;
 
-         var DomRange = api.DomRange;
 
-         var WrappedRange = api.WrappedRange;
 
-         var DOMException = api.DOMException;
 
-         var DomPosition = dom.DomPosition;
 
-         var getNativeSelection;
 
-         var selectionIsCollapsed;
 
-         var features = api.features;
 
-         var CONTROL = "Control";
 
-         var getDocument = dom.getDocument;
 
-         var getBody = dom.getBody;
 
-         var rangesEqual = DomRange.rangesEqual;
 
-         // Utility function to support direction parameters in the API that may be a string ("backward" or "forward") or a
 
-         // Boolean (true for backwards).
 
-         function isDirectionBackward(dir) {
 
-             return (typeof dir == "string") ? /^backward(s)?$/i.test(dir) : !!dir;
 
-         }
 
-         function getWindow(win, methodName) {
 
-             if (!win) {
 
-                 return window;
 
-             } else if (dom.isWindow(win)) {
 
-                 return win;
 
-             } else if (win instanceof WrappedSelection) {
 
-                 return win.win;
 
-             } else {
 
-                 var doc = dom.getContentDocument(win, module, methodName);
 
-                 return dom.getWindow(doc);
 
-             }
 
-         }
 
-         function getWinSelection(winParam) {
 
-             return getWindow(winParam, "getWinSelection").getSelection();
 
-         }
 
-         function getDocSelection(winParam) {
 
-             return getWindow(winParam, "getDocSelection").document.selection;
 
-         }
 
-         
 
-         function winSelectionIsBackward(sel) {
 
-             var backward = false;
 
-             if (sel.anchorNode) {
 
-                 backward = (dom.comparePoints(sel.anchorNode, sel.anchorOffset, sel.focusNode, sel.focusOffset) == 1);
 
-             }
 
-             return backward;
 
-         }
 
-         // Test for the Range/TextRange and Selection features required
 
-         // Test for ability to retrieve selection
 
-         var implementsWinGetSelection = isHostMethod(window, "getSelection"),
 
-             implementsDocSelection = util.isHostObject(document, "selection");
 
-         features.implementsWinGetSelection = implementsWinGetSelection;
 
-         features.implementsDocSelection = implementsDocSelection;
 
-         var useDocumentSelection = implementsDocSelection && (!implementsWinGetSelection || api.config.preferTextRange);
 
-         if (useDocumentSelection) {
 
-             getNativeSelection = getDocSelection;
 
-             api.isSelectionValid = function(winParam) {
 
-                 var doc = getWindow(winParam, "isSelectionValid").document, nativeSel = doc.selection;
 
-                 // Check whether the selection TextRange is actually contained within the correct document
 
-                 return (nativeSel.type != "None" || getDocument(nativeSel.createRange().parentElement()) == doc);
 
-             };
 
-         } else if (implementsWinGetSelection) {
 
-             getNativeSelection = getWinSelection;
 
-             api.isSelectionValid = function() {
 
-                 return true;
 
-             };
 
-         } else {
 
-             module.fail("Neither document.selection or window.getSelection() detected.");
 
-         }
 
-         api.getNativeSelection = getNativeSelection;
 
-         var testSelection = getNativeSelection();
 
-         var testRange = api.createNativeRange(document);
 
-         var body = getBody(document);
 
-         // Obtaining a range from a selection
 
-         var selectionHasAnchorAndFocus = util.areHostProperties(testSelection,
 
-             ["anchorNode", "focusNode", "anchorOffset", "focusOffset"]);
 
-         features.selectionHasAnchorAndFocus = selectionHasAnchorAndFocus;
 
-         // Test for existence of native selection extend() method
 
-         var selectionHasExtend = isHostMethod(testSelection, "extend");
 
-         features.selectionHasExtend = selectionHasExtend;
 
-         
 
-         // Test if rangeCount exists
 
-         var selectionHasRangeCount = (typeof testSelection.rangeCount == NUMBER);
 
-         features.selectionHasRangeCount = selectionHasRangeCount;
 
-         var selectionSupportsMultipleRanges = false;
 
-         var collapsedNonEditableSelectionsSupported = true;
 
-         var addRangeBackwardToNative = selectionHasExtend ?
 
-             function(nativeSelection, range) {
 
-                 var doc = DomRange.getRangeDocument(range);
 
-                 var endRange = api.createRange(doc);
 
-                 endRange.collapseToPoint(range.endContainer, range.endOffset);
 
-                 nativeSelection.addRange(getNativeRange(endRange));
 
-                 nativeSelection.extend(range.startContainer, range.startOffset);
 
-             } : null;
 
-         if (util.areHostMethods(testSelection, ["addRange", "getRangeAt", "removeAllRanges"]) &&
 
-                 typeof testSelection.rangeCount == NUMBER && features.implementsDomRange) {
 
-             (function() {
 
-                 // Previously an iframe was used but this caused problems in some circumstances in IE, so tests are
 
-                 // performed on the current document's selection. See issue 109.
 
-                 // Note also that if a selection previously existed, it is wiped by these tests. This should usually be fine
 
-                 // because initialization usually happens when the document loads, but could be a problem for a script that
 
-                 // loads and initializes Rangy later. If anyone complains, code could be added to save and restore the
 
-                 // selection.
 
-                 var sel = window.getSelection();
 
-                 if (sel) {
 
-                     // Store the current selection
 
-                     var originalSelectionRangeCount = sel.rangeCount;
 
-                     var selectionHasMultipleRanges = (originalSelectionRangeCount > 1);
 
-                     var originalSelectionRanges = [];
 
-                     var originalSelectionBackward = winSelectionIsBackward(sel); 
 
-                     for (var i = 0; i < originalSelectionRangeCount; ++i) {
 
-                         originalSelectionRanges[i] = sel.getRangeAt(i);
 
-                     }
 
-                     
 
-                     // Create some test elements
 
-                     var body = getBody(document);
 
-                     var testEl = body.appendChild( document.createElement("div") );
 
-                     testEl.contentEditable = "false";
 
-                     var textNode = testEl.appendChild( document.createTextNode("\u00a0\u00a0\u00a0") );
 
-                     // Test whether the native selection will allow a collapsed selection within a non-editable element
 
-                     var r1 = document.createRange();
 
-                     r1.setStart(textNode, 1);
 
-                     r1.collapse(true);
 
-                     sel.addRange(r1);
 
-                     collapsedNonEditableSelectionsSupported = (sel.rangeCount == 1);
 
-                     sel.removeAllRanges();
 
-                     // Test whether the native selection is capable of supporting multiple ranges.
 
-                     if (!selectionHasMultipleRanges) {
 
-                         // Doing the original feature test here in Chrome 36 (and presumably later versions) prints a
 
-                         // console error of "Discontiguous selection is not supported." that cannot be suppressed. There's
 
-                         // nothing we can do about this while retaining the feature test so we have to resort to a browser
 
-                         // sniff. I'm not happy about it. See
 
-                         // https://code.google.com/p/chromium/issues/detail?id=399791
 
-                         var chromeMatch = window.navigator.appVersion.match(/Chrome\/(.*?) /);
 
-                         if (chromeMatch && parseInt(chromeMatch[1]) >= 36) {
 
-                             selectionSupportsMultipleRanges = false;
 
-                         } else {
 
-                             var r2 = r1.cloneRange();
 
-                             r1.setStart(textNode, 0);
 
-                             r2.setEnd(textNode, 3);
 
-                             r2.setStart(textNode, 2);
 
-                             sel.addRange(r1);
 
-                             sel.addRange(r2);
 
-                             selectionSupportsMultipleRanges = (sel.rangeCount == 2);
 
-                         }
 
-                     }
 
-                     // Clean up
 
-                     body.removeChild(testEl);
 
-                     sel.removeAllRanges();
 
-                     for (i = 0; i < originalSelectionRangeCount; ++i) {
 
-                         if (i == 0 && originalSelectionBackward) {
 
-                             if (addRangeBackwardToNative) {
 
-                                 addRangeBackwardToNative(sel, originalSelectionRanges[i]);
 
-                             } else {
 
-                                 api.warn("Rangy initialization: original selection was backwards but selection has been restored forwards because the browser does not support Selection.extend");
 
-                                 sel.addRange(originalSelectionRanges[i]);
 
-                             }
 
-                         } else {
 
-                             sel.addRange(originalSelectionRanges[i]);
 
-                         }
 
-                     }
 
-                 }
 
-             })();
 
-         }
 
-         features.selectionSupportsMultipleRanges = selectionSupportsMultipleRanges;
 
-         features.collapsedNonEditableSelectionsSupported = collapsedNonEditableSelectionsSupported;
 
-         // ControlRanges
 
-         var implementsControlRange = false, testControlRange;
 
-         if (body && isHostMethod(body, "createControlRange")) {
 
-             testControlRange = body.createControlRange();
 
-             if (util.areHostProperties(testControlRange, ["item", "add"])) {
 
-                 implementsControlRange = true;
 
-             }
 
-         }
 
-         features.implementsControlRange = implementsControlRange;
 
-         // Selection collapsedness
 
-         if (selectionHasAnchorAndFocus) {
 
-             selectionIsCollapsed = function(sel) {
 
-                 return sel.anchorNode === sel.focusNode && sel.anchorOffset === sel.focusOffset;
 
-             };
 
-         } else {
 
-             selectionIsCollapsed = function(sel) {
 
-                 return sel.rangeCount ? sel.getRangeAt(sel.rangeCount - 1).collapsed : false;
 
-             };
 
-         }
 
-         function updateAnchorAndFocusFromRange(sel, range, backward) {
 
-             var anchorPrefix = backward ? "end" : "start", focusPrefix = backward ? "start" : "end";
 
-             sel.anchorNode = range[anchorPrefix + "Container"];
 
-             sel.anchorOffset = range[anchorPrefix + "Offset"];
 
-             sel.focusNode = range[focusPrefix + "Container"];
 
-             sel.focusOffset = range[focusPrefix + "Offset"];
 
-         }
 
-         function updateAnchorAndFocusFromNativeSelection(sel) {
 
-             var nativeSel = sel.nativeSelection;
 
-             sel.anchorNode = nativeSel.anchorNode;
 
-             sel.anchorOffset = nativeSel.anchorOffset;
 
-             sel.focusNode = nativeSel.focusNode;
 
-             sel.focusOffset = nativeSel.focusOffset;
 
-         }
 
-         function updateEmptySelection(sel) {
 
-             sel.anchorNode = sel.focusNode = null;
 
-             sel.anchorOffset = sel.focusOffset = 0;
 
-             sel.rangeCount = 0;
 
-             sel.isCollapsed = true;
 
-             sel._ranges.length = 0;
 
-         }
 
-         function getNativeRange(range) {
 
-             var nativeRange;
 
-             if (range instanceof DomRange) {
 
-                 nativeRange = api.createNativeRange(range.getDocument());
 
-                 nativeRange.setEnd(range.endContainer, range.endOffset);
 
-                 nativeRange.setStart(range.startContainer, range.startOffset);
 
-             } else if (range instanceof WrappedRange) {
 
-                 nativeRange = range.nativeRange;
 
-             } else if (features.implementsDomRange && (range instanceof dom.getWindow(range.startContainer).Range)) {
 
-                 nativeRange = range;
 
-             }
 
-             return nativeRange;
 
-         }
 
-         function rangeContainsSingleElement(rangeNodes) {
 
-             if (!rangeNodes.length || rangeNodes[0].nodeType != 1) {
 
-                 return false;
 
-             }
 
-             for (var i = 1, len = rangeNodes.length; i < len; ++i) {
 
-                 if (!dom.isAncestorOf(rangeNodes[0], rangeNodes[i])) {
 
-                     return false;
 
-                 }
 
-             }
 
-             return true;
 
-         }
 
-         function getSingleElementFromRange(range) {
 
-             var nodes = range.getNodes();
 
-             if (!rangeContainsSingleElement(nodes)) {
 
-                 throw module.createError("getSingleElementFromRange: range " + range.inspect() + " did not consist of a single element");
 
-             }
 
-             return nodes[0];
 
-         }
 
-         // Simple, quick test which only needs to distinguish between a TextRange and a ControlRange
 
-         function isTextRange(range) {
 
-             return !!range && typeof range.text != "undefined";
 
-         }
 
-         function updateFromTextRange(sel, range) {
 
-             // Create a Range from the selected TextRange
 
-             var wrappedRange = new WrappedRange(range);
 
-             sel._ranges = [wrappedRange];
 
-             updateAnchorAndFocusFromRange(sel, wrappedRange, false);
 
-             sel.rangeCount = 1;
 
-             sel.isCollapsed = wrappedRange.collapsed;
 
-         }
 
-         function updateControlSelection(sel) {
 
-             // Update the wrapped selection based on what's now in the native selection
 
-             sel._ranges.length = 0;
 
-             if (sel.docSelection.type == "None") {
 
-                 updateEmptySelection(sel);
 
-             } else {
 
-                 var controlRange = sel.docSelection.createRange();
 
-                 if (isTextRange(controlRange)) {
 
-                     // This case (where the selection type is "Control" and calling createRange() on the selection returns
 
-                     // a TextRange) can happen in IE 9. It happens, for example, when all elements in the selected
 
-                     // ControlRange have been removed from the ControlRange and removed from the document.
 
-                     updateFromTextRange(sel, controlRange);
 
-                 } else {
 
-                     sel.rangeCount = controlRange.length;
 
-                     var range, doc = getDocument(controlRange.item(0));
 
-                     for (var i = 0; i < sel.rangeCount; ++i) {
 
-                         range = api.createRange(doc);
 
-                         range.selectNode(controlRange.item(i));
 
-                         sel._ranges.push(range);
 
-                     }
 
-                     sel.isCollapsed = sel.rangeCount == 1 && sel._ranges[0].collapsed;
 
-                     updateAnchorAndFocusFromRange(sel, sel._ranges[sel.rangeCount - 1], false);
 
-                 }
 
-             }
 
-         }
 
-         function addRangeToControlSelection(sel, range) {
 
-             var controlRange = sel.docSelection.createRange();
 
-             var rangeElement = getSingleElementFromRange(range);
 
-             // Create a new ControlRange containing all the elements in the selected ControlRange plus the element
 
-             // contained by the supplied range
 
-             var doc = getDocument(controlRange.item(0));
 
-             var newControlRange = getBody(doc).createControlRange();
 
-             for (var i = 0, len = controlRange.length; i < len; ++i) {
 
-                 newControlRange.add(controlRange.item(i));
 
-             }
 
-             try {
 
-                 newControlRange.add(rangeElement);
 
-             } catch (ex) {
 
-                 throw module.createError("addRange(): Element within the specified Range could not be added to control selection (does it have layout?)");
 
-             }
 
-             newControlRange.select();
 
-             // Update the wrapped selection based on what's now in the native selection
 
-             updateControlSelection(sel);
 
-         }
 
-         var getSelectionRangeAt;
 
-         if (isHostMethod(testSelection, "getRangeAt")) {
 
-             // try/catch is present because getRangeAt() must have thrown an error in some browser and some situation.
 
-             // Unfortunately, I didn't write a comment about the specifics and am now scared to take it out. Let that be a
 
-             // lesson to us all, especially me.
 
-             getSelectionRangeAt = function(sel, index) {
 
-                 try {
 
-                     return sel.getRangeAt(index);
 
-                 } catch (ex) {
 
-                     return null;
 
-                 }
 
-             };
 
-         } else if (selectionHasAnchorAndFocus) {
 
-             getSelectionRangeAt = function(sel) {
 
-                 var doc = getDocument(sel.anchorNode);
 
-                 var range = api.createRange(doc);
 
-                 range.setStartAndEnd(sel.anchorNode, sel.anchorOffset, sel.focusNode, sel.focusOffset);
 
-                 // Handle the case when the selection was selected backwards (from the end to the start in the
 
-                 // document)
 
-                 if (range.collapsed !== this.isCollapsed) {
 
-                     range.setStartAndEnd(sel.focusNode, sel.focusOffset, sel.anchorNode, sel.anchorOffset);
 
-                 }
 
-                 return range;
 
-             };
 
-         }
 
-         function WrappedSelection(selection, docSelection, win) {
 
-             this.nativeSelection = selection;
 
-             this.docSelection = docSelection;
 
-             this._ranges = [];
 
-             this.win = win;
 
-             this.refresh();
 
-         }
 
-         WrappedSelection.prototype = api.selectionPrototype;
 
-         function deleteProperties(sel) {
 
-             sel.win = sel.anchorNode = sel.focusNode = sel._ranges = null;
 
-             sel.rangeCount = sel.anchorOffset = sel.focusOffset = 0;
 
-             sel.detached = true;
 
-         }
 
-         var cachedRangySelections = [];
 
-         function actOnCachedSelection(win, action) {
 
-             var i = cachedRangySelections.length, cached, sel;
 
-             while (i--) {
 
-                 cached = cachedRangySelections[i];
 
-                 sel = cached.selection;
 
-                 if (action == "deleteAll") {
 
-                     deleteProperties(sel);
 
-                 } else if (cached.win == win) {
 
-                     if (action == "delete") {
 
-                         cachedRangySelections.splice(i, 1);
 
-                         return true;
 
-                     } else {
 
-                         return sel;
 
-                     }
 
-                 }
 
-             }
 
-             if (action == "deleteAll") {
 
-                 cachedRangySelections.length = 0;
 
-             }
 
-             return null;
 
-         }
 
-         var getSelection = function(win) {
 
-             // Check if the parameter is a Rangy Selection object
 
-             if (win && win instanceof WrappedSelection) {
 
-                 win.refresh();
 
-                 return win;
 
-             }
 
-             win = getWindow(win, "getNativeSelection");
 
-             var sel = actOnCachedSelection(win);
 
-             var nativeSel = getNativeSelection(win), docSel = implementsDocSelection ? getDocSelection(win) : null;
 
-             if (sel) {
 
-                 sel.nativeSelection = nativeSel;
 
-                 sel.docSelection = docSel;
 
-                 sel.refresh();
 
-             } else {
 
-                 sel = new WrappedSelection(nativeSel, docSel, win);
 
-                 cachedRangySelections.push( { win: win, selection: sel } );
 
-             }
 
-             return sel;
 
-         };
 
-         api.getSelection = getSelection;
 
-         api.getIframeSelection = function(iframeEl) {
 
-             module.deprecationNotice("getIframeSelection()", "getSelection(iframeEl)");
 
-             return api.getSelection(dom.getIframeWindow(iframeEl));
 
-         };
 
-         var selProto = WrappedSelection.prototype;
 
-         function createControlSelection(sel, ranges) {
 
-             // Ensure that the selection becomes of type "Control"
 
-             var doc = getDocument(ranges[0].startContainer);
 
-             var controlRange = getBody(doc).createControlRange();
 
-             for (var i = 0, el, len = ranges.length; i < len; ++i) {
 
-                 el = getSingleElementFromRange(ranges[i]);
 
-                 try {
 
-                     controlRange.add(el);
 
-                 } catch (ex) {
 
-                     throw module.createError("setRanges(): Element within one of the specified Ranges could not be added to control selection (does it have layout?)");
 
-                 }
 
-             }
 
-             controlRange.select();
 
-             // Update the wrapped selection based on what's now in the native selection
 
-             updateControlSelection(sel);
 
-         }
 
-         // Selecting a range
 
-         if (!useDocumentSelection && selectionHasAnchorAndFocus && util.areHostMethods(testSelection, ["removeAllRanges", "addRange"])) {
 
-             selProto.removeAllRanges = function() {
 
-                 this.nativeSelection.removeAllRanges();
 
-                 updateEmptySelection(this);
 
-             };
 
-             var addRangeBackward = function(sel, range) {
 
-                 addRangeBackwardToNative(sel.nativeSelection, range);
 
-                 sel.refresh();
 
-             };
 
-             if (selectionHasRangeCount) {
 
-                 selProto.addRange = function(range, direction) {
 
-                     if (implementsControlRange && implementsDocSelection && this.docSelection.type == CONTROL) {
 
-                         addRangeToControlSelection(this, range);
 
-                     } else {
 
-                         if (isDirectionBackward(direction) && selectionHasExtend) {
 
-                             addRangeBackward(this, range);
 
-                         } else {
 
-                             var previousRangeCount;
 
-                             if (selectionSupportsMultipleRanges) {
 
-                                 previousRangeCount = this.rangeCount;
 
-                             } else {
 
-                                 this.removeAllRanges();
 
-                                 previousRangeCount = 0;
 
-                             }
 
-                             // Clone the native range so that changing the selected range does not affect the selection.
 
-                             // This is contrary to the spec but is the only way to achieve consistency between browsers. See
 
-                             // issue 80.
 
-                             this.nativeSelection.addRange(getNativeRange(range).cloneRange());
 
-                             // Check whether adding the range was successful
 
-                             this.rangeCount = this.nativeSelection.rangeCount;
 
-                             if (this.rangeCount == previousRangeCount + 1) {
 
-                                 // The range was added successfully
 
-                                 // Check whether the range that we added to the selection is reflected in the last range extracted from
 
-                                 // the selection
 
-                                 if (api.config.checkSelectionRanges) {
 
-                                     var nativeRange = getSelectionRangeAt(this.nativeSelection, this.rangeCount - 1);
 
-                                     if (nativeRange && !rangesEqual(nativeRange, range)) {
 
-                                         // Happens in WebKit with, for example, a selection placed at the start of a text node
 
-                                         range = new WrappedRange(nativeRange);
 
-                                     }
 
-                                 }
 
-                                 this._ranges[this.rangeCount - 1] = range;
 
-                                 updateAnchorAndFocusFromRange(this, range, selectionIsBackward(this.nativeSelection));
 
-                                 this.isCollapsed = selectionIsCollapsed(this);
 
-                             } else {
 
-                                 // The range was not added successfully. The simplest thing is to refresh
 
-                                 this.refresh();
 
-                             }
 
-                         }
 
-                     }
 
-                 };
 
-             } else {
 
-                 selProto.addRange = function(range, direction) {
 
-                     if (isDirectionBackward(direction) && selectionHasExtend) {
 
-                         addRangeBackward(this, range);
 
-                     } else {
 
-                         this.nativeSelection.addRange(getNativeRange(range));
 
-                         this.refresh();
 
-                     }
 
-                 };
 
-             }
 
-             selProto.setRanges = function(ranges) {
 
-                 if (implementsControlRange && implementsDocSelection && ranges.length > 1) {
 
-                     createControlSelection(this, ranges);
 
-                 } else {
 
-                     this.removeAllRanges();
 
-                     for (var i = 0, len = ranges.length; i < len; ++i) {
 
-                         this.addRange(ranges[i]);
 
-                     }
 
-                 }
 
-             };
 
-         } else if (isHostMethod(testSelection, "empty") && isHostMethod(testRange, "select") &&
 
-                    implementsControlRange && useDocumentSelection) {
 
-             selProto.removeAllRanges = function() {
 
-                 // Added try/catch as fix for issue #21
 
-                 try {
 
-                     this.docSelection.empty();
 
-                     // Check for empty() not working (issue #24)
 
-                     if (this.docSelection.type != "None") {
 
-                         // Work around failure to empty a control selection by instead selecting a TextRange and then
 
-                         // calling empty()
 
-                         var doc;
 
-                         if (this.anchorNode) {
 
-                             doc = getDocument(this.anchorNode);
 
-                         } else if (this.docSelection.type == CONTROL) {
 
-                             var controlRange = this.docSelection.createRange();
 
-                             if (controlRange.length) {
 
-                                 doc = getDocument( controlRange.item(0) );
 
-                             }
 
-                         }
 
-                         if (doc) {
 
-                             var textRange = getBody(doc).createTextRange();
 
-                             textRange.select();
 
-                             this.docSelection.empty();
 
-                         }
 
-                     }
 
-                 } catch(ex) {}
 
-                 updateEmptySelection(this);
 
-             };
 
-             selProto.addRange = function(range) {
 
-                 if (this.docSelection.type == CONTROL) {
 
-                     addRangeToControlSelection(this, range);
 
-                 } else {
 
-                     api.WrappedTextRange.rangeToTextRange(range).select();
 
-                     this._ranges[0] = range;
 
-                     this.rangeCount = 1;
 
-                     this.isCollapsed = this._ranges[0].collapsed;
 
-                     updateAnchorAndFocusFromRange(this, range, false);
 
-                 }
 
-             };
 
-             selProto.setRanges = function(ranges) {
 
-                 this.removeAllRanges();
 
-                 var rangeCount = ranges.length;
 
-                 if (rangeCount > 1) {
 
-                     createControlSelection(this, ranges);
 
-                 } else if (rangeCount) {
 
-                     this.addRange(ranges[0]);
 
-                 }
 
-             };
 
-         } else {
 
-             module.fail("No means of selecting a Range or TextRange was found");
 
-             return false;
 
-         }
 
-         selProto.getRangeAt = function(index) {
 
-             if (index < 0 || index >= this.rangeCount) {
 
-                 throw new DOMException("INDEX_SIZE_ERR");
 
-             } else {
 
-                 // Clone the range to preserve selection-range independence. See issue 80.
 
-                 return this._ranges[index].cloneRange();
 
-             }
 
-         };
 
-         var refreshSelection;
 
-         if (useDocumentSelection) {
 
-             refreshSelection = function(sel) {
 
-                 var range;
 
-                 if (api.isSelectionValid(sel.win)) {
 
-                     range = sel.docSelection.createRange();
 
-                 } else {
 
-                     range = getBody(sel.win.document).createTextRange();
 
-                     range.collapse(true);
 
-                 }
 
-                 if (sel.docSelection.type == CONTROL) {
 
-                     updateControlSelection(sel);
 
-                 } else if (isTextRange(range)) {
 
-                     updateFromTextRange(sel, range);
 
-                 } else {
 
-                     updateEmptySelection(sel);
 
-                 }
 
-             };
 
-         } else if (isHostMethod(testSelection, "getRangeAt") && typeof testSelection.rangeCount == NUMBER) {
 
-             refreshSelection = function(sel) {
 
-                 if (implementsControlRange && implementsDocSelection && sel.docSelection.type == CONTROL) {
 
-                     updateControlSelection(sel);
 
-                 } else {
 
-                     sel._ranges.length = sel.rangeCount = sel.nativeSelection.rangeCount;
 
-                     if (sel.rangeCount) {
 
-                         for (var i = 0, len = sel.rangeCount; i < len; ++i) {
 
-                             sel._ranges[i] = new api.WrappedRange(sel.nativeSelection.getRangeAt(i));
 
-                         }
 
-                         updateAnchorAndFocusFromRange(sel, sel._ranges[sel.rangeCount - 1], selectionIsBackward(sel.nativeSelection));
 
-                         sel.isCollapsed = selectionIsCollapsed(sel);
 
-                     } else {
 
-                         updateEmptySelection(sel);
 
-                     }
 
-                 }
 
-             };
 
-         } else if (selectionHasAnchorAndFocus && typeof testSelection.isCollapsed == BOOLEAN && typeof testRange.collapsed == BOOLEAN && features.implementsDomRange) {
 
-             refreshSelection = function(sel) {
 
-                 var range, nativeSel = sel.nativeSelection;
 
-                 if (nativeSel.anchorNode) {
 
-                     range = getSelectionRangeAt(nativeSel, 0);
 
-                     sel._ranges = [range];
 
-                     sel.rangeCount = 1;
 
-                     updateAnchorAndFocusFromNativeSelection(sel);
 
-                     sel.isCollapsed = selectionIsCollapsed(sel);
 
-                 } else {
 
-                     updateEmptySelection(sel);
 
-                 }
 
-             };
 
-         } else {
 
-             module.fail("No means of obtaining a Range or TextRange from the user's selection was found");
 
-             return false;
 
-         }
 
-         selProto.refresh = function(checkForChanges) {
 
-             var oldRanges = checkForChanges ? this._ranges.slice(0) : null;
 
-             var oldAnchorNode = this.anchorNode, oldAnchorOffset = this.anchorOffset;
 
-             refreshSelection(this);
 
-             if (checkForChanges) {
 
-                 // Check the range count first
 
-                 var i = oldRanges.length;
 
-                 if (i != this._ranges.length) {
 
-                     return true;
 
-                 }
 
-                 // Now check the direction. Checking the anchor position is the same is enough since we're checking all the
 
-                 // ranges after this
 
-                 if (this.anchorNode != oldAnchorNode || this.anchorOffset != oldAnchorOffset) {
 
-                     return true;
 
-                 }
 
-                 // Finally, compare each range in turn
 
-                 while (i--) {
 
-                     if (!rangesEqual(oldRanges[i], this._ranges[i])) {
 
-                         return true;
 
-                     }
 
-                 }
 
-                 return false;
 
-             }
 
-         };
 
-         // Removal of a single range
 
-         var removeRangeManually = function(sel, range) {
 
-             var ranges = sel.getAllRanges();
 
-             sel.removeAllRanges();
 
-             for (var i = 0, len = ranges.length; i < len; ++i) {
 
-                 if (!rangesEqual(range, ranges[i])) {
 
-                     sel.addRange(ranges[i]);
 
-                 }
 
-             }
 
-             if (!sel.rangeCount) {
 
-                 updateEmptySelection(sel);
 
-             }
 
-         };
 
-         if (implementsControlRange && implementsDocSelection) {
 
-             selProto.removeRange = function(range) {
 
-                 if (this.docSelection.type == CONTROL) {
 
-                     var controlRange = this.docSelection.createRange();
 
-                     var rangeElement = getSingleElementFromRange(range);
 
-                     // Create a new ControlRange containing all the elements in the selected ControlRange minus the
 
-                     // element contained by the supplied range
 
-                     var doc = getDocument(controlRange.item(0));
 
-                     var newControlRange = getBody(doc).createControlRange();
 
-                     var el, removed = false;
 
-                     for (var i = 0, len = controlRange.length; i < len; ++i) {
 
-                         el = controlRange.item(i);
 
-                         if (el !== rangeElement || removed) {
 
-                             newControlRange.add(controlRange.item(i));
 
-                         } else {
 
-                             removed = true;
 
-                         }
 
-                     }
 
-                     newControlRange.select();
 
-                     // Update the wrapped selection based on what's now in the native selection
 
-                     updateControlSelection(this);
 
-                 } else {
 
-                     removeRangeManually(this, range);
 
-                 }
 
-             };
 
-         } else {
 
-             selProto.removeRange = function(range) {
 
-                 removeRangeManually(this, range);
 
-             };
 
-         }
 
-         // Detecting if a selection is backward
 
-         var selectionIsBackward;
 
-         if (!useDocumentSelection && selectionHasAnchorAndFocus && features.implementsDomRange) {
 
-             selectionIsBackward = winSelectionIsBackward;
 
-             selProto.isBackward = function() {
 
-                 return selectionIsBackward(this);
 
-             };
 
-         } else {
 
-             selectionIsBackward = selProto.isBackward = function() {
 
-                 return false;
 
-             };
 
-         }
 
-         // Create an alias for backwards compatibility. From 1.3, everything is "backward" rather than "backwards"
 
-         selProto.isBackwards = selProto.isBackward;
 
-         // Selection stringifier
 
-         // This is conformant to the old HTML5 selections draft spec but differs from WebKit and Mozilla's implementation.
 
-         // The current spec does not yet define this method.
 
-         selProto.toString = function() {
 
-             var rangeTexts = [];
 
-             for (var i = 0, len = this.rangeCount; i < len; ++i) {
 
-                 rangeTexts[i] = "" + this._ranges[i];
 
-             }
 
-             return rangeTexts.join("");
 
-         };
 
-         function assertNodeInSameDocument(sel, node) {
 
-             if (sel.win.document != getDocument(node)) {
 
-                 throw new DOMException("WRONG_DOCUMENT_ERR");
 
-             }
 
-         }
 
-         // No current browser conforms fully to the spec for this method, so Rangy's own method is always used
 
-         selProto.collapse = function(node, offset) {
 
-             assertNodeInSameDocument(this, node);
 
-             var range = api.createRange(node);
 
-             range.collapseToPoint(node, offset);
 
-             this.setSingleRange(range);
 
-             this.isCollapsed = true;
 
-         };
 
-         selProto.collapseToStart = function() {
 
-             if (this.rangeCount) {
 
-                 var range = this._ranges[0];
 
-                 this.collapse(range.startContainer, range.startOffset);
 
-             } else {
 
-                 throw new DOMException("INVALID_STATE_ERR");
 
-             }
 
-         };
 
-         selProto.collapseToEnd = function() {
 
-             if (this.rangeCount) {
 
-                 var range = this._ranges[this.rangeCount - 1];
 
-                 this.collapse(range.endContainer, range.endOffset);
 
-             } else {
 
-                 throw new DOMException("INVALID_STATE_ERR");
 
-             }
 
-         };
 
-         // The spec is very specific on how selectAllChildren should be implemented so the native implementation is
 
-         // never used by Rangy.
 
-         selProto.selectAllChildren = function(node) {
 
-             assertNodeInSameDocument(this, node);
 
-             var range = api.createRange(node);
 
-             range.selectNodeContents(node);
 
-             this.setSingleRange(range);
 
-         };
 
-         selProto.deleteFromDocument = function() {
 
-             // Sepcial behaviour required for IE's control selections
 
-             if (implementsControlRange && implementsDocSelection && this.docSelection.type == CONTROL) {
 
-                 var controlRange = this.docSelection.createRange();
 
-                 var element;
 
-                 while (controlRange.length) {
 
-                     element = controlRange.item(0);
 
-                     controlRange.remove(element);
 
-                     element.parentNode.removeChild(element);
 
-                 }
 
-                 this.refresh();
 
-             } else if (this.rangeCount) {
 
-                 var ranges = this.getAllRanges();
 
-                 if (ranges.length) {
 
-                     this.removeAllRanges();
 
-                     for (var i = 0, len = ranges.length; i < len; ++i) {
 
-                         ranges[i].deleteContents();
 
-                     }
 
-                     // The spec says nothing about what the selection should contain after calling deleteContents on each
 
-                     // range. Firefox moves the selection to where the final selected range was, so we emulate that
 
-                     this.addRange(ranges[len - 1]);
 
-                 }
 
-             }
 
-         };
 
-         // The following are non-standard extensions
 
-         selProto.eachRange = function(func, returnValue) {
 
-             for (var i = 0, len = this._ranges.length; i < len; ++i) {
 
-                 if ( func( this.getRangeAt(i) ) ) {
 
-                     return returnValue;
 
-                 }
 
-             }
 
-         };
 
-         selProto.getAllRanges = function() {
 
-             var ranges = [];
 
-             this.eachRange(function(range) {
 
-                 ranges.push(range);
 
-             });
 
-             return ranges;
 
-         };
 
-         selProto.setSingleRange = function(range, direction) {
 
-             this.removeAllRanges();
 
-             this.addRange(range, direction);
 
-         };
 
-         selProto.callMethodOnEachRange = function(methodName, params) {
 
-             var results = [];
 
-             this.eachRange( function(range) {
 
-                 results.push( range[methodName].apply(range, params) );
 
-             } );
 
-             return results;
 
-         };
 
-         
 
-         function createStartOrEndSetter(isStart) {
 
-             return function(node, offset) {
 
-                 var range;
 
-                 if (this.rangeCount) {
 
-                     range = this.getRangeAt(0);
 
-                     range["set" + (isStart ? "Start" : "End")](node, offset);
 
-                 } else {
 
-                     range = api.createRange(this.win.document);
 
-                     range.setStartAndEnd(node, offset);
 
-                 }
 
-                 this.setSingleRange(range, this.isBackward());
 
-             };
 
-         }
 
-         selProto.setStart = createStartOrEndSetter(true);
 
-         selProto.setEnd = createStartOrEndSetter(false);
 
-         
 
-         // Add select() method to Range prototype. Any existing selection will be removed.
 
-         api.rangePrototype.select = function(direction) {
 
-             getSelection( this.getDocument() ).setSingleRange(this, direction);
 
-         };
 
-         selProto.changeEachRange = function(func) {
 
-             var ranges = [];
 
-             var backward = this.isBackward();
 
-             this.eachRange(function(range) {
 
-                 func(range);
 
-                 ranges.push(range);
 
-             });
 
-             this.removeAllRanges();
 
-             if (backward && ranges.length == 1) {
 
-                 this.addRange(ranges[0], "backward");
 
-             } else {
 
-                 this.setRanges(ranges);
 
-             }
 
-         };
 
-         selProto.containsNode = function(node, allowPartial) {
 
-             return this.eachRange( function(range) {
 
-                 return range.containsNode(node, allowPartial);
 
-             }, true ) || false;
 
-         };
 
-         selProto.getBookmark = function(containerNode) {
 
-             return {
 
-                 backward: this.isBackward(),
 
-                 rangeBookmarks: this.callMethodOnEachRange("getBookmark", [containerNode])
 
-             };
 
-         };
 
-         selProto.moveToBookmark = function(bookmark) {
 
-             var selRanges = [];
 
-             for (var i = 0, rangeBookmark, range; rangeBookmark = bookmark.rangeBookmarks[i++]; ) {
 
-                 range = api.createRange(this.win);
 
-                 range.moveToBookmark(rangeBookmark);
 
-                 selRanges.push(range);
 
-             }
 
-             if (bookmark.backward) {
 
-                 this.setSingleRange(selRanges[0], "backward");
 
-             } else {
 
-                 this.setRanges(selRanges);
 
-             }
 
-         };
 
-         selProto.toHtml = function() {
 
-             var rangeHtmls = [];
 
-             this.eachRange(function(range) {
 
-                 rangeHtmls.push( DomRange.toHtml(range) );
 
-             });
 
-             return rangeHtmls.join("");
 
-         };
 
-         if (features.implementsTextRange) {
 
-             selProto.getNativeTextRange = function() {
 
-                 var sel, textRange;
 
-                 if ( (sel = this.docSelection) ) {
 
-                     var range = sel.createRange();
 
-                     if (isTextRange(range)) {
 
-                         return range;
 
-                     } else {
 
-                         throw module.createError("getNativeTextRange: selection is a control selection"); 
 
-                     }
 
-                 } else if (this.rangeCount > 0) {
 
-                     return api.WrappedTextRange.rangeToTextRange( this.getRangeAt(0) );
 
-                 } else {
 
-                     throw module.createError("getNativeTextRange: selection contains no range");
 
-                 }
 
-             };
 
-         }
 
-         function inspect(sel) {
 
-             var rangeInspects = [];
 
-             var anchor = new DomPosition(sel.anchorNode, sel.anchorOffset);
 
-             var focus = new DomPosition(sel.focusNode, sel.focusOffset);
 
-             var name = (typeof sel.getName == "function") ? sel.getName() : "Selection";
 
-             if (typeof sel.rangeCount != "undefined") {
 
-                 for (var i = 0, len = sel.rangeCount; i < len; ++i) {
 
-                     rangeInspects[i] = DomRange.inspect(sel.getRangeAt(i));
 
-                 }
 
-             }
 
-             return "[" + name + "(Ranges: " + rangeInspects.join(", ") +
 
-                     ")(anchor: " + anchor.inspect() + ", focus: " + focus.inspect() + "]";
 
-         }
 
-         selProto.getName = function() {
 
-             return "WrappedSelection";
 
-         };
 
-         selProto.inspect = function() {
 
-             return inspect(this);
 
-         };
 
-         selProto.detach = function() {
 
-             actOnCachedSelection(this.win, "delete");
 
-             deleteProperties(this);
 
-         };
 
-         WrappedSelection.detachAll = function() {
 
-             actOnCachedSelection(null, "deleteAll");
 
-         };
 
-         WrappedSelection.inspect = inspect;
 
-         WrappedSelection.isDirectionBackward = isDirectionBackward;
 
-         api.Selection = WrappedSelection;
 
-         api.selectionPrototype = selProto;
 
-         api.addShimListener(function(win) {
 
-             if (typeof win.getSelection == "undefined") {
 
-                 win.getSelection = function() {
 
-                     return getSelection(win);
 
-                 };
 
-             }
 
-             win = null;
 
-         });
 
-     });
 
-     
 
-     /*----------------------------------------------------------------------------------------------------------------*/
 
-     return api;
 
- }, this);;/**
 
-  * Selection save and restore module for Rangy.
 
-  * Saves and restores user selections using marker invisible elements in the DOM.
 
-  *
 
-  * Part of Rangy, a cross-browser JavaScript range and selection library
 
-  * http://code.google.com/p/rangy/
 
-  *
 
-  * Depends on Rangy core.
 
-  *
 
-  * Copyright 2014, Tim Down
 
-  * Licensed under the MIT license.
 
-  * Version: 1.3alpha.20140804
 
-  * Build date: 4 August 2014
 
-  */
 
- (function(factory, global) {
 
-     if (typeof define == "function" && define.amd) {
 
-         // AMD. Register as an anonymous module with a dependency on Rangy.
 
-         define(["rangy"], factory);
 
-         /*
 
-          } else if (typeof exports == "object") {
 
-          // Node/CommonJS style for Browserify
 
-          module.exports = factory;
 
-          */
 
-     } else {
 
-         // No AMD or CommonJS support so we use the rangy global variable
 
-         factory(global.rangy);
 
-     }
 
- })(function(rangy) {
 
-     rangy.createModule("SaveRestore", ["WrappedRange"], function(api, module) {
 
-         var dom = api.dom;
 
-         var markerTextChar = "\ufeff";
 
-         function gEBI(id, doc) {
 
-             return (doc || document).getElementById(id);
 
-         }
 
-         function insertRangeBoundaryMarker(range, atStart) {
 
-             var markerId = "selectionBoundary_" + (+new Date()) + "_" + ("" + Math.random()).slice(2);
 
-             var markerEl;
 
-             var doc = dom.getDocument(range.startContainer);
 
-             // Clone the Range and collapse to the appropriate boundary point
 
-             var boundaryRange = range.cloneRange();
 
-             boundaryRange.collapse(atStart);
 
-             // Create the marker element containing a single invisible character using DOM methods and insert it
 
-             markerEl = doc.createElement("span");
 
-             markerEl.id = markerId;
 
-             markerEl.style.lineHeight = "0";
 
-             markerEl.style.display = "none";
 
-             markerEl.className = "rangySelectionBoundary";
 
-             markerEl.appendChild(doc.createTextNode(markerTextChar));
 
-             boundaryRange.insertNode(markerEl);
 
-             return markerEl;
 
-         }
 
-         function setRangeBoundary(doc, range, markerId, atStart) {
 
-             var markerEl = gEBI(markerId, doc);
 
-             if (markerEl) {
 
-                 range[atStart ? "setStartBefore" : "setEndBefore"](markerEl);
 
-                 markerEl.parentNode.removeChild(markerEl);
 
-             } else {
 
-                 module.warn("Marker element has been removed. Cannot restore selection.");
 
-             }
 
-         }
 
-         function compareRanges(r1, r2) {
 
-             return r2.compareBoundaryPoints(r1.START_TO_START, r1);
 
-         }
 
-         function saveRange(range, backward) {
 
-             var startEl, endEl, doc = api.DomRange.getRangeDocument(range), text = range.toString();
 
-             if (range.collapsed) {
 
-                 endEl = insertRangeBoundaryMarker(range, false);
 
-                 return {
 
-                     document: doc,
 
-                     markerId: endEl.id,
 
-                     collapsed: true
 
-                 };
 
-             } else {
 
-                 endEl = insertRangeBoundaryMarker(range, false);
 
-                 startEl = insertRangeBoundaryMarker(range, true);
 
-                 return {
 
-                     document: doc,
 
-                     startMarkerId: startEl.id,
 
-                     endMarkerId: endEl.id,
 
-                     collapsed: false,
 
-                     backward: backward,
 
-                     toString: function() {
 
-                         return "original text: '" + text + "', new text: '" + range.toString() + "'";
 
-                     }
 
-                 };
 
-             }
 
-         }
 
-         function restoreRange(rangeInfo, normalize) {
 
-             var doc = rangeInfo.document;
 
-             if (typeof normalize == "undefined") {
 
-                 normalize = true;
 
-             }
 
-             var range = api.createRange(doc);
 
-             if (rangeInfo.collapsed) {
 
-                 var markerEl = gEBI(rangeInfo.markerId, doc);
 
-                 if (markerEl) {
 
-                     markerEl.style.display = "inline";
 
-                     var previousNode = markerEl.previousSibling;
 
-                     // Workaround for issue 17
 
-                     if (previousNode && previousNode.nodeType == 3) {
 
-                         markerEl.parentNode.removeChild(markerEl);
 
-                         range.collapseToPoint(previousNode, previousNode.length);
 
-                     } else {
 
-                         range.collapseBefore(markerEl);
 
-                         markerEl.parentNode.removeChild(markerEl);
 
-                     }
 
-                 } else {
 
-                     module.warn("Marker element has been removed. Cannot restore selection.");
 
-                 }
 
-             } else {
 
-                 setRangeBoundary(doc, range, rangeInfo.startMarkerId, true);
 
-                 setRangeBoundary(doc, range, rangeInfo.endMarkerId, false);
 
-             }
 
-             if (normalize) {
 
-                 range.normalizeBoundaries();
 
-             }
 
-             return range;
 
-         }
 
-         function saveRanges(ranges, backward) {
 
-             var rangeInfos = [], range, doc;
 
-             // Order the ranges by position within the DOM, latest first, cloning the array to leave the original untouched
 
-             ranges = ranges.slice(0);
 
-             ranges.sort(compareRanges);
 
-             for (var i = 0, len = ranges.length; i < len; ++i) {
 
-                 rangeInfos[i] = saveRange(ranges[i], backward);
 
-             }
 
-             // Now that all the markers are in place and DOM manipulation over, adjust each range's boundaries to lie
 
-             // between its markers
 
-             for (i = len - 1; i >= 0; --i) {
 
-                 range = ranges[i];
 
-                 doc = api.DomRange.getRangeDocument(range);
 
-                 if (range.collapsed) {
 
-                     range.collapseAfter(gEBI(rangeInfos[i].markerId, doc));
 
-                 } else {
 
-                     range.setEndBefore(gEBI(rangeInfos[i].endMarkerId, doc));
 
-                     range.setStartAfter(gEBI(rangeInfos[i].startMarkerId, doc));
 
-                 }
 
-             }
 
-             return rangeInfos;
 
-         }
 
-         function saveSelection(win) {
 
-             if (!api.isSelectionValid(win)) {
 
-                 module.warn("Cannot save selection. This usually happens when the selection is collapsed and the selection document has lost focus.");
 
-                 return null;
 
-             }
 
-             var sel = api.getSelection(win);
 
-             var ranges = sel.getAllRanges();
 
-             var backward = (ranges.length == 1 && sel.isBackward());
 
-             var rangeInfos = saveRanges(ranges, backward);
 
-             // Ensure current selection is unaffected
 
-             if (backward) {
 
-                 sel.setSingleRange(ranges[0], "backward");
 
-             } else {
 
-                 sel.setRanges(ranges);
 
-             }
 
-             return {
 
-                 win: win,
 
-                 rangeInfos: rangeInfos,
 
-                 restored: false
 
-             };
 
-         }
 
-         function restoreRanges(rangeInfos) {
 
-             var ranges = [];
 
-             // Ranges are in reverse order of appearance in the DOM. We want to restore earliest first to avoid
 
-             // normalization affecting previously restored ranges.
 
-             var rangeCount = rangeInfos.length;
 
-             for (var i = rangeCount - 1; i >= 0; i--) {
 
-                 ranges[i] = restoreRange(rangeInfos[i], true);
 
-             }
 
-             return ranges;
 
-         }
 
-         function restoreSelection(savedSelection, preserveDirection) {
 
-             if (!savedSelection.restored) {
 
-                 var rangeInfos = savedSelection.rangeInfos;
 
-                 var sel = api.getSelection(savedSelection.win);
 
-                 var ranges = restoreRanges(rangeInfos), rangeCount = rangeInfos.length;
 
-                 if (rangeCount == 1 && preserveDirection && api.features.selectionHasExtend && rangeInfos[0].backward) {
 
-                     sel.removeAllRanges();
 
-                     sel.addRange(ranges[0], true);
 
-                 } else {
 
-                     sel.setRanges(ranges);
 
-                 }
 
-                 savedSelection.restored = true;
 
-             }
 
-         }
 
-         function removeMarkerElement(doc, markerId) {
 
-             var markerEl = gEBI(markerId, doc);
 
-             if (markerEl) {
 
-                 markerEl.parentNode.removeChild(markerEl);
 
-             }
 
-         }
 
-         function removeMarkers(savedSelection) {
 
-             var rangeInfos = savedSelection.rangeInfos;
 
-             for (var i = 0, len = rangeInfos.length, rangeInfo; i < len; ++i) {
 
-                 rangeInfo = rangeInfos[i];
 
-                 if (rangeInfo.collapsed) {
 
-                     removeMarkerElement(savedSelection.doc, rangeInfo.markerId);
 
-                 } else {
 
-                     removeMarkerElement(savedSelection.doc, rangeInfo.startMarkerId);
 
-                     removeMarkerElement(savedSelection.doc, rangeInfo.endMarkerId);
 
-                 }
 
-             }
 
-         }
 
-         api.util.extend(api, {
 
-             saveRange: saveRange,
 
-             restoreRange: restoreRange,
 
-             saveRanges: saveRanges,
 
-             restoreRanges: restoreRanges,
 
-             saveSelection: saveSelection,
 
-             restoreSelection: restoreSelection,
 
-             removeMarkerElement: removeMarkerElement,
 
-             removeMarkers: removeMarkers
 
-         });
 
-     });
 
-     
 
- }, this);;/*
 
- 	Base.js, version 1.1a
 
- 	Copyright 2006-2010, Dean Edwards
 
- 	License: http://www.opensource.org/licenses/mit-license.php
 
- */
 
- var Base = function() {
 
- 	// dummy
 
- };
 
- Base.extend = function(_instance, _static) { // subclass
 
- 	var extend = Base.prototype.extend;
 
- 	
 
- 	// build the prototype
 
- 	Base._prototyping = true;
 
- 	var proto = new this;
 
- 	extend.call(proto, _instance);
 
-   proto.base = function() {
 
-     // call this method from any other method to invoke that method's ancestor
 
-   };
 
- 	delete Base._prototyping;
 
- 	
 
- 	// create the wrapper for the constructor function
 
- 	//var constructor = proto.constructor.valueOf(); //-dean
 
- 	var constructor = proto.constructor;
 
- 	var klass = proto.constructor = function() {
 
- 		if (!Base._prototyping) {
 
- 			if (this._constructing || this.constructor == klass) { // instantiation
 
- 				this._constructing = true;
 
- 				constructor.apply(this, arguments);
 
- 				delete this._constructing;
 
- 			} else if (arguments[0] != null) { // casting
 
- 				return (arguments[0].extend || extend).call(arguments[0], proto);
 
- 			}
 
- 		}
 
- 	};
 
- 	
 
- 	// build the class interface
 
- 	klass.ancestor = this;
 
- 	klass.extend = this.extend;
 
- 	klass.forEach = this.forEach;
 
- 	klass.implement = this.implement;
 
- 	klass.prototype = proto;
 
- 	klass.toString = this.toString;
 
- 	klass.valueOf = function(type) {
 
- 		//return (type == "object") ? klass : constructor; //-dean
 
- 		return (type == "object") ? klass : constructor.valueOf();
 
- 	};
 
- 	extend.call(klass, _static);
 
- 	// class initialisation
 
- 	if (typeof klass.init == "function") klass.init();
 
- 	return klass;
 
- };
 
- Base.prototype = {	
 
- 	extend: function(source, value) {
 
- 		if (arguments.length > 1) { // extending with a name/value pair
 
- 			var ancestor = this[source];
 
- 			if (ancestor && (typeof value == "function") && // overriding a method?
 
- 				// the valueOf() comparison is to avoid circular references
 
- 				(!ancestor.valueOf || ancestor.valueOf() != value.valueOf()) &&
 
- 				/\bbase\b/.test(value)) {
 
- 				// get the underlying method
 
- 				var method = value.valueOf();
 
- 				// override
 
- 				value = function() {
 
- 					var previous = this.base || Base.prototype.base;
 
- 					this.base = ancestor;
 
- 					var returnValue = method.apply(this, arguments);
 
- 					this.base = previous;
 
- 					return returnValue;
 
- 				};
 
- 				// point to the underlying method
 
- 				value.valueOf = function(type) {
 
- 					return (type == "object") ? value : method;
 
- 				};
 
- 				value.toString = Base.toString;
 
- 			}
 
- 			this[source] = value;
 
- 		} else if (source) { // extending with an object literal
 
- 			var extend = Base.prototype.extend;
 
- 			// if this object has a customised extend method then use it
 
- 			if (!Base._prototyping && typeof this != "function") {
 
- 				extend = this.extend || extend;
 
- 			}
 
- 			var proto = {toSource: null};
 
- 			// do the "toString" and other methods manually
 
- 			var hidden = ["constructor", "toString", "valueOf"];
 
- 			// if we are prototyping then include the constructor
 
- 			var i = Base._prototyping ? 0 : 1;
 
- 			while (key = hidden[i++]) {
 
- 				if (source[key] != proto[key]) {
 
- 					extend.call(this, key, source[key]);
 
- 				}
 
- 			}
 
- 			// copy each of the source object's properties to this object
 
- 			for (var key in source) {
 
- 				if (!proto[key]) extend.call(this, key, source[key]);
 
- 			}
 
- 		}
 
- 		return this;
 
- 	}
 
- };
 
- // initialise
 
- Base = Base.extend({
 
- 	constructor: function() {
 
- 		this.extend(arguments[0]);
 
- 	}
 
- }, {
 
- 	ancestor: Object,
 
- 	version: "1.1",
 
- 	
 
- 	forEach: function(object, block, context) {
 
- 		for (var key in object) {
 
- 			if (this.prototype[key] === undefined) {
 
- 				block.call(context, object[key], key, object);
 
- 			}
 
- 		}
 
- 	},
 
- 		
 
- 	implement: function() {
 
- 		for (var i = 0; i < arguments.length; i++) {
 
- 			if (typeof arguments[i] == "function") {
 
- 				// if it's a function, call it
 
- 				arguments[i](this.prototype);
 
- 			} else {
 
- 				// add the interface using the extend method
 
- 				this.prototype.extend(arguments[i]);
 
- 			}
 
- 		}
 
- 		return this;
 
- 	},
 
- 	
 
- 	toString: function() {
 
- 		return String(this.valueOf());
 
- 	}
 
- });;/**
 
-  * Detect browser support for specific features
 
-  */
 
- wysihtml5.browser = (function() {
 
-   var userAgent   = navigator.userAgent,
 
-       testElement = document.createElement("div"),
 
-       // Browser sniffing is unfortunately needed since some behaviors are impossible to feature detect
 
-       isGecko     = userAgent.indexOf("Gecko")        !== -1 && userAgent.indexOf("KHTML") === -1,
 
-       isWebKit    = userAgent.indexOf("AppleWebKit/") !== -1,
 
-       isChrome    = userAgent.indexOf("Chrome/")      !== -1,
 
-       isOpera     = userAgent.indexOf("Opera/")       !== -1;
 
-   function iosVersion(userAgent) {
 
-     return +((/ipad|iphone|ipod/.test(userAgent) && userAgent.match(/ os (\d+).+? like mac os x/)) || [undefined, 0])[1];
 
-   }
 
-   function androidVersion(userAgent) {
 
-     return +(userAgent.match(/android (\d+)/) || [undefined, 0])[1];
 
-   }
 
-   function isIE(version, equation) {
 
-     var rv = -1,
 
-         re;
 
-     if (navigator.appName == 'Microsoft Internet Explorer') {
 
-       re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
 
-     } else if (navigator.appName == 'Netscape') {
 
-       re = new RegExp("Trident/.*rv:([0-9]{1,}[\.0-9]{0,})");
 
-     }
 
-     if (re && re.exec(navigator.userAgent) != null) {
 
-       rv = parseFloat(RegExp.$1);
 
-     }
 
-     if (rv === -1) { return false; }
 
-     if (!version) { return true; }
 
-     if (!equation) { return version === rv; }
 
-     if (equation === "<") { return version < rv; }
 
-     if (equation === ">") { return version > rv; }
 
-     if (equation === "<=") { return version <= rv; }
 
-     if (equation === ">=") { return version >= rv; }
 
-   }
 
-   return {
 
-     // Static variable needed, publicly accessible, to be able override it in unit tests
 
-     USER_AGENT: userAgent,
 
-     /**
 
-      * Exclude browsers that are not capable of displaying and handling
 
-      * contentEditable as desired:
 
-      *    - iPhone, iPad (tested iOS 4.2.2) and Android (tested 2.2) refuse to make contentEditables focusable
 
-      *    - IE < 8 create invalid markup and crash randomly from time to time
 
-      *
 
-      * @return {Boolean}
 
-      */
 
-     supported: function() {
 
-       var userAgent                   = this.USER_AGENT.toLowerCase(),
 
-           // Essential for making html elements editable
 
-           hasContentEditableSupport   = "contentEditable" in testElement,
 
-           // Following methods are needed in order to interact with the contentEditable area
 
-           hasEditingApiSupport        = document.execCommand && document.queryCommandSupported && document.queryCommandState,
 
-           // document selector apis are only supported by IE 8+, Safari 4+, Chrome and Firefox 3.5+
 
-           hasQuerySelectorSupport     = document.querySelector && document.querySelectorAll,
 
-           // contentEditable is unusable in mobile browsers (tested iOS 4.2.2, Android 2.2, Opera Mobile, WebOS 3.05)
 
-           isIncompatibleMobileBrowser = (this.isIos() && iosVersion(userAgent) < 5) || (this.isAndroid() && androidVersion(userAgent) < 4) || userAgent.indexOf("opera mobi") !== -1 || userAgent.indexOf("hpwos/") !== -1;
 
-       return hasContentEditableSupport
 
-         && hasEditingApiSupport
 
-         && hasQuerySelectorSupport
 
-         && !isIncompatibleMobileBrowser;
 
-     },
 
-     isTouchDevice: function() {
 
-       return this.supportsEvent("touchmove");
 
-     },
 
-     isIos: function() {
 
-       return (/ipad|iphone|ipod/i).test(this.USER_AGENT);
 
-     },
 
-     isAndroid: function() {
 
-       return this.USER_AGENT.indexOf("Android") !== -1;
 
-     },
 
-     /**
 
-      * Whether the browser supports sandboxed iframes
 
-      * Currently only IE 6+ offers such feature <iframe security="restricted">
 
-      *
 
-      * http://msdn.microsoft.com/en-us/library/ms534622(v=vs.85).aspx
 
-      * http://blogs.msdn.com/b/ie/archive/2008/01/18/using-frames-more-securely.aspx
 
-      *
 
-      * HTML5 sandboxed iframes are still buggy and their DOM is not reachable from the outside (except when using postMessage)
 
-      */
 
-     supportsSandboxedIframes: function() {
 
-       return isIE();
 
-     },
 
-     /**
 
-      * IE6+7 throw a mixed content warning when the src of an iframe
 
-      * is empty/unset or about:blank
 
-      * window.querySelector is implemented as of IE8
 
-      */
 
-     throwsMixedContentWarningWhenIframeSrcIsEmpty: function() {
 
-       return !("querySelector" in document);
 
-     },
 
-     /**
 
-      * Whether the caret is correctly displayed in contentEditable elements
 
-      * Firefox sometimes shows a huge caret in the beginning after focusing
 
-      */
 
-     displaysCaretInEmptyContentEditableCorrectly: function() {
 
-       return isIE();
 
-     },
 
-     /**
 
-      * Opera and IE are the only browsers who offer the css value
 
-      * in the original unit, thx to the currentStyle object
 
-      * All other browsers provide the computed style in px via window.getComputedStyle
 
-      */
 
-     hasCurrentStyleProperty: function() {
 
-       return "currentStyle" in testElement;
 
-     },
 
-     /**
 
-      * Firefox on OSX navigates through history when hitting CMD + Arrow right/left
 
-      */
 
-     hasHistoryIssue: function() {
 
-       return isGecko && navigator.platform.substr(0, 3) === "Mac";
 
-     },
 
-     /**
 
-      * Whether the browser inserts a <br> when pressing enter in a contentEditable element
 
-      */
 
-     insertsLineBreaksOnReturn: function() {
 
-       return isGecko;
 
-     },
 
-     supportsPlaceholderAttributeOn: function(element) {
 
-       return "placeholder" in element;
 
-     },
 
-     supportsEvent: function(eventName) {
 
-       return "on" + eventName in testElement || (function() {
 
-         testElement.setAttribute("on" + eventName, "return;");
 
-         return typeof(testElement["on" + eventName]) === "function";
 
-       })();
 
-     },
 
-     /**
 
-      * Opera doesn't correctly fire focus/blur events when clicking in- and outside of iframe
 
-      */
 
-     supportsEventsInIframeCorrectly: function() {
 
-       return !isOpera;
 
-     },
 
-     /**
 
-      * Everything below IE9 doesn't know how to treat HTML5 tags
 
-      *
 
-      * @param {Object} context The document object on which to check HTML5 support
 
-      *
 
-      * @example
 
-      *    wysihtml5.browser.supportsHTML5Tags(document);
 
-      */
 
-     supportsHTML5Tags: function(context) {
 
-       var element = context.createElement("div"),
 
-           html5   = "<article>foo</article>";
 
-       element.innerHTML = html5;
 
-       return element.innerHTML.toLowerCase() === html5;
 
-     },
 
-     /**
 
-      * Checks whether a document supports a certain queryCommand
 
-      * In particular, Opera needs a reference to a document that has a contentEditable in it's dom tree
 
-      * in oder to report correct results
 
-      *
 
-      * @param {Object} doc Document object on which to check for a query command
 
-      * @param {String} command The query command to check for
 
-      * @return {Boolean}
 
-      *
 
-      * @example
 
-      *    wysihtml5.browser.supportsCommand(document, "bold");
 
-      */
 
-     supportsCommand: (function() {
 
-       // Following commands are supported but contain bugs in some browsers
 
-       var buggyCommands = {
 
-         // formatBlock fails with some tags (eg. <blockquote>)
 
-         "formatBlock":          isIE(10, "<="),
 
-          // When inserting unordered or ordered lists in Firefox, Chrome or Safari, the current selection or line gets
 
-          // converted into a list (<ul><li>...</li></ul>, <ol><li>...</li></ol>)
 
-          // IE and Opera act a bit different here as they convert the entire content of the current block element into a list
 
-         "insertUnorderedList":  isIE(),
 
-         "insertOrderedList":    isIE()
 
-       };
 
-       // Firefox throws errors for queryCommandSupported, so we have to build up our own object of supported commands
 
-       var supported = {
 
-         "insertHTML": isGecko
 
-       };
 
-       return function(doc, command) {
 
-         var isBuggy = buggyCommands[command];
 
-         if (!isBuggy) {
 
-           // Firefox throws errors when invoking queryCommandSupported or queryCommandEnabled
 
-           try {
 
-             return doc.queryCommandSupported(command);
 
-           } catch(e1) {}
 
-           try {
 
-             return doc.queryCommandEnabled(command);
 
-           } catch(e2) {
 
-             return !!supported[command];
 
-           }
 
-         }
 
-         return false;
 
-       };
 
-     })(),
 
-     /**
 
-      * IE: URLs starting with:
 
-      *    www., http://, https://, ftp://, gopher://, mailto:, new:, snews:, telnet:, wasis:, file://,
 
-      *    nntp://, newsrc:, ldap://, ldaps://, outlook:, mic:// and url:
 
-      * will automatically be auto-linked when either the user inserts them via copy&paste or presses the
 
-      * space bar when the caret is directly after such an url.
 
-      * This behavior cannot easily be avoided in IE < 9 since the logic is hardcoded in the mshtml.dll
 
-      * (related blog post on msdn
 
-      * http://blogs.msdn.com/b/ieinternals/archive/2009/09/17/prevent-automatic-hyperlinking-in-contenteditable-html.aspx).
 
-      */
 
-     doesAutoLinkingInContentEditable: function() {
 
-       return isIE();
 
-     },
 
-     /**
 
-      * As stated above, IE auto links urls typed into contentEditable elements
 
-      * Since IE9 it's possible to prevent this behavior
 
-      */
 
-     canDisableAutoLinking: function() {
 
-       return this.supportsCommand(document, "AutoUrlDetect");
 
-     },
 
-     /**
 
-      * IE leaves an empty paragraph in the contentEditable element after clearing it
 
-      * Chrome/Safari sometimes an empty <div>
 
-      */
 
-     clearsContentEditableCorrectly: function() {
 
-       return isGecko || isOpera || isWebKit;
 
-     },
 
-     /**
 
-      * IE gives wrong results for getAttribute
 
-      */
 
-     supportsGetAttributeCorrectly: function() {
 
-       var td = document.createElement("td");
 
-       return td.getAttribute("rowspan") != "1";
 
-     },
 
-     /**
 
-      * When clicking on images in IE, Opera and Firefox, they are selected, which makes it easy to interact with them.
 
-      * Chrome and Safari both don't support this
 
-      */
 
-     canSelectImagesInContentEditable: function() {
 
-       return isGecko || isIE() || isOpera;
 
-     },
 
-     /**
 
-      * All browsers except Safari and Chrome automatically scroll the range/caret position into view
 
-      */
 
-     autoScrollsToCaret: function() {
 
-       return !isWebKit;
 
-     },
 
-     /**
 
-      * Check whether the browser automatically closes tags that don't need to be opened
 
-      */
 
-     autoClosesUnclosedTags: function() {
 
-       var clonedTestElement = testElement.cloneNode(false),
 
-           returnValue,
 
-           innerHTML;
 
-       clonedTestElement.innerHTML = "<p><div></div>";
 
-       innerHTML                   = clonedTestElement.innerHTML.toLowerCase();
 
-       returnValue                 = innerHTML === "<p></p><div></div>" || innerHTML === "<p><div></div></p>";
 
-       // Cache result by overwriting current function
 
-       this.autoClosesUnclosedTags = function() { return returnValue; };
 
-       return returnValue;
 
-     },
 
-     /**
 
-      * Whether the browser supports the native document.getElementsByClassName which returns live NodeLists
 
-      */
 
-     supportsNativeGetElementsByClassName: function() {
 
-       return String(document.getElementsByClassName).indexOf("[native code]") !== -1;
 
-     },
 
-     /**
 
-      * As of now (19.04.2011) only supported by Firefox 4 and Chrome
 
-      * See https://developer.mozilla.org/en/DOM/Selection/modify
 
-      */
 
-     supportsSelectionModify: function() {
 
-       return "getSelection" in window && "modify" in window.getSelection();
 
-     },
 
-     /**
 
-      * Opera needs a white space after a <br> in order to position the caret correctly
 
-      */
 
-     needsSpaceAfterLineBreak: function() {
 
-       return isOpera;
 
-     },
 
-     /**
 
-      * Whether the browser supports the speech api on the given element
 
-      * See http://mikepultz.com/2011/03/accessing-google-speech-api-chrome-11/
 
-      *
 
-      * @example
 
-      *    var input = document.createElement("input");
 
-      *    if (wysihtml5.browser.supportsSpeechApiOn(input)) {
 
-      *      // ...
 
-      *    }
 
-      */
 
-     supportsSpeechApiOn: function(input) {
 
-       var chromeVersion = userAgent.match(/Chrome\/(\d+)/) || [undefined, 0];
 
-       return chromeVersion[1] >= 11 && ("onwebkitspeechchange" in input || "speech" in input);
 
-     },
 
-     /**
 
-      * IE9 crashes when setting a getter via Object.defineProperty on XMLHttpRequest or XDomainRequest
 
-      * See https://connect.microsoft.com/ie/feedback/details/650112
 
-      * or try the POC http://tifftiff.de/ie9_crash/
 
-      */
 
-     crashesWhenDefineProperty: function(property) {
 
-       return isIE(9) && (property === "XMLHttpRequest" || property === "XDomainRequest");
 
-     },
 
-     /**
 
-      * IE is the only browser who fires the "focus" event not immediately when .focus() is called on an element
 
-      */
 
-     doesAsyncFocus: function() {
 
-       return isIE();
 
-     },
 
-     /**
 
-      * In IE it's impssible for the user and for the selection library to set the caret after an <img> when it's the lastChild in the document
 
-      */
 
-     hasProblemsSettingCaretAfterImg: function() {
 
-       return isIE();
 
-     },
 
-     hasUndoInContextMenu: function() {
 
-       return isGecko || isChrome || isOpera;
 
-     },
 
-     /**
 
-      * Opera sometimes doesn't insert the node at the right position when range.insertNode(someNode)
 
-      * is used (regardless if rangy or native)
 
-      * This especially happens when the caret is positioned right after a <br> because then
 
-      * insertNode() will insert the node right before the <br>
 
-      */
 
-     hasInsertNodeIssue: function() {
 
-       return isOpera;
 
-     },
 
-     /**
 
-      * IE 8+9 don't fire the focus event of the <body> when the iframe gets focused (even though the caret gets set into the <body>)
 
-      */
 
-     hasIframeFocusIssue: function() {
 
-       return isIE();
 
-     },
 
-     /**
 
-      * Chrome + Safari create invalid nested markup after paste
 
-      *
 
-      *  <p>
 
-      *    foo
 
-      *    <p>bar</p> <!-- BOO! -->
 
-      *  </p>
 
-      */
 
-     createsNestedInvalidMarkupAfterPaste: function() {
 
-       return isWebKit;
 
-     },
 
-     supportsMutationEvents: function() {
 
-         return ("MutationEvent" in window);
 
-     },
 
-     /**
 
-       IE (at least up to 11) does not support clipboardData on event.
 
-       It is on window but cannot return text/html
 
-       Should actually check for clipboardData on paste event, but cannot in firefox
 
-     */
 
-     supportsModenPaste: function () {
 
-       return !("clipboardData" in window);
 
-     }
 
-   };
 
- })();
 
- ;wysihtml5.lang.array = function(arr) {
 
-   return {
 
-     /**
 
-      * Check whether a given object exists in an array
 
-      *
 
-      * @example
 
-      *    wysihtml5.lang.array([1, 2]).contains(1);
 
-      *    // => true
 
-      *
 
-      * Can be used to match array with array. If intersection is found true is returned
 
-      */
 
-     contains: function(needle) {
 
-       if (Array.isArray(needle)) {
 
-         for (var i = needle.length; i--;) {
 
-           if (wysihtml5.lang.array(arr).indexOf(needle[i]) !== -1) {
 
-             return true;
 
-           }
 
-         }
 
-         return false;
 
-       } else {
 
-         return wysihtml5.lang.array(arr).indexOf(needle) !== -1;
 
-       }
 
-     },
 
-     /**
 
-      * Check whether a given object exists in an array and return index
 
-      * If no elelemt found returns -1
 
-      *
 
-      * @example
 
-      *    wysihtml5.lang.array([1, 2]).indexOf(2);
 
-      *    // => 1
 
-      */
 
-     indexOf: function(needle) {
 
-         if (arr.indexOf) {
 
-           return arr.indexOf(needle);
 
-         } else {
 
-           for (var i=0, length=arr.length; i<length; i++) {
 
-             if (arr[i] === needle) { return i; }
 
-           }
 
-           return -1;
 
-         }
 
-     },
 
-     /**
 
-      * Substract one array from another
 
-      *
 
-      * @example
 
-      *    wysihtml5.lang.array([1, 2, 3, 4]).without([3, 4]);
 
-      *    // => [1, 2]
 
-      */
 
-     without: function(arrayToSubstract) {
 
-       arrayToSubstract = wysihtml5.lang.array(arrayToSubstract);
 
-       var newArr  = [],
 
-           i       = 0,
 
-           length  = arr.length;
 
-       for (; i<length; i++) {
 
-         if (!arrayToSubstract.contains(arr[i])) {
 
-           newArr.push(arr[i]);
 
-         }
 
-       }
 
-       return newArr;
 
-     },
 
-     /**
 
-      * Return a clean native array
 
-      *
 
-      * Following will convert a Live NodeList to a proper Array
 
-      * @example
 
-      *    var childNodes = wysihtml5.lang.array(document.body.childNodes).get();
 
-      */
 
-     get: function() {
 
-       var i        = 0,
 
-           length   = arr.length,
 
-           newArray = [];
 
-       for (; i<length; i++) {
 
-         newArray.push(arr[i]);
 
-       }
 
-       return newArray;
 
-     },
 
-     /**
 
-      * Creates a new array with the results of calling a provided function on every element in this array.
 
-      * optionally this can be provided as second argument
 
-      *
 
-      * @example
 
-      *    var childNodes = wysihtml5.lang.array([1,2,3,4]).map(function (value, index, array) {
 
-             return value * 2;
 
-      *    });
 
-      *    // => [2,4,6,8]
 
-      */
 
-     map: function(callback, thisArg) {
 
-       if (Array.prototype.map) {
 
-         return arr.map(callback, thisArg);
 
-       } else {
 
-         var len = arr.length >>> 0,
 
-             A = new Array(len),
 
-             i = 0;
 
-         for (; i < len; i++) {
 
-            A[i] = callback.call(thisArg, arr[i], i, arr);
 
-         }
 
-         return A;
 
-       }
 
-     },
 
-     /* ReturnS new array without duplicate entries
 
-      *
 
-      * @example
 
-      *    var uniq = wysihtml5.lang.array([1,2,3,2,1,4]).unique();
 
-      *    // => [1,2,3,4]
 
-      */
 
-     unique: function() {
 
-       var vals = [],
 
-           max = arr.length,
 
-           idx = 0;
 
-       while (idx < max) {
 
-         if (!wysihtml5.lang.array(vals).contains(arr[idx])) {
 
-           vals.push(arr[idx]);
 
-         }
 
-         idx++;
 
-       }
 
-       return vals;
 
-     }
 
-   };
 
- };
 
- ;wysihtml5.lang.Dispatcher = Base.extend(
 
-   /** @scope wysihtml5.lang.Dialog.prototype */ {
 
-   on: function(eventName, handler) {
 
-     this.events = this.events || {};
 
-     this.events[eventName] = this.events[eventName] || [];
 
-     this.events[eventName].push(handler);
 
-     return this;
 
-   },
 
-   off: function(eventName, handler) {
 
-     this.events = this.events || {};
 
-     var i = 0,
 
-         handlers,
 
-         newHandlers;
 
-     if (eventName) {
 
-       handlers    = this.events[eventName] || [],
 
-       newHandlers = [];
 
-       for (; i<handlers.length; i++) {
 
-         if (handlers[i] !== handler && handler) {
 
-           newHandlers.push(handlers[i]);
 
-         }
 
-       }
 
-       this.events[eventName] = newHandlers;
 
-     } else {
 
-       // Clean up all events
 
-       this.events = {};
 
-     }
 
-     return this;
 
-   },
 
-   fire: function(eventName, payload) {
 
-     this.events = this.events || {};
 
-     var handlers = this.events[eventName] || [],
 
-         i        = 0;
 
-     for (; i<handlers.length; i++) {
 
-       handlers[i].call(this, payload);
 
-     }
 
-     return this;
 
-   },
 
-   // deprecated, use .on()
 
-   observe: function() {
 
-     return this.on.apply(this, arguments);
 
-   },
 
-   // deprecated, use .off()
 
-   stopObserving: function() {
 
-     return this.off.apply(this, arguments);
 
-   }
 
- });
 
- ;wysihtml5.lang.object = function(obj) {
 
-   return {
 
-     /**
 
-      * @example
 
-      *    wysihtml5.lang.object({ foo: 1, bar: 1 }).merge({ bar: 2, baz: 3 }).get();
 
-      *    // => { foo: 1, bar: 2, baz: 3 }
 
-      */
 
-     merge: function(otherObj) {
 
-       for (var i in otherObj) {
 
-         obj[i] = otherObj[i];
 
-       }
 
-       return this;
 
-     },
 
-     get: function() {
 
-       return obj;
 
-     },
 
-     /**
 
-      * @example
 
-      *    wysihtml5.lang.object({ foo: 1 }).clone();
 
-      *    // => { foo: 1 }
 
-      *
 
-      *    v0.4.14 adds options for deep clone : wysihtml5.lang.object({ foo: 1 }).clone(true);
 
-      */
 
-     clone: function(deep) {
 
-       var newObj = {},
 
-           i;
 
-       if (obj === null || !wysihtml5.lang.object(obj).isPlainObject()) {
 
-         return obj;
 
-       }
 
-       for (i in obj) {
 
-         if(obj.hasOwnProperty(i)) {
 
-           if (deep) {
 
-             newObj[i] = wysihtml5.lang.object(obj[i]).clone(deep);
 
-           } else {
 
-             newObj[i] = obj[i];
 
-           }
 
-         }
 
-       }
 
-       return newObj;
 
-     },
 
-     /**
 
-      * @example
 
-      *    wysihtml5.lang.object([]).isArray();
 
-      *    // => true
 
-      */
 
-     isArray: function() {
 
-       return Object.prototype.toString.call(obj) === "[object Array]";
 
-     },
 
-     /**
 
-      * @example
 
-      *    wysihtml5.lang.object(function() {}).isFunction();
 
-      *    // => true
 
-      */
 
-     isFunction: function() {
 
-       return Object.prototype.toString.call(obj) === '[object Function]';
 
-     },
 
-     isPlainObject: function () {
 
-       return Object.prototype.toString.call(obj) === '[object Object]';
 
-     }
 
-   };
 
- };
 
- ;(function() {
 
-   var WHITE_SPACE_START = /^\s+/,
 
-       WHITE_SPACE_END   = /\s+$/,
 
-       ENTITY_REG_EXP    = /[&<>\t"]/g,
 
-       ENTITY_MAP = {
 
-         '&': '&',
 
-         '<': '<',
 
-         '>': '>',
 
-         '"': """,
 
-         '\t':"  "
 
-       };
 
-   wysihtml5.lang.string = function(str) {
 
-     str = String(str);
 
-     return {
 
-       /**
 
-        * @example
 
-        *    wysihtml5.lang.string("   foo   ").trim();
 
-        *    // => "foo"
 
-        */
 
-       trim: function() {
 
-         return str.replace(WHITE_SPACE_START, "").replace(WHITE_SPACE_END, "");
 
-       },
 
-       /**
 
-        * @example
 
-        *    wysihtml5.lang.string("Hello #{name}").interpolate({ name: "Christopher" });
 
-        *    // => "Hello Christopher"
 
-        */
 
-       interpolate: function(vars) {
 
-         for (var i in vars) {
 
-           str = this.replace("#{" + i + "}").by(vars[i]);
 
-         }
 
-         return str;
 
-       },
 
-       /**
 
-        * @example
 
-        *    wysihtml5.lang.string("Hello Tom").replace("Tom").with("Hans");
 
-        *    // => "Hello Hans"
 
-        */
 
-       replace: function(search) {
 
-         return {
 
-           by: function(replace) {
 
-             return str.split(search).join(replace);
 
-           }
 
-         };
 
-       },
 
-       /**
 
-        * @example
 
-        *    wysihtml5.lang.string("hello<br>").escapeHTML();
 
-        *    // => "hello<br>"
 
-        */
 
-       escapeHTML: function(linebreaks, convertSpaces) {
 
-         var html = str.replace(ENTITY_REG_EXP, function(c) { return ENTITY_MAP[c]; });
 
-         if (linebreaks) {
 
-           html = html.replace(/(?:\r\n|\r|\n)/g, '<br />');
 
-         }
 
-         if (convertSpaces) {
 
-           html = html.replace(/  /gi, "  ");
 
-         }
 
-         return html;
 
-       }
 
-     };
 
-   };
 
- })();
 
- ;/**
 
-  * Find urls in descendant text nodes of an element and auto-links them
 
-  * Inspired by http://james.padolsey.com/javascript/find-and-replace-text-with-javascript/
 
-  *
 
-  * @param {Element} element Container element in which to search for urls
 
-  *
 
-  * @example
 
-  *    <div id="text-container">Please click here: www.google.com</div>
 
-  *    <script>wysihtml5.dom.autoLink(document.getElementById("text-container"));</script>
 
-  */
 
- (function(wysihtml5) {
 
-   var /**
 
-        * Don't auto-link urls that are contained in the following elements:
 
-        */
 
-       IGNORE_URLS_IN        = wysihtml5.lang.array(["CODE", "PRE", "A", "SCRIPT", "HEAD", "TITLE", "STYLE"]),
 
-       /**
 
-        * revision 1:
 
-        *    /(\S+\.{1}[^\s\,\.\!]+)/g
 
-        *
 
-        * revision 2:
 
-        *    /(\b(((https?|ftp):\/\/)|(www\.))[-A-Z0-9+&@#\/%?=~_|!:,.;\[\]]*[-A-Z0-9+&@#\/%=~_|])/gim
 
-        *
 
-        * put this in the beginning if you don't wan't to match within a word
 
-        *    (^|[\>\(\{\[\s\>])
 
-        */
 
-       URL_REG_EXP           = /((https?:\/\/|www\.)[^\s<]{3,})/gi,
 
-       TRAILING_CHAR_REG_EXP = /([^\w\/\-](,?))$/i,
 
-       MAX_DISPLAY_LENGTH    = 100,
 
-       BRACKETS              = { ")": "(", "]": "[", "}": "{" };
 
-   function autoLink(element, ignoreInClasses) {
 
-     if (_hasParentThatShouldBeIgnored(element, ignoreInClasses)) {
 
-       return element;
 
-     }
 
-     if (element === element.ownerDocument.documentElement) {
 
-       element = element.ownerDocument.body;
 
-     }
 
-     return _parseNode(element, ignoreInClasses);
 
-   }
 
-   /**
 
-    * This is basically a rebuild of
 
-    * the rails auto_link_urls text helper
 
-    */
 
-   function _convertUrlsToLinks(str) {
 
-     return str.replace(URL_REG_EXP, function(match, url) {
 
-       var punctuation = (url.match(TRAILING_CHAR_REG_EXP) || [])[1] || "",
 
-           opening     = BRACKETS[punctuation];
 
-       url = url.replace(TRAILING_CHAR_REG_EXP, "");
 
-       if (url.split(opening).length > url.split(punctuation).length) {
 
-         url = url + punctuation;
 
-         punctuation = "";
 
-       }
 
-       var realUrl    = url,
 
-           displayUrl = url;
 
-       if (url.length > MAX_DISPLAY_LENGTH) {
 
-         displayUrl = displayUrl.substr(0, MAX_DISPLAY_LENGTH) + "...";
 
-       }
 
-       // Add http prefix if necessary
 
-       if (realUrl.substr(0, 4) === "www.") {
 
-         realUrl = "http://" + realUrl;
 
-       }
 
-       return '<a href="' + realUrl + '">' + displayUrl + '</a>' + punctuation;
 
-     });
 
-   }
 
-   /**
 
-    * Creates or (if already cached) returns a temp element
 
-    * for the given document object
 
-    */
 
-   function _getTempElement(context) {
 
-     var tempElement = context._wysihtml5_tempElement;
 
-     if (!tempElement) {
 
-       tempElement = context._wysihtml5_tempElement = context.createElement("div");
 
-     }
 
-     return tempElement;
 
-   }
 
-   /**
 
-    * Replaces the original text nodes with the newly auto-linked dom tree
 
-    */
 
-   function _wrapMatchesInNode(textNode) {
 
-     var parentNode  = textNode.parentNode,
 
-         nodeValue   = wysihtml5.lang.string(textNode.data).escapeHTML(),
 
-         tempElement = _getTempElement(parentNode.ownerDocument);
 
-     // We need to insert an empty/temporary <span /> to fix IE quirks
 
-     // Elsewise IE would strip white space in the beginning
 
-     tempElement.innerHTML = "<span></span>" + _convertUrlsToLinks(nodeValue);
 
-     tempElement.removeChild(tempElement.firstChild);
 
-     while (tempElement.firstChild) {
 
-       // inserts tempElement.firstChild before textNode
 
-       parentNode.insertBefore(tempElement.firstChild, textNode);
 
-     }
 
-     parentNode.removeChild(textNode);
 
-   }
 
-   function _hasParentThatShouldBeIgnored(node, ignoreInClasses) {
 
-     var nodeName;
 
-     while (node.parentNode) {
 
-       node = node.parentNode;
 
-       nodeName = node.nodeName;
 
-       if (node.className && wysihtml5.lang.array(node.className.split(' ')).contains(ignoreInClasses)) {
 
-         return true;
 
-       }
 
-       if (IGNORE_URLS_IN.contains(nodeName)) {
 
-         return true;
 
-       } else if (nodeName === "body") {
 
-         return false;
 
-       }
 
-     }
 
-     return false;
 
-   }
 
-   function _parseNode(element, ignoreInClasses) {
 
-     if (IGNORE_URLS_IN.contains(element.nodeName)) {
 
-       return;
 
-     }
 
-     if (element.className && wysihtml5.lang.array(element.className.split(' ')).contains(ignoreInClasses)) {
 
-       return;
 
-     }
 
-     if (element.nodeType === wysihtml5.TEXT_NODE && element.data.match(URL_REG_EXP)) {
 
-       _wrapMatchesInNode(element);
 
-       return;
 
-     }
 
-     var childNodes        = wysihtml5.lang.array(element.childNodes).get(),
 
-         childNodesLength  = childNodes.length,
 
-         i                 = 0;
 
-     for (; i<childNodesLength; i++) {
 
-       _parseNode(childNodes[i], ignoreInClasses);
 
-     }
 
-     return element;
 
-   }
 
-   wysihtml5.dom.autoLink = autoLink;
 
-   // Reveal url reg exp to the outside
 
-   wysihtml5.dom.autoLink.URL_REG_EXP = URL_REG_EXP;
 
- })(wysihtml5);
 
- ;(function(wysihtml5) {
 
-   var api = wysihtml5.dom;
 
-   api.addClass = function(element, className) {
 
-     var classList = element.classList;
 
-     if (classList) {
 
-       return classList.add(className);
 
-     }
 
-     if (api.hasClass(element, className)) {
 
-       return;
 
-     }
 
-     element.className += " " + className;
 
-   };
 
-   api.removeClass = function(element, className) {
 
-     var classList = element.classList;
 
-     if (classList) {
 
-       return classList.remove(className);
 
-     }
 
-     element.className = element.className.replace(new RegExp("(^|\\s+)" + className + "(\\s+|$)"), " ");
 
-   };
 
-   api.hasClass = function(element, className) {
 
-     var classList = element.classList;
 
-     if (classList) {
 
-       return classList.contains(className);
 
-     }
 
-     var elementClassName = element.className;
 
-     return (elementClassName.length > 0 && (elementClassName == className || new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
 
-   };
 
- })(wysihtml5);
 
- ;wysihtml5.dom.contains = (function() {
 
-   var documentElement = document.documentElement;
 
-   if (documentElement.contains) {
 
-     return function(container, element) {
 
-       if (element.nodeType !== wysihtml5.ELEMENT_NODE) {
 
-         element = element.parentNode;
 
-       }
 
-       return container !== element && container.contains(element);
 
-     };
 
-   } else if (documentElement.compareDocumentPosition) {
 
-     return function(container, element) {
 
-       // https://developer.mozilla.org/en/DOM/Node.compareDocumentPosition
 
-       return !!(container.compareDocumentPosition(element) & 16);
 
-     };
 
-   }
 
- })();
 
- ;/**
 
-  * Converts an HTML fragment/element into a unordered/ordered list
 
-  *
 
-  * @param {Element} element The element which should be turned into a list
 
-  * @param {String} listType The list type in which to convert the tree (either "ul" or "ol")
 
-  * @return {Element} The created list
 
-  *
 
-  * @example
 
-  *    <!-- Assume the following dom: -->
 
-  *    <span id="pseudo-list">
 
-  *      eminem<br>
 
-  *      dr. dre
 
-  *      <div>50 Cent</div>
 
-  *    </span>
 
-  *
 
-  *    <script>
 
-  *      wysihtml5.dom.convertToList(document.getElementById("pseudo-list"), "ul");
 
-  *    </script>
 
-  *
 
-  *    <!-- Will result in: -->
 
-  *    <ul>
 
-  *      <li>eminem</li>
 
-  *      <li>dr. dre</li>
 
-  *      <li>50 Cent</li>
 
-  *    </ul>
 
-  */
 
- wysihtml5.dom.convertToList = (function() {
 
-   function _createListItem(doc, list) {
 
-     var listItem = doc.createElement("li");
 
-     list.appendChild(listItem);
 
-     return listItem;
 
-   }
 
-   function _createList(doc, type) {
 
-     return doc.createElement(type);
 
-   }
 
-   function convertToList(element, listType, uneditableClass) {
 
-     if (element.nodeName === "UL" || element.nodeName === "OL" || element.nodeName === "MENU") {
 
-       // Already a list
 
-       return element;
 
-     }
 
-     var doc               = element.ownerDocument,
 
-         list              = _createList(doc, listType),
 
-         lineBreaks        = element.querySelectorAll("br"),
 
-         lineBreaksLength  = lineBreaks.length,
 
-         childNodes,
 
-         childNodesLength,
 
-         childNode,
 
-         lineBreak,
 
-         parentNode,
 
-         isBlockElement,
 
-         isLineBreak,
 
-         currentListItem,
 
-         i;
 
-     // First find <br> at the end of inline elements and move them behind them
 
-     for (i=0; i<lineBreaksLength; i++) {
 
-       lineBreak = lineBreaks[i];
 
-       while ((parentNode = lineBreak.parentNode) && parentNode !== element && parentNode.lastChild === lineBreak) {
 
-         if (wysihtml5.dom.getStyle("display").from(parentNode) === "block") {
 
-           parentNode.removeChild(lineBreak);
 
-           break;
 
-         }
 
-         wysihtml5.dom.insert(lineBreak).after(lineBreak.parentNode);
 
-       }
 
-     }
 
-     childNodes        = wysihtml5.lang.array(element.childNodes).get();
 
-     childNodesLength  = childNodes.length;
 
-     for (i=0; i<childNodesLength; i++) {
 
-       currentListItem   = currentListItem || _createListItem(doc, list);
 
-       childNode         = childNodes[i];
 
-       isBlockElement    = wysihtml5.dom.getStyle("display").from(childNode) === "block";
 
-       isLineBreak       = childNode.nodeName === "BR";
 
-       // consider uneditable as an inline element
 
-       if (isBlockElement && (!uneditableClass || !wysihtml5.dom.hasClass(childNode, uneditableClass))) {
 
-         // Append blockElement to current <li> if empty, otherwise create a new one
 
-         currentListItem = currentListItem.firstChild ? _createListItem(doc, list) : currentListItem;
 
-         currentListItem.appendChild(childNode);
 
-         currentListItem = null;
 
-         continue;
 
-       }
 
-       if (isLineBreak) {
 
-         // Only create a new list item in the next iteration when the current one has already content
 
-         currentListItem = currentListItem.firstChild ? null : currentListItem;
 
-         continue;
 
-       }
 
-       currentListItem.appendChild(childNode);
 
-     }
 
-     if (childNodes.length === 0) {
 
-       _createListItem(doc, list);
 
-     }
 
-     element.parentNode.replaceChild(list, element);
 
-     return list;
 
-   }
 
-   return convertToList;
 
- })();
 
- ;/**
 
-  * Copy a set of attributes from one element to another
 
-  *
 
-  * @param {Array} attributesToCopy List of attributes which should be copied
 
-  * @return {Object} Returns an object which offers the "from" method which can be invoked with the element where to
 
-  *    copy the attributes from., this again returns an object which provides a method named "to" which can be invoked
 
-  *    with the element where to copy the attributes to (see example)
 
-  *
 
-  * @example
 
-  *    var textarea    = document.querySelector("textarea"),
 
-  *        div         = document.querySelector("div[contenteditable=true]"),
 
-  *        anotherDiv  = document.querySelector("div.preview");
 
-  *    wysihtml5.dom.copyAttributes(["spellcheck", "value", "placeholder"]).from(textarea).to(div).andTo(anotherDiv);
 
-  *
 
-  */
 
- wysihtml5.dom.copyAttributes = function(attributesToCopy) {
 
-   return {
 
-     from: function(elementToCopyFrom) {
 
-       return {
 
-         to: function(elementToCopyTo) {
 
-           var attribute,
 
-               i         = 0,
 
-               length    = attributesToCopy.length;
 
-           for (; i<length; i++) {
 
-             attribute = attributesToCopy[i];
 
-             if (typeof(elementToCopyFrom[attribute]) !== "undefined" && elementToCopyFrom[attribute] !== "") {
 
-               elementToCopyTo[attribute] = elementToCopyFrom[attribute];
 
-             }
 
-           }
 
-           return { andTo: arguments.callee };
 
-         }
 
-       };
 
-     }
 
-   };
 
- };
 
- ;/**
 
-  * Copy a set of styles from one element to another
 
-  * Please note that this only works properly across browsers when the element from which to copy the styles
 
-  * is in the dom
 
-  *
 
-  * Interesting article on how to copy styles
 
-  *
 
-  * @param {Array} stylesToCopy List of styles which should be copied
 
-  * @return {Object} Returns an object which offers the "from" method which can be invoked with the element where to
 
-  *    copy the styles from., this again returns an object which provides a method named "to" which can be invoked
 
-  *    with the element where to copy the styles to (see example)
 
-  *
 
-  * @example
 
-  *    var textarea    = document.querySelector("textarea"),
 
-  *        div         = document.querySelector("div[contenteditable=true]"),
 
-  *        anotherDiv  = document.querySelector("div.preview");
 
-  *    wysihtml5.dom.copyStyles(["overflow-y", "width", "height"]).from(textarea).to(div).andTo(anotherDiv);
 
-  *
 
-  */
 
- (function(dom) {
 
-   /**
 
-    * Mozilla, WebKit and Opera recalculate the computed width when box-sizing: boder-box; is set
 
-    * So if an element has "width: 200px; -moz-box-sizing: border-box; border: 1px;" then
 
-    * its computed css width will be 198px
 
-    *
 
-    * See https://bugzilla.mozilla.org/show_bug.cgi?id=520992
 
-    */
 
-   var BOX_SIZING_PROPERTIES = ["-webkit-box-sizing", "-moz-box-sizing", "-ms-box-sizing", "box-sizing"];
 
-   var shouldIgnoreBoxSizingBorderBox = function(element) {
 
-     if (hasBoxSizingBorderBox(element)) {
 
-        return parseInt(dom.getStyle("width").from(element), 10) < element.offsetWidth;
 
-     }
 
-     return false;
 
-   };
 
-   var hasBoxSizingBorderBox = function(element) {
 
-     var i       = 0,
 
-         length  = BOX_SIZING_PROPERTIES.length;
 
-     for (; i<length; i++) {
 
-       if (dom.getStyle(BOX_SIZING_PROPERTIES[i]).from(element) === "border-box") {
 
-         return BOX_SIZING_PROPERTIES[i];
 
-       }
 
-     }
 
-   };
 
-   dom.copyStyles = function(stylesToCopy) {
 
-     return {
 
-       from: function(element) {
 
-         if (shouldIgnoreBoxSizingBorderBox(element)) {
 
-           stylesToCopy = wysihtml5.lang.array(stylesToCopy).without(BOX_SIZING_PROPERTIES);
 
-         }
 
-         var cssText = "",
 
-             length  = stylesToCopy.length,
 
-             i       = 0,
 
-             property;
 
-         for (; i<length; i++) {
 
-           property = stylesToCopy[i];
 
-           cssText += property + ":" + dom.getStyle(property).from(element) + ";";
 
-         }
 
-         return {
 
-           to: function(element) {
 
-             dom.setStyles(cssText).on(element);
 
-             return { andTo: arguments.callee };
 
-           }
 
-         };
 
-       }
 
-     };
 
-   };
 
- })(wysihtml5.dom);
 
- ;/**
 
-  * Event Delegation
 
-  *
 
-  * @example
 
-  *    wysihtml5.dom.delegate(document.body, "a", "click", function() {
 
-  *      // foo
 
-  *    });
 
-  */
 
- (function(wysihtml5) {
 
-   wysihtml5.dom.delegate = function(container, selector, eventName, handler) {
 
-     return wysihtml5.dom.observe(container, eventName, function(event) {
 
-       var target    = event.target,
 
-           match     = wysihtml5.lang.array(container.querySelectorAll(selector));
 
-       while (target && target !== container) {
 
-         if (match.contains(target)) {
 
-           handler.call(target, event);
 
-           break;
 
-         }
 
-         target = target.parentNode;
 
-       }
 
-     });
 
-   };
 
- })(wysihtml5);
 
- ;// TODO: Refactor dom tree traversing here
 
- (function(wysihtml5) {
 
-   wysihtml5.dom.domNode = function(node) {
 
-     var defaultNodeTypes = [wysihtml5.ELEMENT_NODE, wysihtml5.TEXT_NODE];
 
-     var _isBlankText = function(node) {
 
-       return node.nodeType === wysihtml5.TEXT_NODE && (/^\s*$/g).test(node.data);
 
-     };
 
-     return {
 
-       // var node = wysihtml5.dom.domNode(element).prev({nodeTypes: [1,3], ignoreBlankTexts: true});
 
-       prev: function(options) {
 
-         var prevNode = node.previousSibling,
 
-             types = (options && options.nodeTypes) ? options.nodeTypes : defaultNodeTypes;
 
-         
 
-         if (!prevNode) {
 
-           return null;
 
-         }
 
-         if (
 
-           (!wysihtml5.lang.array(types).contains(prevNode.nodeType)) || // nodeTypes check.
 
-           (options && options.ignoreBlankTexts && _isBlankText(prevNode)) // Blank text nodes bypassed if set
 
-         ) {
 
-           return wysihtml5.dom.domNode(prevNode).prev(options);
 
-         }
 
-         
 
-         return prevNode;
 
-       },
 
-       // var node = wysihtml5.dom.domNode(element).next({nodeTypes: [1,3], ignoreBlankTexts: true});
 
-       next: function(options) {
 
-         var nextNode = node.nextSibling,
 
-             types = (options && options.nodeTypes) ? options.nodeTypes : defaultNodeTypes;
 
-         
 
-         if (!nextNode) {
 
-           return null;
 
-         }
 
-         if (
 
-           (!wysihtml5.lang.array(types).contains(nextNode.nodeType)) || // nodeTypes check.
 
-           (options && options.ignoreBlankTexts && _isBlankText(nextNode)) // blank text nodes bypassed if set
 
-         ) {
 
-           return wysihtml5.dom.domNode(nextNode).next(options);
 
-         }
 
-         
 
-         return nextNode;
 
-       }
 
-     };
 
-   };
 
- })(wysihtml5);;/**
 
-  * Returns the given html wrapped in a div element
 
-  *
 
-  * Fixing IE's inability to treat unknown elements (HTML5 section, article, ...) correctly
 
-  * when inserted via innerHTML
 
-  *
 
-  * @param {String} html The html which should be wrapped in a dom element
 
-  * @param {Obejct} [context] Document object of the context the html belongs to
 
-  *
 
-  * @example
 
-  *    wysihtml5.dom.getAsDom("<article>foo</article>");
 
-  */
 
- wysihtml5.dom.getAsDom = (function() {
 
-   var _innerHTMLShiv = function(html, context) {
 
-     var tempElement = context.createElement("div");
 
-     tempElement.style.display = "none";
 
-     context.body.appendChild(tempElement);
 
-     // IE throws an exception when trying to insert <frameset></frameset> via innerHTML
 
-     try { tempElement.innerHTML = html; } catch(e) {}
 
-     context.body.removeChild(tempElement);
 
-     return tempElement;
 
-   };
 
-   /**
 
-    * Make sure IE supports HTML5 tags, which is accomplished by simply creating one instance of each element
 
-    */
 
-   var _ensureHTML5Compatibility = function(context) {
 
-     if (context._wysihtml5_supportsHTML5Tags) {
 
-       return;
 
-     }
 
-     for (var i=0, length=HTML5_ELEMENTS.length; i<length; i++) {
 
-       context.createElement(HTML5_ELEMENTS[i]);
 
-     }
 
-     context._wysihtml5_supportsHTML5Tags = true;
 
-   };
 
-   /**
 
-    * List of html5 tags
 
-    * taken from http://simon.html5.org/html5-elements
 
-    */
 
-   var HTML5_ELEMENTS = [
 
-     "abbr", "article", "aside", "audio", "bdi", "canvas", "command", "datalist", "details", "figcaption",
 
-     "figure", "footer", "header", "hgroup", "keygen", "mark", "meter", "nav", "output", "progress",
 
-     "rp", "rt", "ruby", "svg", "section", "source", "summary", "time", "track", "video", "wbr"
 
-   ];
 
-   return function(html, context) {
 
-     context = context || document;
 
-     var tempElement;
 
-     if (typeof(html) === "object" && html.nodeType) {
 
-       tempElement = context.createElement("div");
 
-       tempElement.appendChild(html);
 
-     } else if (wysihtml5.browser.supportsHTML5Tags(context)) {
 
-       tempElement = context.createElement("div");
 
-       tempElement.innerHTML = html;
 
-     } else {
 
-       _ensureHTML5Compatibility(context);
 
-       tempElement = _innerHTMLShiv(html, context);
 
-     }
 
-     return tempElement;
 
-   };
 
- })();
 
- ;/**
 
-  * Walks the dom tree from the given node up until it finds a match
 
-  * Designed for optimal performance.
 
-  *
 
-  * @param {Element} node The from which to check the parent nodes
 
-  * @param {Object} matchingSet Object to match against (possible properties: nodeName, className, classRegExp)
 
-  * @param {Number} [levels] How many parents should the function check up from the current node (defaults to 50)
 
-  * @return {null|Element} Returns the first element that matched the desiredNodeName(s)
 
-  * @example
 
-  *    var listElement = wysihtml5.dom.getParentElement(document.querySelector("li"), { nodeName: ["MENU", "UL", "OL"] });
 
-  *    // ... or ...
 
-  *    var unorderedListElement = wysihtml5.dom.getParentElement(document.querySelector("li"), { nodeName: "UL" });
 
-  *    // ... or ...
 
-  *    var coloredElement = wysihtml5.dom.getParentElement(myTextNode, { nodeName: "SPAN", className: "wysiwyg-color-red", classRegExp: /wysiwyg-color-[a-z]/g });
 
-  */
 
- wysihtml5.dom.getParentElement = (function() {
 
-   function _isSameNodeName(nodeName, desiredNodeNames) {
 
-     if (!desiredNodeNames || !desiredNodeNames.length) {
 
-       return true;
 
-     }
 
-     if (typeof(desiredNodeNames) === "string") {
 
-       return nodeName === desiredNodeNames;
 
-     } else {
 
-       return wysihtml5.lang.array(desiredNodeNames).contains(nodeName);
 
-     }
 
-   }
 
-   function _isElement(node) {
 
-     return node.nodeType === wysihtml5.ELEMENT_NODE;
 
-   }
 
-   function _hasClassName(element, className, classRegExp) {
 
-     var classNames = (element.className || "").match(classRegExp) || [];
 
-     if (!className) {
 
-       return !!classNames.length;
 
-     }
 
-     return classNames[classNames.length - 1] === className;
 
-   }
 
-   function _hasStyle(element, cssStyle, styleRegExp) {
 
-     var styles = (element.getAttribute('style') || "").match(styleRegExp) || [];
 
-     if (!cssStyle) {
 
-       return !!styles.length;
 
-     }
 
-     return styles[styles.length - 1] === cssStyle;
 
-   }
 
-   return function(node, matchingSet, levels, container) {
 
-     var findByStyle = (matchingSet.cssStyle || matchingSet.styleRegExp),
 
-         findByClass = (matchingSet.className || matchingSet.classRegExp);
 
-     levels = levels || 50; // Go max 50 nodes upwards from current node
 
-     while (levels-- && node && node.nodeName !== "BODY" && (!container || node !== container)) {
 
-       if (_isElement(node) && _isSameNodeName(node.nodeName, matchingSet.nodeName) &&
 
-           (!findByStyle || _hasStyle(node, matchingSet.cssStyle, matchingSet.styleRegExp)) &&
 
-           (!findByClass || _hasClassName(node, matchingSet.className, matchingSet.classRegExp))
 
-       ) {
 
-         return node;
 
-       }
 
-       node = node.parentNode;
 
-     }
 
-     return null;
 
-   };
 
- })();
 
- ;/**
 
-  * Get element's style for a specific css property
 
-  *
 
-  * @param {Element} element The element on which to retrieve the style
 
-  * @param {String} property The CSS property to retrieve ("float", "display", "text-align", ...)
 
-  *
 
-  * @example
 
-  *    wysihtml5.dom.getStyle("display").from(document.body);
 
-  *    // => "block"
 
-  */
 
- wysihtml5.dom.getStyle = (function() {
 
-   var stylePropertyMapping = {
 
-         "float": ("styleFloat" in document.createElement("div").style) ? "styleFloat" : "cssFloat"
 
-       },
 
-       REG_EXP_CAMELIZE = /\-[a-z]/g;
 
-   function camelize(str) {
 
-     return str.replace(REG_EXP_CAMELIZE, function(match) {
 
-       return match.charAt(1).toUpperCase();
 
-     });
 
-   }
 
-   return function(property) {
 
-     return {
 
-       from: function(element) {
 
-         if (element.nodeType !== wysihtml5.ELEMENT_NODE) {
 
-           return;
 
-         }
 
-         var doc               = element.ownerDocument,
 
-             camelizedProperty = stylePropertyMapping[property] || camelize(property),
 
-             style             = element.style,
 
-             currentStyle      = element.currentStyle,
 
-             styleValue        = style[camelizedProperty];
 
-         if (styleValue) {
 
-           return styleValue;
 
-         }
 
-         // currentStyle is no standard and only supported by Opera and IE but it has one important advantage over the standard-compliant
 
-         // window.getComputedStyle, since it returns css property values in their original unit:
 
-         // If you set an elements width to "50%", window.getComputedStyle will give you it's current width in px while currentStyle
 
-         // gives you the original "50%".
 
-         // Opera supports both, currentStyle and window.getComputedStyle, that's why checking for currentStyle should have higher prio
 
-         if (currentStyle) {
 
-           try {
 
-             return currentStyle[camelizedProperty];
 
-           } catch(e) {
 
-             //ie will occasionally fail for unknown reasons. swallowing exception
 
-           }
 
-         }
 
-         var win                 = doc.defaultView || doc.parentWindow,
 
-             needsOverflowReset  = (property === "height" || property === "width") && element.nodeName === "TEXTAREA",
 
-             originalOverflow,
 
-             returnValue;
 
-         if (win.getComputedStyle) {
 
-           // Chrome and Safari both calculate a wrong width and height for textareas when they have scroll bars
 
-           // therfore we remove and restore the scrollbar and calculate the value in between
 
-           if (needsOverflowReset) {
 
-             originalOverflow = style.overflow;
 
-             style.overflow = "hidden";
 
-           }
 
-           returnValue = win.getComputedStyle(element, null).getPropertyValue(property);
 
-           if (needsOverflowReset) {
 
-             style.overflow = originalOverflow || "";
 
-           }
 
-           return returnValue;
 
-         }
 
-       }
 
-     };
 
-   };
 
- })();
 
- ;wysihtml5.dom.getTextNodes = function(node, ingoreEmpty){
 
-   var all = [];
 
-   for (node=node.firstChild;node;node=node.nextSibling){
 
-     if (node.nodeType == 3) {
 
-       if (!ingoreEmpty || !(/^\s*$/).test(node.innerText || node.textContent)) {
 
-         all.push(node);
 
-       }
 
-     } else {
 
-       all = all.concat(wysihtml5.dom.getTextNodes(node, ingoreEmpty));
 
-     }
 
-   }
 
-   return all;
 
- };;/**
 
-  * High performant way to check whether an element with a specific tag name is in the given document
 
-  * Optimized for being heavily executed
 
-  * Unleashes the power of live node lists
 
-  *
 
-  * @param {Object} doc The document object of the context where to check
 
-  * @param {String} tagName Upper cased tag name
 
-  * @example
 
-  *    wysihtml5.dom.hasElementWithTagName(document, "IMG");
 
-  */
 
- wysihtml5.dom.hasElementWithTagName = (function() {
 
-   var LIVE_CACHE          = {},
 
-       DOCUMENT_IDENTIFIER = 1;
 
-   function _getDocumentIdentifier(doc) {
 
-     return doc._wysihtml5_identifier || (doc._wysihtml5_identifier = DOCUMENT_IDENTIFIER++);
 
-   }
 
-   return function(doc, tagName) {
 
-     var key         = _getDocumentIdentifier(doc) + ":" + tagName,
 
-         cacheEntry  = LIVE_CACHE[key];
 
-     if (!cacheEntry) {
 
-       cacheEntry = LIVE_CACHE[key] = doc.getElementsByTagName(tagName);
 
-     }
 
-     return cacheEntry.length > 0;
 
-   };
 
- })();
 
- ;/**
 
-  * High performant way to check whether an element with a specific class name is in the given document
 
-  * Optimized for being heavily executed
 
-  * Unleashes the power of live node lists
 
-  *
 
-  * @param {Object} doc The document object of the context where to check
 
-  * @param {String} tagName Upper cased tag name
 
-  * @example
 
-  *    wysihtml5.dom.hasElementWithClassName(document, "foobar");
 
-  */
 
- (function(wysihtml5) {
 
-   var LIVE_CACHE          = {},
 
-       DOCUMENT_IDENTIFIER = 1;
 
-   function _getDocumentIdentifier(doc) {
 
-     return doc._wysihtml5_identifier || (doc._wysihtml5_identifier = DOCUMENT_IDENTIFIER++);
 
-   }
 
-   wysihtml5.dom.hasElementWithClassName = function(doc, className) {
 
-     // getElementsByClassName is not supported by IE<9
 
-     // but is sometimes mocked via library code (which then doesn't return live node lists)
 
-     if (!wysihtml5.browser.supportsNativeGetElementsByClassName()) {
 
-       return !!doc.querySelector("." + className);
 
-     }
 
-     var key         = _getDocumentIdentifier(doc) + ":" + className,
 
-         cacheEntry  = LIVE_CACHE[key];
 
-     if (!cacheEntry) {
 
-       cacheEntry = LIVE_CACHE[key] = doc.getElementsByClassName(className);
 
-     }
 
-     return cacheEntry.length > 0;
 
-   };
 
- })(wysihtml5);
 
- ;wysihtml5.dom.insert = function(elementToInsert) {
 
-   return {
 
-     after: function(element) {
 
-       element.parentNode.insertBefore(elementToInsert, element.nextSibling);
 
-     },
 
-     before: function(element) {
 
-       element.parentNode.insertBefore(elementToInsert, element);
 
-     },
 
-     into: function(element) {
 
-       element.appendChild(elementToInsert);
 
-     }
 
-   };
 
- };
 
- ;wysihtml5.dom.insertCSS = function(rules) {
 
-   rules = rules.join("\n");
 
-   return {
 
-     into: function(doc) {
 
-       var styleElement = doc.createElement("style");
 
-       styleElement.type = "text/css";
 
-       if (styleElement.styleSheet) {
 
-         styleElement.styleSheet.cssText = rules;
 
-       } else {
 
-         styleElement.appendChild(doc.createTextNode(rules));
 
-       }
 
-       var link = doc.querySelector("head link");
 
-       if (link) {
 
-         link.parentNode.insertBefore(styleElement, link);
 
-         return;
 
-       } else {
 
-         var head = doc.querySelector("head");
 
-         if (head) {
 
-           head.appendChild(styleElement);
 
-         }
 
-       }
 
-     }
 
-   };
 
- };
 
- ;// TODO: Refactor dom tree traversing here
 
- (function(wysihtml5) {
 
-   wysihtml5.dom.lineBreaks = function(node) {
 
-     function _isLineBreak(n) {
 
-       return n.nodeName === "BR";
 
-     }
 
-     /**
 
-      * Checks whether the elment causes a visual line break
 
-      * (<br> or block elements)
 
-      */
 
-     function _isLineBreakOrBlockElement(element) {
 
-       if (_isLineBreak(element)) {
 
-         return true;
 
-       }
 
-       if (wysihtml5.dom.getStyle("display").from(element) === "block") {
 
-         return true;
 
-       }
 
-       return false;
 
-     }
 
-     return {
 
-       /* wysihtml5.dom.lineBreaks(element).add();
 
-        *
 
-        * Adds line breaks before and after the given node if the previous and next siblings
 
-        * aren't already causing a visual line break (block element or <br>)
 
-        */
 
-       add: function(options) {
 
-         var doc             = node.ownerDocument,
 
-           nextSibling     = wysihtml5.dom.domNode(node).next({ignoreBlankTexts: true}),
 
-           previousSibling = wysihtml5.dom.domNode(node).prev({ignoreBlankTexts: true});
 
-         if (nextSibling && !_isLineBreakOrBlockElement(nextSibling)) {
 
-           wysihtml5.dom.insert(doc.createElement("br")).after(node);
 
-         }
 
-         if (previousSibling && !_isLineBreakOrBlockElement(previousSibling)) {
 
-           wysihtml5.dom.insert(doc.createElement("br")).before(node);
 
-         }
 
-       },
 
-       /* wysihtml5.dom.lineBreaks(element).remove();
 
-        *
 
-        * Removes line breaks before and after the given node
 
-        */
 
-       remove: function(options) {
 
-         var nextSibling     = wysihtml5.dom.domNode(node).next({ignoreBlankTexts: true}),
 
-             previousSibling = wysihtml5.dom.domNode(node).prev({ignoreBlankTexts: true});
 
-         if (nextSibling && _isLineBreak(nextSibling)) {
 
-           nextSibling.parentNode.removeChild(nextSibling);
 
-         }
 
-         if (previousSibling && _isLineBreak(previousSibling)) {
 
-           previousSibling.parentNode.removeChild(previousSibling);
 
-         }
 
-       }
 
-     };
 
-   };
 
- })(wysihtml5);;/**
 
-  * Method to set dom events
 
-  *
 
-  * @example
 
-  *    wysihtml5.dom.observe(iframe.contentWindow.document.body, ["focus", "blur"], function() { ... });
 
-  */
 
- wysihtml5.dom.observe = function(element, eventNames, handler) {
 
-   eventNames = typeof(eventNames) === "string" ? [eventNames] : eventNames;
 
-   var handlerWrapper,
 
-       eventName,
 
-       i       = 0,
 
-       length  = eventNames.length;
 
-   for (; i<length; i++) {
 
-     eventName = eventNames[i];
 
-     if (element.addEventListener) {
 
-       element.addEventListener(eventName, handler, false);
 
-     } else {
 
-       handlerWrapper = function(event) {
 
-         if (!("target" in event)) {
 
-           event.target = event.srcElement;
 
-         }
 
-         event.preventDefault = event.preventDefault || function() {
 
-           this.returnValue = false;
 
-         };
 
-         event.stopPropagation = event.stopPropagation || function() {
 
-           this.cancelBubble = true;
 
-         };
 
-         handler.call(element, event);
 
-       };
 
-       element.attachEvent("on" + eventName, handlerWrapper);
 
-     }
 
-   }
 
-   return {
 
-     stop: function() {
 
-       var eventName,
 
-           i       = 0,
 
-           length  = eventNames.length;
 
-       for (; i<length; i++) {
 
-         eventName = eventNames[i];
 
-         if (element.removeEventListener) {
 
-           element.removeEventListener(eventName, handler, false);
 
-         } else {
 
-           element.detachEvent("on" + eventName, handlerWrapper);
 
-         }
 
-       }
 
-     }
 
-   };
 
- };
 
- ;/**
 
-  * HTML Sanitizer
 
-  * Rewrites the HTML based on given rules
 
-  *
 
-  * @param {Element|String} elementOrHtml HTML String to be sanitized OR element whose content should be sanitized
 
-  * @param {Object} [rules] List of rules for rewriting the HTML, if there's no rule for an element it will
 
-  *    be converted to a "span". Each rule is a key/value pair where key is the tag to convert, and value the
 
-  *    desired substitution.
 
-  * @param {Object} context Document object in which to parse the html, needed to sandbox the parsing
 
-  *
 
-  * @return {Element|String} Depends on the elementOrHtml parameter. When html then the sanitized html as string elsewise the element.
 
-  *
 
-  * @example
 
-  *    var userHTML = '<div id="foo" onclick="alert(1);"><p><font color="red">foo</font><script>alert(1);</script></p></div>';
 
-  *    wysihtml5.dom.parse(userHTML, {
 
-  *      tags {
 
-  *        p:      "div",      // Rename p tags to div tags
 
-  *        font:   "span"      // Rename font tags to span tags
 
-  *        div:    true,       // Keep them, also possible (same result when passing: "div" or true)
 
-  *        script: undefined   // Remove script elements
 
-  *      }
 
-  *    });
 
-  *    // => <div><div><span>foo bar</span></div></div>
 
-  *
 
-  *    var userHTML = '<table><tbody><tr><td>I'm a table!</td></tr></tbody></table>';
 
-  *    wysihtml5.dom.parse(userHTML);
 
-  *    // => '<span><span><span><span>I'm a table!</span></span></span></span>'
 
-  *
 
-  *    var userHTML = '<div>foobar<br>foobar</div>';
 
-  *    wysihtml5.dom.parse(userHTML, {
 
-  *      tags: {
 
-  *        div: undefined,
 
-  *        br:  true
 
-  *      }
 
-  *    });
 
-  *    // => ''
 
-  *
 
-  *    var userHTML = '<div class="red">foo</div><div class="pink">bar</div>';
 
-  *    wysihtml5.dom.parse(userHTML, {
 
-  *      classes: {
 
-  *        red:    1,
 
-  *        green:  1
 
-  *      },
 
-  *      tags: {
 
-  *        div: {
 
-  *          rename_tag:     "p"
 
-  *        }
 
-  *      }
 
-  *    });
 
-  *    // => '<p class="red">foo</p><p>bar</p>'
 
-  */
 
- wysihtml5.dom.parse = function(elementOrHtml_current, config_current) {
 
-   /* TODO: Currently escaped module pattern as otherwise folloowing default swill be shared among multiple editors.
 
-    * Refactor whole code as this method while workind is kind of awkward too */
 
-   /**
 
-    * It's not possible to use a XMLParser/DOMParser as HTML5 is not always well-formed XML
 
-    * new DOMParser().parseFromString('<img src="foo.gif">') will cause a parseError since the
 
-    * node isn't closed
 
-    *
 
-    * Therefore we've to use the browser's ordinary HTML parser invoked by setting innerHTML.
 
-    */
 
-   var NODE_TYPE_MAPPING = {
 
-         "1": _handleElement,
 
-         "3": _handleText,
 
-         "8": _handleComment
 
-       },
 
-       // Rename unknown tags to this
 
-       DEFAULT_NODE_NAME   = "span",
 
-       WHITE_SPACE_REG_EXP = /\s+/,
 
-       defaultRules        = { tags: {}, classes: {} },
 
-       currentRules        = {};
 
-   /**
 
-    * Iterates over all childs of the element, recreates them, appends them into a document fragment
 
-    * which later replaces the entire body content
 
-    */
 
-    function parse(elementOrHtml, config) {
 
-     wysihtml5.lang.object(currentRules).merge(defaultRules).merge(config.rules).get();
 
-     var context       = config.context || elementOrHtml.ownerDocument || document,
 
-         fragment      = context.createDocumentFragment(),
 
-         isString      = typeof(elementOrHtml) === "string",
 
-         clearInternals = false,
 
-         element,
 
-         newNode,
 
-         firstChild;
 
-     if (config.clearInternals === true) {
 
-       clearInternals = true;
 
-     }
 
-     if (isString) {
 
-       element = wysihtml5.dom.getAsDom(elementOrHtml, context);
 
-     } else {
 
-       element = elementOrHtml;
 
-     }
 
-     if (currentRules.selectors) {
 
-       _applySelectorRules(element, currentRules.selectors);
 
-     }
 
-     while (element.firstChild) {
 
-       firstChild = element.firstChild;
 
-       newNode = _convert(firstChild, config.cleanUp, clearInternals, config.uneditableClass);
 
-       if (newNode) {
 
-         fragment.appendChild(newNode);
 
-       }
 
-       if (firstChild !== newNode) {
 
-         element.removeChild(firstChild);
 
-       }
 
-     }
 
-     if (config.unjoinNbsps) {
 
-       // replace joined non-breakable spaces with unjoined
 
-       var txtnodes = wysihtml5.dom.getTextNodes(fragment);
 
-       for (var n = txtnodes.length; n--;) {
 
-         txtnodes[n].nodeValue = txtnodes[n].nodeValue.replace(/([\S\u00A0])\u00A0/gi, "$1 ");
 
-       }
 
-     }
 
-     // Clear element contents
 
-     element.innerHTML = "";
 
-     // Insert new DOM tree
 
-     element.appendChild(fragment);
 
-     return isString ? wysihtml5.quirks.getCorrectInnerHTML(element) : element;
 
-   }
 
-   function _convert(oldNode, cleanUp, clearInternals, uneditableClass) {
 
-     var oldNodeType     = oldNode.nodeType,
 
-         oldChilds       = oldNode.childNodes,
 
-         oldChildsLength = oldChilds.length,
 
-         method          = NODE_TYPE_MAPPING[oldNodeType],
 
-         i               = 0,
 
-         fragment,
 
-         newNode,
 
-         newChild;
 
-     // Passes directly elemets with uneditable class
 
-     if (uneditableClass && oldNodeType === 1 && wysihtml5.dom.hasClass(oldNode, uneditableClass)) {
 
-         return oldNode;
 
-     }
 
-     newNode = method && method(oldNode, clearInternals);
 
-     // Remove or unwrap node in case of return value null or false
 
-     if (!newNode) {
 
-         if (newNode === false) {
 
-             // false defines that tag should be removed but contents should remain (unwrap)
 
-             fragment = oldNode.ownerDocument.createDocumentFragment();
 
-             for (i = oldChildsLength; i--;) {
 
-               if (oldChilds[i]) {
 
-                 newChild = _convert(oldChilds[i], cleanUp, clearInternals, uneditableClass);
 
-                 if (newChild) {
 
-                   if (oldChilds[i] === newChild) {
 
-                     i--;
 
-                   }
 
-                   fragment.insertBefore(newChild, fragment.firstChild);
 
-                 }
 
-               }
 
-             }
 
-             if (wysihtml5.dom.getStyle("display").from(oldNode) === "block") {
 
-               fragment.appendChild(oldNode.ownerDocument.createElement("br"));
 
-             }
 
-             // TODO: try to minimize surplus spaces
 
-             if (wysihtml5.lang.array([
 
-                 "div", "pre", "p",
 
-                 "table", "td", "th",
 
-                 "ul", "ol", "li",
 
-                 "dd", "dl",
 
-                 "footer", "header", "section",
 
-                 "h1", "h2", "h3", "h4", "h5", "h6"
 
-             ]).contains(oldNode.nodeName.toLowerCase()) && oldNode.parentNode.lastChild !== oldNode) {
 
-                 // add space at first when unwraping non-textflow elements
 
-                 if (!oldNode.nextSibling || oldNode.nextSibling.nodeType !== 3 || !(/^\s/).test(oldNode.nextSibling.nodeValue)) {
 
-                   fragment.appendChild(oldNode.ownerDocument.createTextNode(" "));
 
-                 }
 
-             }
 
-             if (fragment.normalize) {
 
-               fragment.normalize();
 
-             }
 
-             return fragment;
 
-         } else {
 
-           // Remove
 
-           return null;
 
-         }
 
-     }
 
-     // Converts all childnodes
 
-     for (i=0; i<oldChildsLength; i++) {
 
-       if (oldChilds[i]) {
 
-         newChild = _convert(oldChilds[i], cleanUp, clearInternals, uneditableClass);
 
-         if (newChild) {
 
-           if (oldChilds[i] === newChild) {
 
-             i--;
 
-           }
 
-           newNode.appendChild(newChild);
 
-         }
 
-       }
 
-     }
 
-     // Cleanup senseless <span> elements
 
-     if (cleanUp &&
 
-         newNode.nodeName.toLowerCase() === DEFAULT_NODE_NAME &&
 
-         (!newNode.childNodes.length ||
 
-          ((/^\s*$/gi).test(newNode.innerHTML) && (clearInternals || (oldNode.className !== "_wysihtml5-temp-placeholder" && oldNode.className !== "rangySelectionBoundary"))) ||
 
-          !newNode.attributes.length)
 
-         ) {
 
-       fragment = newNode.ownerDocument.createDocumentFragment();
 
-       while (newNode.firstChild) {
 
-         fragment.appendChild(newNode.firstChild);
 
-       }
 
-       if (fragment.normalize) {
 
-         fragment.normalize();
 
-       }
 
-       return fragment;
 
-     }
 
-     if (newNode.normalize) {
 
-       newNode.normalize();
 
-     }
 
-     return newNode;
 
-   }
 
-   function _applySelectorRules (element, selectorRules) {
 
-     var sel, method, els;
 
-     for (sel in selectorRules) {
 
-       if (selectorRules.hasOwnProperty(sel)) {
 
-         if (wysihtml5.lang.object(selectorRules[sel]).isFunction()) {
 
-           method = selectorRules[sel];
 
-         } else if (typeof(selectorRules[sel]) === "string" && elementHandlingMethods[selectorRules[sel]]) {
 
-           method = elementHandlingMethods[selectorRules[sel]];
 
-         }
 
-         els = element.querySelectorAll(sel);
 
-         for (var i = els.length; i--;) {
 
-           method(els[i]);
 
-         }
 
-       }
 
-     }
 
-   }
 
-   function _handleElement(oldNode, clearInternals) {
 
-     var rule,
 
-         newNode,
 
-         tagRules    = currentRules.tags,
 
-         nodeName    = oldNode.nodeName.toLowerCase(),
 
-         scopeName   = oldNode.scopeName,
 
-         renameTag;
 
-     /**
 
-      * We already parsed that element
 
-      * ignore it! (yes, this sometimes happens in IE8 when the html is invalid)
 
-      */
 
-     if (oldNode._wysihtml5) {
 
-       return null;
 
-     }
 
-     oldNode._wysihtml5 = 1;
 
-     if (oldNode.className === "wysihtml5-temp") {
 
-       return null;
 
-     }
 
-     /**
 
-      * IE is the only browser who doesn't include the namespace in the
 
-      * nodeName, that's why we have to prepend it by ourselves
 
-      * scopeName is a proprietary IE feature
 
-      * read more here http://msdn.microsoft.com/en-us/library/ms534388(v=vs.85).aspx
 
-      */
 
-     if (scopeName && scopeName != "HTML") {
 
-       nodeName = scopeName + ":" + nodeName;
 
-     }
 
-     /**
 
-      * Repair node
 
-      * IE is a bit bitchy when it comes to invalid nested markup which includes unclosed tags
 
-      * A <p> doesn't need to be closed according HTML4-5 spec, we simply replace it with a <div> to preserve its content and layout
 
-      */
 
-     if ("outerHTML" in oldNode) {
 
-       if (!wysihtml5.browser.autoClosesUnclosedTags() &&
 
-           oldNode.nodeName === "P" &&
 
-           oldNode.outerHTML.slice(-4).toLowerCase() !== "</p>") {
 
-         nodeName = "div";
 
-       }
 
-     }
 
-     if (nodeName in tagRules) {
 
-       rule = tagRules[nodeName];
 
-       if (!rule || rule.remove) {
 
-         return null;
 
-       } else if (rule.unwrap) {
 
-         return false;
 
-       }
 
-       rule = typeof(rule) === "string" ? { rename_tag: rule } : rule;
 
-     } else if (oldNode.firstChild) {
 
-       rule = { rename_tag: DEFAULT_NODE_NAME };
 
-     } else {
 
-       // Remove empty unknown elements
 
-       return null;
 
-     }
 
-     // tests if type condition is met or node should be removed/unwrapped/renamed
 
-     if (rule.one_of_type && !_testTypes(oldNode, currentRules, rule.one_of_type, clearInternals)) {
 
-       if (rule.remove_action) {
 
-         if (rule.remove_action === "unwrap") {
 
-           return false;
 
-         } else if (rule.remove_action === "rename") {
 
-           renameTag = rule.remove_action_rename_to || DEFAULT_NODE_NAME;
 
-         } else {
 
-           return null;
 
-         }
 
-       } else {
 
-         return null;
 
-       }
 
-     }
 
-     newNode = oldNode.ownerDocument.createElement(renameTag || rule.rename_tag || nodeName);
 
-     _handleAttributes(oldNode, newNode, rule, clearInternals);
 
-     _handleStyles(oldNode, newNode, rule);
 
-     oldNode = null;
 
-     if (newNode.normalize) { newNode.normalize(); }
 
-     return newNode;
 
-   }
 
-   function _testTypes(oldNode, rules, types, clearInternals) {
 
-     var definition, type;
 
-     // do not interfere with placeholder span or pasting caret position is not maintained
 
-     if (oldNode.nodeName === "SPAN" && !clearInternals && (oldNode.className === "_wysihtml5-temp-placeholder" || oldNode.className === "rangySelectionBoundary")) {
 
-       return true;
 
-     }
 
-     for (type in types) {
 
-       if (types.hasOwnProperty(type) && rules.type_definitions && rules.type_definitions[type]) {
 
-         definition = rules.type_definitions[type];
 
-         if (_testType(oldNode, definition)) {
 
-           return true;
 
-         }
 
-       }
 
-     }
 
-     return false;
 
-   }
 
-   function array_contains(a, obj) {
 
-       var i = a.length;
 
-       while (i--) {
 
-          if (a[i] === obj) {
 
-              return true;
 
-          }
 
-       }
 
-       return false;
 
-   }
 
-   function _testType(oldNode, definition) {
 
-     var nodeClasses = oldNode.getAttribute("class"),
 
-         nodeStyles =  oldNode.getAttribute("style"),
 
-         classesLength, s, s_corrected, a, attr, currentClass, styleProp;
 
-     // test for methods
 
-     if (definition.methods) {
 
-       for (var m in definition.methods) {
 
-         if (definition.methods.hasOwnProperty(m) && typeCeckMethods[m]) {
 
-           if (typeCeckMethods[m](oldNode)) {
 
-             return true;
 
-           }
 
-         }
 
-       }
 
-     }
 
-     // test for classes, if one found return true
 
-     if (nodeClasses && definition.classes) {
 
-       nodeClasses = nodeClasses.replace(/^\s+/g, '').replace(/\s+$/g, '').split(WHITE_SPACE_REG_EXP);
 
-       classesLength = nodeClasses.length;
 
-       for (var i = 0; i < classesLength; i++) {
 
-         if (definition.classes[nodeClasses[i]]) {
 
-           return true;
 
-         }
 
-       }
 
-     }
 
-     // test for styles, if one found return true
 
-     if (nodeStyles && definition.styles) {
 
-       nodeStyles = nodeStyles.split(';');
 
-       for (s in definition.styles) {
 
-         if (definition.styles.hasOwnProperty(s)) {
 
-           for (var sp = nodeStyles.length; sp--;) {
 
-             styleProp = nodeStyles[sp].split(':');
 
-             if (styleProp[0].replace(/\s/g, '').toLowerCase() === s) {
 
-               if (definition.styles[s] === true || definition.styles[s] === 1 || wysihtml5.lang.array(definition.styles[s]).contains(styleProp[1].replace(/\s/g, '').toLowerCase()) ) {
 
-                 return true;
 
-               }
 
-             }
 
-           }
 
-         }
 
-       }
 
-     }
 
-     // test for attributes in general against regex match
 
-     if (definition.attrs) {
 
-         for (a in definition.attrs) {
 
-             if (definition.attrs.hasOwnProperty(a)) {
 
-                 attr = wysihtml5.dom.getAttribute(oldNode, a);
 
-                 if (typeof(attr) === "string") {
 
-                     if (attr.search(definition.attrs[a]) > -1) {
 
-                         return true;
 
-                     }
 
-                 }
 
-             }
 
-         }
 
-     }
 
-     return false;
 
-   }
 
-   function _handleStyles(oldNode, newNode, rule) {
 
-     var s, v;
 
-     if(rule && rule.keep_styles) {
 
-       for (s in rule.keep_styles) {
 
-         if (rule.keep_styles.hasOwnProperty(s)) {
 
-           v = (s === "float") ? oldNode.style.styleFloat || oldNode.style.cssFloat : oldNode.style[s];
 
-           // value can be regex and if so should match or style skipped
 
-           if (rule.keep_styles[s] instanceof RegExp && !(rule.keep_styles[s].test(v))) {
 
-             continue;
 
-           }
 
-           if (s === "float") {
 
-             // IE compability
 
-             newNode.style[(oldNode.style.styleFloat) ? 'styleFloat': 'cssFloat'] = v;
 
-            } else if (oldNode.style[s]) {
 
-              newNode.style[s] = v;
 
-            }
 
-         }
 
-       }
 
-     }
 
-   };
 
-   function _getAttributesBeginningWith(beginning, attributes) {
 
-     var returnAttributes = [];
 
-     for (var attr in attributes) {
 
-       if (attributes.hasOwnProperty(attr) && attr.indexOf(beginning) === 0) {
 
-         returnAttributes.push(attr);
 
-       }
 
-     }
 
-     return returnAttributes;
 
-   }
 
-   function _checkAttribute(attributeName, attributeValue, methodName, nodeName) {
 
-     var method = attributeCheckMethods[methodName],
 
-         newAttributeValue;
 
-     if (method) {
 
-       if (attributeValue || (attributeName === "alt" && nodeName == "IMG")) {
 
-         newAttributeValue = method(attributeValue);
 
-         if (typeof(newAttributeValue) === "string") {
 
-           return newAttributeValue;
 
-         }
 
-       }
 
-     }
 
-     return false;
 
-   }
 
-   function _checkAttributes(oldNode, local_attributes) {
 
-     var globalAttributes  = wysihtml5.lang.object(currentRules.attributes || {}).clone(), // global values for check/convert values of attributes
 
-         checkAttributes   = wysihtml5.lang.object(globalAttributes).merge( wysihtml5.lang.object(local_attributes || {}).clone()).get(),
 
-         attributes        = {},
 
-         oldAttributes     = wysihtml5.dom.getAttributes(oldNode),
 
-         attributeName, newValue, matchingAttributes;
 
-     for (attributeName in checkAttributes) {
 
-       if ((/\*$/).test(attributeName)) {
 
-         matchingAttributes = _getAttributesBeginningWith(attributeName.slice(0,-1), oldAttributes);
 
-         for (var i = 0, imax = matchingAttributes.length; i < imax; i++) {
 
-           newValue = _checkAttribute(matchingAttributes[i], oldAttributes[matchingAttributes[i]], checkAttributes[attributeName], oldNode.nodeName);
 
-           if (newValue !== false) {
 
-             attributes[matchingAttributes[i]] = newValue;
 
-           }
 
-         }
 
-       } else {
 
-         newValue = _checkAttribute(attributeName, oldAttributes[attributeName], checkAttributes[attributeName], oldNode.nodeName);
 
-         if (newValue !== false) {
 
-           attributes[attributeName] = newValue;
 
-         }
 
-       }
 
-     }
 
-     return attributes;
 
-   }
 
-   // TODO: refactor. Too long to read
 
-   function _handleAttributes(oldNode, newNode, rule, clearInternals) {
 
-     var attributes          = {},                         // fresh new set of attributes to set on newNode
 
-         setClass            = rule.set_class,             // classes to set
 
-         addClass            = rule.add_class,             // add classes based on existing attributes
 
-         addStyle            = rule.add_style,             // add styles based on existing attributes
 
-         setAttributes       = rule.set_attributes,        // attributes to set on the current node
 
-         allowedClasses      = currentRules.classes,
 
-         i                   = 0,
 
-         classes             = [],
 
-         styles              = [],
 
-         newClasses          = [],
 
-         oldClasses          = [],
 
-         classesLength,
 
-         newClassesLength,
 
-         currentClass,
 
-         newClass,
 
-         attributeName,
 
-         method;
 
-     if (setAttributes) {
 
-       attributes = wysihtml5.lang.object(setAttributes).clone();
 
-     }
 
-     // check/convert values of attributes
 
-     attributes = wysihtml5.lang.object(attributes).merge(_checkAttributes(oldNode,  rule.check_attributes)).get();
 
-     if (setClass) {
 
-       classes.push(setClass);
 
-     }
 
-     if (addClass) {
 
-       for (attributeName in addClass) {
 
-         method = addClassMethods[addClass[attributeName]];
 
-         if (!method) {
 
-           continue;
 
-         }
 
-         newClass = method(wysihtml5.dom.getAttribute(oldNode, attributeName));
 
-         if (typeof(newClass) === "string") {
 
-           classes.push(newClass);
 
-         }
 
-       }
 
-     }
 
-     if (addStyle) {
 
-       for (attributeName in addStyle) {
 
-         method = addStyleMethods[addStyle[attributeName]];
 
-         if (!method) {
 
-           continue;
 
-         }
 
-         newStyle = method(wysihtml5.dom.getAttribute(oldNode, attributeName));
 
-         if (typeof(newStyle) === "string") {
 
-           styles.push(newStyle);
 
-         }
 
-       }
 
-     }
 
-     if (typeof(allowedClasses) === "string" && allowedClasses === "any" && oldNode.getAttribute("class")) {
 
-       if (currentRules.classes_blacklist) {
 
-         oldClasses = oldNode.getAttribute("class");
 
-         if (oldClasses) {
 
-           classes = classes.concat(oldClasses.split(WHITE_SPACE_REG_EXP));
 
-         }
 
-         classesLength = classes.length;
 
-         for (; i<classesLength; i++) {
 
-           currentClass = classes[i];
 
-           if (!currentRules.classes_blacklist[currentClass]) {
 
-             newClasses.push(currentClass);
 
-           }
 
-         }
 
-         if (newClasses.length) {
 
-           attributes["class"] = wysihtml5.lang.array(newClasses).unique().join(" ");
 
-         }
 
-       } else {
 
-         attributes["class"] = oldNode.getAttribute("class");
 
-       }
 
-     } else {
 
-       // make sure that wysihtml5 temp class doesn't get stripped out
 
-       if (!clearInternals) {
 
-         allowedClasses["_wysihtml5-temp-placeholder"] = 1;
 
-         allowedClasses["_rangySelectionBoundary"] = 1;
 
-         allowedClasses["wysiwyg-tmp-selected-cell"] = 1;
 
-       }
 
-       // add old classes last
 
-       oldClasses = oldNode.getAttribute("class");
 
-       if (oldClasses) {
 
-         classes = classes.concat(oldClasses.split(WHITE_SPACE_REG_EXP));
 
-       }
 
-       classesLength = classes.length;
 
-       for (; i<classesLength; i++) {
 
-         currentClass = classes[i];
 
-         if (allowedClasses[currentClass]) {
 
-           newClasses.push(currentClass);
 
-         }
 
-       }
 
-       if (newClasses.length) {
 
-         attributes["class"] = wysihtml5.lang.array(newClasses).unique().join(" ");
 
-       }
 
-     }
 
-     // remove table selection class if present
 
-     if (attributes["class"] && clearInternals) {
 
-       attributes["class"] = attributes["class"].replace("wysiwyg-tmp-selected-cell", "");
 
-       if ((/^\s*$/g).test(attributes["class"])) {
 
-         delete attributes["class"];
 
-       }
 
-     }
 
-     if (styles.length) {
 
-       attributes["style"] = wysihtml5.lang.array(styles).unique().join(" ");
 
-     }
 
-     // set attributes on newNode
 
-     for (attributeName in attributes) {
 
-       // Setting attributes can cause a js error in IE under certain circumstances
 
-       // eg. on a <img> under https when it's new attribute value is non-https
 
-       // TODO: Investigate this further and check for smarter handling
 
-       try {
 
-         newNode.setAttribute(attributeName, attributes[attributeName]);
 
-       } catch(e) {}
 
-     }
 
-     // IE8 sometimes loses the width/height attributes when those are set before the "src"
 
-     // so we make sure to set them again
 
-     if (attributes.src) {
 
-       if (typeof(attributes.width) !== "undefined") {
 
-         newNode.setAttribute("width", attributes.width);
 
-       }
 
-       if (typeof(attributes.height) !== "undefined") {
 
-         newNode.setAttribute("height", attributes.height);
 
-       }
 
-     }
 
-   }
 
-   var INVISIBLE_SPACE_REG_EXP = /\uFEFF/g;
 
-   function _handleText(oldNode) {
 
-     var nextSibling = oldNode.nextSibling;
 
-     if (nextSibling && nextSibling.nodeType === wysihtml5.TEXT_NODE) {
 
-       // Concatenate text nodes
 
-       nextSibling.data = oldNode.data.replace(INVISIBLE_SPACE_REG_EXP, "") + nextSibling.data.replace(INVISIBLE_SPACE_REG_EXP, "");
 
-     } else {
 
-       // \uFEFF = wysihtml5.INVISIBLE_SPACE (used as a hack in certain rich text editing situations)
 
-       var data = oldNode.data.replace(INVISIBLE_SPACE_REG_EXP, "");
 
-       return oldNode.ownerDocument.createTextNode(data);
 
-     }
 
-   }
 
-   function _handleComment(oldNode) {
 
-     if (currentRules.comments) {
 
-       return oldNode.ownerDocument.createComment(oldNode.nodeValue);
 
-     }
 
-   }
 
-   // ------------ attribute checks ------------ \\
 
-   var attributeCheckMethods = {
 
-     url: (function() {
 
-       var REG_EXP = /^https?:\/\//i;
 
-       return function(attributeValue) {
 
-         if (!attributeValue || !attributeValue.match(REG_EXP)) {
 
-           return null;
 
-         }
 
-         return attributeValue.replace(REG_EXP, function(match) {
 
-           return match.toLowerCase();
 
-         });
 
-       };
 
-     })(),
 
-     src: (function() {
 
-       var REG_EXP = /^(\/|https?:\/\/)/i;
 
-       return function(attributeValue) {
 
-         if (!attributeValue || !attributeValue.match(REG_EXP)) {
 
-           return null;
 
-         }
 
-         return attributeValue.replace(REG_EXP, function(match) {
 
-           return match.toLowerCase();
 
-         });
 
-       };
 
-     })(),
 
-     href: (function() {
 
-       var REG_EXP = /^(#|\/|https?:\/\/|mailto:)/i;
 
-       return function(attributeValue) {
 
-         if (!attributeValue || !attributeValue.match(REG_EXP)) {
 
-           return null;
 
-         }
 
-         return attributeValue.replace(REG_EXP, function(match) {
 
-           return match.toLowerCase();
 
-         });
 
-       };
 
-     })(),
 
-     alt: (function() {
 
-       var REG_EXP = /[^ a-z0-9_\-]/gi;
 
-       return function(attributeValue) {
 
-         if (!attributeValue) {
 
-           return "";
 
-         }
 
-         return attributeValue.replace(REG_EXP, "");
 
-       };
 
-     })(),
 
-     numbers: (function() {
 
-       var REG_EXP = /\D/g;
 
-       return function(attributeValue) {
 
-         attributeValue = (attributeValue || "").replace(REG_EXP, "");
 
-         return attributeValue || null;
 
-       };
 
-     })(),
 
-     any: (function() {
 
-       return function(attributeValue) {
 
-         return attributeValue;
 
-       };
 
-     })()
 
-   };
 
-   // ------------ style converter (converts an html attribute to a style) ------------ \\
 
-   var addStyleMethods = {
 
-     align_text: (function() {
 
-       var mapping = {
 
-         left:     "text-align: left;",
 
-         right:    "text-align: right;",
 
-         center:   "text-align: center;"
 
-       };
 
-       return function(attributeValue) {
 
-         return mapping[String(attributeValue).toLowerCase()];
 
-       };
 
-     })(),
 
-   };
 
-   // ------------ class converter (converts an html attribute to a class name) ------------ \\
 
-   var addClassMethods = {
 
-     align_img: (function() {
 
-       var mapping = {
 
-         left:   "wysiwyg-float-left",
 
-         right:  "wysiwyg-float-right"
 
-       };
 
-       return function(attributeValue) {
 
-         return mapping[String(attributeValue).toLowerCase()];
 
-       };
 
-     })(),
 
-     align_text: (function() {
 
-       var mapping = {
 
-         left:     "wysiwyg-text-align-left",
 
-         right:    "wysiwyg-text-align-right",
 
-         center:   "wysiwyg-text-align-center",
 
-         justify:  "wysiwyg-text-align-justify"
 
-       };
 
-       return function(attributeValue) {
 
-         return mapping[String(attributeValue).toLowerCase()];
 
-       };
 
-     })(),
 
-     clear_br: (function() {
 
-       var mapping = {
 
-         left:   "wysiwyg-clear-left",
 
-         right:  "wysiwyg-clear-right",
 
-         both:   "wysiwyg-clear-both",
 
-         all:    "wysiwyg-clear-both"
 
-       };
 
-       return function(attributeValue) {
 
-         return mapping[String(attributeValue).toLowerCase()];
 
-       };
 
-     })(),
 
-     size_font: (function() {
 
-       var mapping = {
 
-         "1": "wysiwyg-font-size-xx-small",
 
-         "2": "wysiwyg-font-size-small",
 
-         "3": "wysiwyg-font-size-medium",
 
-         "4": "wysiwyg-font-size-large",
 
-         "5": "wysiwyg-font-size-x-large",
 
-         "6": "wysiwyg-font-size-xx-large",
 
-         "7": "wysiwyg-font-size-xx-large",
 
-         "-": "wysiwyg-font-size-smaller",
 
-         "+": "wysiwyg-font-size-larger"
 
-       };
 
-       return function(attributeValue) {
 
-         return mapping[String(attributeValue).charAt(0)];
 
-       };
 
-     })()
 
-   };
 
-   // checks if element is possibly visible
 
-   var typeCeckMethods = {
 
-     has_visible_contet: (function() {
 
-       var txt,
 
-           isVisible = false,
 
-           visibleElements = ['img', 'video', 'picture', 'br', 'script', 'noscript',
 
-                              'style', 'table', 'iframe', 'object', 'embed', 'audio',
 
-                              'svg', 'input', 'button', 'select','textarea', 'canvas'];
 
-       return function(el) {
 
-         // has visible innertext. so is visible
 
-         txt = (el.innerText || el.textContent).replace(/\s/g, '');
 
-         if (txt && txt.length > 0) {
 
-           return true;
 
-         }
 
-         // matches list of visible dimensioned elements
 
-         for (var i = visibleElements.length; i--;) {
 
-           if (el.querySelector(visibleElements[i])) {
 
-             return true;
 
-           }
 
-         }
 
-         // try to measure dimesions in last resort. (can find only of elements in dom)
 
-         if (el.offsetWidth && el.offsetWidth > 0 && el.offsetHeight && el.offsetHeight > 0) {
 
-           return true;
 
-         }
 
-         return false;
 
-       };
 
-     })()
 
-   };
 
-   var elementHandlingMethods = {
 
-     unwrap: function (element) {
 
-       wysihtml5.dom.unwrap(element);
 
-     },
 
-     remove: function (element) {
 
-       element.parentNode.removeChild(element);
 
-     }
 
-   };
 
-   return parse(elementOrHtml_current, config_current);
 
- };
 
- ;/**
 
-  * Checks for empty text node childs and removes them
 
-  *
 
-  * @param {Element} node The element in which to cleanup
 
-  * @example
 
-  *    wysihtml5.dom.removeEmptyTextNodes(element);
 
-  */
 
- wysihtml5.dom.removeEmptyTextNodes = function(node) {
 
-   var childNode,
 
-       childNodes        = wysihtml5.lang.array(node.childNodes).get(),
 
-       childNodesLength  = childNodes.length,
 
-       i                 = 0;
 
-   for (; i<childNodesLength; i++) {
 
-     childNode = childNodes[i];
 
-     if (childNode.nodeType === wysihtml5.TEXT_NODE && childNode.data === "") {
 
-       childNode.parentNode.removeChild(childNode);
 
-     }
 
-   }
 
- };
 
- ;/**
 
-  * Renames an element (eg. a <div> to a <p>) and keeps its childs
 
-  *
 
-  * @param {Element} element The list element which should be renamed
 
-  * @param {Element} newNodeName The desired tag name
 
-  *
 
-  * @example
 
-  *    <!-- Assume the following dom: -->
 
-  *    <ul id="list">
 
-  *      <li>eminem</li>
 
-  *      <li>dr. dre</li>
 
-  *      <li>50 Cent</li>
 
-  *    </ul>
 
-  *
 
-  *    <script>
 
-  *      wysihtml5.dom.renameElement(document.getElementById("list"), "ol");
 
-  *    </script>
 
-  *
 
-  *    <!-- Will result in: -->
 
-  *    <ol>
 
-  *      <li>eminem</li>
 
-  *      <li>dr. dre</li>
 
-  *      <li>50 Cent</li>
 
-  *    </ol>
 
-  */
 
- wysihtml5.dom.renameElement = function(element, newNodeName) {
 
-   var newElement = element.ownerDocument.createElement(newNodeName),
 
-       firstChild;
 
-   while (firstChild = element.firstChild) {
 
-     newElement.appendChild(firstChild);
 
-   }
 
-   wysihtml5.dom.copyAttributes(["align", "className"]).from(element).to(newElement);
 
-   element.parentNode.replaceChild(newElement, element);
 
-   return newElement;
 
- };
 
- ;/**
 
-  * Takes an element, removes it and replaces it with it's childs
 
-  *
 
-  * @param {Object} node The node which to replace with it's child nodes
 
-  * @example
 
-  *    <div id="foo">
 
-  *      <span>hello</span>
 
-  *    </div>
 
-  *    <script>
 
-  *      // Remove #foo and replace with it's children
 
-  *      wysihtml5.dom.replaceWithChildNodes(document.getElementById("foo"));
 
-  *    </script>
 
-  */
 
- wysihtml5.dom.replaceWithChildNodes = function(node) {
 
-   if (!node.parentNode) {
 
-     return;
 
-   }
 
-   if (!node.firstChild) {
 
-     node.parentNode.removeChild(node);
 
-     return;
 
-   }
 
-   var fragment = node.ownerDocument.createDocumentFragment();
 
-   while (node.firstChild) {
 
-     fragment.appendChild(node.firstChild);
 
-   }
 
-   node.parentNode.replaceChild(fragment, node);
 
-   node = fragment = null;
 
- };
 
- ;/**
 
-  * Unwraps an unordered/ordered list
 
-  *
 
-  * @param {Element} element The list element which should be unwrapped
 
-  *
 
-  * @example
 
-  *    <!-- Assume the following dom: -->
 
-  *    <ul id="list">
 
-  *      <li>eminem</li>
 
-  *      <li>dr. dre</li>
 
-  *      <li>50 Cent</li>
 
-  *    </ul>
 
-  *
 
-  *    <script>
 
-  *      wysihtml5.dom.resolveList(document.getElementById("list"));
 
-  *    </script>
 
-  *
 
-  *    <!-- Will result in: -->
 
-  *    eminem<br>
 
-  *    dr. dre<br>
 
-  *    50 Cent<br>
 
-  */
 
- (function(dom) {
 
-   function _isBlockElement(node) {
 
-     return dom.getStyle("display").from(node) === "block";
 
-   }
 
-   function _isLineBreak(node) {
 
-     return node.nodeName === "BR";
 
-   }
 
-   function _appendLineBreak(element) {
 
-     var lineBreak = element.ownerDocument.createElement("br");
 
-     element.appendChild(lineBreak);
 
-   }
 
-   function resolveList(list, useLineBreaks) {
 
-     if (!list.nodeName.match(/^(MENU|UL|OL)$/)) {
 
-       return;
 
-     }
 
-     var doc             = list.ownerDocument,
 
-         fragment        = doc.createDocumentFragment(),
 
-         previousSibling = wysihtml5.dom.domNode(list).prev({ignoreBlankTexts: true}),
 
-         firstChild,
 
-         lastChild,
 
-         isLastChild,
 
-         shouldAppendLineBreak,
 
-         paragraph,
 
-         listItem;
 
-     if (useLineBreaks) {
 
-       // Insert line break if list is after a non-block element
 
-       if (previousSibling && !_isBlockElement(previousSibling) && !_isLineBreak(previousSibling)) {
 
-         _appendLineBreak(fragment);
 
-       }
 
-       while (listItem = (list.firstElementChild || list.firstChild)) {
 
-         lastChild = listItem.lastChild;
 
-         while (firstChild = listItem.firstChild) {
 
-           isLastChild           = firstChild === lastChild;
 
-           // This needs to be done before appending it to the fragment, as it otherwise will lose style information
 
-           shouldAppendLineBreak = isLastChild && !_isBlockElement(firstChild) && !_isLineBreak(firstChild);
 
-           fragment.appendChild(firstChild);
 
-           if (shouldAppendLineBreak) {
 
-             _appendLineBreak(fragment);
 
-           }
 
-         }
 
-         listItem.parentNode.removeChild(listItem);
 
-       }
 
-     } else {
 
-       while (listItem = (list.firstElementChild || list.firstChild)) {
 
-         if (listItem.querySelector && listItem.querySelector("div, p, ul, ol, menu, blockquote, h1, h2, h3, h4, h5, h6")) {
 
-           while (firstChild = listItem.firstChild) {
 
-             fragment.appendChild(firstChild);
 
-           }
 
-         } else {
 
-           paragraph = doc.createElement("p");
 
-           while (firstChild = listItem.firstChild) {
 
-             paragraph.appendChild(firstChild);
 
-           }
 
-           fragment.appendChild(paragraph);
 
-         }
 
-         listItem.parentNode.removeChild(listItem);
 
-       }
 
-     }
 
-     list.parentNode.replaceChild(fragment, list);
 
-   }
 
-   dom.resolveList = resolveList;
 
- })(wysihtml5.dom);
 
- ;/**
 
-  * Sandbox for executing javascript, parsing css styles and doing dom operations in a secure way
 
-  *
 
-  * Browser Compatibility:
 
-  *  - Secure in MSIE 6+, but only when the user hasn't made changes to his security level "restricted"
 
-  *  - Partially secure in other browsers (Firefox, Opera, Safari, Chrome, ...)
 
-  *
 
-  * Please note that this class can't benefit from the HTML5 sandbox attribute for the following reasons:
 
-  *    - sandboxing doesn't work correctly with inlined content (src="javascript:'<html>...</html>'")
 
-  *    - sandboxing of physical documents causes that the dom isn't accessible anymore from the outside (iframe.contentWindow, ...)
 
-  *    - setting the "allow-same-origin" flag would fix that, but then still javascript and dom events refuse to fire
 
-  *    - therefore the "allow-scripts" flag is needed, which then would deactivate any security, as the js executed inside the iframe
 
-  *      can do anything as if the sandbox attribute wasn't set
 
-  *
 
-  * @param {Function} [readyCallback] Method that gets invoked when the sandbox is ready
 
-  * @param {Object} [config] Optional parameters
 
-  *
 
-  * @example
 
-  *    new wysihtml5.dom.Sandbox(function(sandbox) {
 
-  *      sandbox.getWindow().document.body.innerHTML = '<img src=foo.gif onerror="alert(document.cookie)">';
 
-  *    });
 
-  */
 
- (function(wysihtml5) {
 
-   var /**
 
-        * Default configuration
 
-        */
 
-       doc                 = document,
 
-       /**
 
-        * Properties to unset/protect on the window object
 
-        */
 
-       windowProperties    = [
 
-         "parent", "top", "opener", "frameElement", "frames",
 
-         "localStorage", "globalStorage", "sessionStorage", "indexedDB"
 
-       ],
 
-       /**
 
-        * Properties on the window object which are set to an empty function
 
-        */
 
-       windowProperties2   = [
 
-         "open", "close", "openDialog", "showModalDialog",
 
-         "alert", "confirm", "prompt",
 
-         "openDatabase", "postMessage",
 
-         "XMLHttpRequest", "XDomainRequest"
 
-       ],
 
-       /**
 
-        * Properties to unset/protect on the document object
 
-        */
 
-       documentProperties  = [
 
-         "referrer",
 
-         "write", "open", "close"
 
-       ];
 
-   wysihtml5.dom.Sandbox = Base.extend(
 
-     /** @scope wysihtml5.dom.Sandbox.prototype */ {
 
-     constructor: function(readyCallback, config) {
 
-       this.callback = readyCallback || wysihtml5.EMPTY_FUNCTION;
 
-       this.config   = wysihtml5.lang.object({}).merge(config).get();
 
-       this.editableArea   = this._createIframe();
 
-     },
 
-     insertInto: function(element) {
 
-       if (typeof(element) === "string") {
 
-         element = doc.getElementById(element);
 
-       }
 
-       element.appendChild(this.editableArea);
 
-     },
 
-     getIframe: function() {
 
-       return this.editableArea;
 
-     },
 
-     getWindow: function() {
 
-       this._readyError();
 
-     },
 
-     getDocument: function() {
 
-       this._readyError();
 
-     },
 
-     destroy: function() {
 
-       var iframe = this.getIframe();
 
-       iframe.parentNode.removeChild(iframe);
 
-     },
 
-     _readyError: function() {
 
-       throw new Error("wysihtml5.Sandbox: Sandbox iframe isn't loaded yet");
 
-     },
 
-     /**
 
-      * Creates the sandbox iframe
 
-      *
 
-      * Some important notes:
 
-      *  - We can't use HTML5 sandbox for now:
 
-      *    setting it causes that the iframe's dom can't be accessed from the outside
 
-      *    Therefore we need to set the "allow-same-origin" flag which enables accessing the iframe's dom
 
-      *    But then there's another problem, DOM events (focus, blur, change, keypress, ...) aren't fired.
 
-      *    In order to make this happen we need to set the "allow-scripts" flag.
 
-      *    A combination of allow-scripts and allow-same-origin is almost the same as setting no sandbox attribute at all.
 
-      *  - Chrome & Safari, doesn't seem to support sandboxing correctly when the iframe's html is inlined (no physical document)
 
-      *  - IE needs to have the security="restricted" attribute set before the iframe is
 
-      *    inserted into the dom tree
 
-      *  - Believe it or not but in IE "security" in document.createElement("iframe") is false, even
 
-      *    though it supports it
 
-      *  - When an iframe has security="restricted", in IE eval() & execScript() don't work anymore
 
-      *  - IE doesn't fire the onload event when the content is inlined in the src attribute, therefore we rely
 
-      *    on the onreadystatechange event
 
-      */
 
-     _createIframe: function() {
 
-       var that   = this,
 
-           iframe = doc.createElement("iframe");
 
-       iframe.className = "wysihtml5-sandbox";
 
-       wysihtml5.dom.setAttributes({
 
-         "security":           "restricted",
 
-         "allowtransparency":  "true",
 
-         "frameborder":        0,
 
-         "width":              0,
 
-         "height":             0,
 
-         "marginwidth":        0,
 
-         "marginheight":       0
 
-       }).on(iframe);
 
-       // Setting the src like this prevents ssl warnings in IE6
 
-       if (wysihtml5.browser.throwsMixedContentWarningWhenIframeSrcIsEmpty()) {
 
-         iframe.src = "javascript:'<html></html>'";
 
-       }
 
-       iframe.onload = function() {
 
-         iframe.onreadystatechange = iframe.onload = null;
 
-         that._onLoadIframe(iframe);
 
-       };
 
-       iframe.onreadystatechange = function() {
 
-         if (/loaded|complete/.test(iframe.readyState)) {
 
-           iframe.onreadystatechange = iframe.onload = null;
 
-           that._onLoadIframe(iframe);
 
-         }
 
-       };
 
-       return iframe;
 
-     },
 
-     /**
 
-      * Callback for when the iframe has finished loading
 
-      */
 
-     _onLoadIframe: function(iframe) {
 
-       // don't resume when the iframe got unloaded (eg. by removing it from the dom)
 
-       if (!wysihtml5.dom.contains(doc.documentElement, iframe)) {
 
-         return;
 
-       }
 
-       var that           = this,
 
-           iframeWindow   = iframe.contentWindow,
 
-           iframeDocument = iframe.contentWindow.document,
 
-           charset        = doc.characterSet || doc.charset || "utf-8",
 
-           sandboxHtml    = this._getHtml({
 
-             charset:      charset,
 
-             stylesheets:  this.config.stylesheets
 
-           });
 
-       // Create the basic dom tree including proper DOCTYPE and charset
 
-       iframeDocument.open("text/html", "replace");
 
-       iframeDocument.write(sandboxHtml);
 
-       iframeDocument.close();
 
-       this.getWindow = function() { return iframe.contentWindow; };
 
-       this.getDocument = function() { return iframe.contentWindow.document; };
 
-       // Catch js errors and pass them to the parent's onerror event
 
-       // addEventListener("error") doesn't work properly in some browsers
 
-       // TODO: apparently this doesn't work in IE9!
 
-       iframeWindow.onerror = function(errorMessage, fileName, lineNumber) {
 
-         throw new Error("wysihtml5.Sandbox: " + errorMessage, fileName, lineNumber);
 
-       };
 
-       if (!wysihtml5.browser.supportsSandboxedIframes()) {
 
-         // Unset a bunch of sensitive variables
 
-         // Please note: This isn't hack safe!
 
-         // It more or less just takes care of basic attacks and prevents accidental theft of sensitive information
 
-         // IE is secure though, which is the most important thing, since IE is the only browser, who
 
-         // takes over scripts & styles into contentEditable elements when copied from external websites
 
-         // or applications (Microsoft Word, ...)
 
-         var i, length;
 
-         for (i=0, length=windowProperties.length; i<length; i++) {
 
-           this._unset(iframeWindow, windowProperties[i]);
 
-         }
 
-         for (i=0, length=windowProperties2.length; i<length; i++) {
 
-           this._unset(iframeWindow, windowProperties2[i], wysihtml5.EMPTY_FUNCTION);
 
-         }
 
-         for (i=0, length=documentProperties.length; i<length; i++) {
 
-           this._unset(iframeDocument, documentProperties[i]);
 
-         }
 
-         // This doesn't work in Safari 5
 
-         // See http://stackoverflow.com/questions/992461/is-it-possible-to-override-document-cookie-in-webkit
 
-         this._unset(iframeDocument, "cookie", "", true);
 
-       }
 
-       this.loaded = true;
 
-       // Trigger the callback
 
-       setTimeout(function() { that.callback(that); }, 0);
 
-     },
 
-     _getHtml: function(templateVars) {
 
-       var stylesheets = templateVars.stylesheets,
 
-           html        = "",
 
-           i           = 0,
 
-           length;
 
-       stylesheets = typeof(stylesheets) === "string" ? [stylesheets] : stylesheets;
 
-       if (stylesheets) {
 
-         length = stylesheets.length;
 
-         for (; i<length; i++) {
 
-           html += '<link rel="stylesheet" href="' + stylesheets[i] + '">';
 
-         }
 
-       }
 
-       templateVars.stylesheets = html;
 
-       return wysihtml5.lang.string(
 
-         '<!DOCTYPE html><html><head>'
 
-         + '<meta charset="#{charset}">#{stylesheets}</head>'
 
-         + '<body></body></html>'
 
-       ).interpolate(templateVars);
 
-     },
 
-     /**
 
-      * Method to unset/override existing variables
 
-      * @example
 
-      *    // Make cookie unreadable and unwritable
 
-      *    this._unset(document, "cookie", "", true);
 
-      */
 
-     _unset: function(object, property, value, setter) {
 
-       try { object[property] = value; } catch(e) {}
 
-       try { object.__defineGetter__(property, function() { return value; }); } catch(e) {}
 
-       if (setter) {
 
-         try { object.__defineSetter__(property, function() {}); } catch(e) {}
 
-       }
 
-       if (!wysihtml5.browser.crashesWhenDefineProperty(property)) {
 
-         try {
 
-           var config = {
 
-             get: function() { return value; }
 
-           };
 
-           if (setter) {
 
-             config.set = function() {};
 
-           }
 
-           Object.defineProperty(object, property, config);
 
-         } catch(e) {}
 
-       }
 
-     }
 
-   });
 
- })(wysihtml5);
 
- ;(function(wysihtml5) {
 
-   var doc = document;
 
-   wysihtml5.dom.ContentEditableArea = Base.extend({
 
-       getContentEditable: function() {
 
-         return this.element;
 
-       },
 
-       getWindow: function() {
 
-         return this.element.ownerDocument.defaultView;
 
-       },
 
-       getDocument: function() {
 
-         return this.element.ownerDocument;
 
-       },
 
-       constructor: function(readyCallback, config, contentEditable) {
 
-         this.callback = readyCallback || wysihtml5.EMPTY_FUNCTION;
 
-         this.config   = wysihtml5.lang.object({}).merge(config).get();
 
-         if (contentEditable) {
 
-             this.element = this._bindElement(contentEditable);
 
-         } else {
 
-             this.element = this._createElement();
 
-         }
 
-       },
 
-       // creates a new contenteditable and initiates it
 
-       _createElement: function() {
 
-         var element = doc.createElement("div");
 
-         element.className = "wysihtml5-sandbox";
 
-         this._loadElement(element);
 
-         return element;
 
-       },
 
-       // initiates an allready existent contenteditable
 
-       _bindElement: function(contentEditable) {
 
-         contentEditable.className = (contentEditable.className && contentEditable.className != '') ? contentEditable.className + " wysihtml5-sandbox" : "wysihtml5-sandbox";
 
-         this._loadElement(contentEditable, true);
 
-         return contentEditable;
 
-       },
 
-       _loadElement: function(element, contentExists) {
 
-           var that = this;
 
-         if (!contentExists) {
 
-             var sandboxHtml = this._getHtml();
 
-             element.innerHTML = sandboxHtml;
 
-         }
 
-         this.getWindow = function() { return element.ownerDocument.defaultView; };
 
-         this.getDocument = function() { return element.ownerDocument; };
 
-         // Catch js errors and pass them to the parent's onerror event
 
-         // addEventListener("error") doesn't work properly in some browsers
 
-         // TODO: apparently this doesn't work in IE9!
 
-         // TODO: figure out and bind the errors logic for contenteditble mode
 
-         /*iframeWindow.onerror = function(errorMessage, fileName, lineNumber) {
 
-           throw new Error("wysihtml5.Sandbox: " + errorMessage, fileName, lineNumber);
 
-         }
 
-         */
 
-         this.loaded = true;
 
-         // Trigger the callback
 
-         setTimeout(function() { that.callback(that); }, 0);
 
-       },
 
-       _getHtml: function(templateVars) {
 
-         return '';
 
-       }
 
-   });
 
- })(wysihtml5);
 
- ;(function() {
 
-   var mapping = {
 
-     "className": "class"
 
-   };
 
-   wysihtml5.dom.setAttributes = function(attributes) {
 
-     return {
 
-       on: function(element) {
 
-         for (var i in attributes) {
 
-           element.setAttribute(mapping[i] || i, attributes[i]);
 
-         }
 
-       }
 
-     };
 
-   };
 
- })();
 
- ;wysihtml5.dom.setStyles = function(styles) {
 
-   return {
 
-     on: function(element) {
 
-       var style = element.style;
 
-       if (typeof(styles) === "string") {
 
-         style.cssText += ";" + styles;
 
-         return;
 
-       }
 
-       for (var i in styles) {
 
-         if (i === "float") {
 
-           style.cssFloat = styles[i];
 
-           style.styleFloat = styles[i];
 
-         } else {
 
-           style[i] = styles[i];
 
-         }
 
-       }
 
-     }
 
-   };
 
- };
 
- ;/**
 
-  * Simulate HTML5 placeholder attribute
 
-  *
 
-  * Needed since
 
-  *    - div[contentEditable] elements don't support it
 
-  *    - older browsers (such as IE8 and Firefox 3.6) don't support it at all
 
-  *
 
-  * @param {Object} parent Instance of main wysihtml5.Editor class
 
-  * @param {Element} view Instance of wysihtml5.views.* class
 
-  * @param {String} placeholderText
 
-  *
 
-  * @example
 
-  *    wysihtml.dom.simulatePlaceholder(this, composer, "Foobar");
 
-  */
 
- (function(dom) {
 
-   dom.simulatePlaceholder = function(editor, view, placeholderText) {
 
-     var CLASS_NAME = "placeholder",
 
-         unset = function() {
 
-           var composerIsVisible   = view.element.offsetWidth > 0 && view.element.offsetHeight > 0;
 
-           if (view.hasPlaceholderSet()) {
 
-             view.clear();
 
-             view.element.focus();
 
-             if (composerIsVisible ) {
 
-               setTimeout(function() {
 
-                 var sel = view.selection.getSelection();
 
-                 if (!sel.focusNode || !sel.anchorNode) {
 
-                   view.selection.selectNode(view.element.firstChild || view.element);
 
-                 }
 
-               }, 0);
 
-             }
 
-           }
 
-           view.placeholderSet = false;
 
-           dom.removeClass(view.element, CLASS_NAME);
 
-         },
 
-         set = function() {
 
-           if (view.isEmpty()) {
 
-             view.placeholderSet = true;
 
-             view.setValue(placeholderText);
 
-             dom.addClass(view.element, CLASS_NAME);
 
-           }
 
-         };
 
-     editor
 
-       .on("set_placeholder", set)
 
-       .on("unset_placeholder", unset)
 
-       .on("focus:composer", unset)
 
-       .on("paste:composer", unset)
 
-       .on("blur:composer", set);
 
-     set();
 
-   };
 
- })(wysihtml5.dom);
 
- ;(function(dom) {
 
-   var documentElement = document.documentElement;
 
-   if ("textContent" in documentElement) {
 
-     dom.setTextContent = function(element, text) {
 
-       element.textContent = text;
 
-     };
 
-     dom.getTextContent = function(element) {
 
-       return element.textContent;
 
-     };
 
-   } else if ("innerText" in documentElement) {
 
-     dom.setTextContent = function(element, text) {
 
-       element.innerText = text;
 
-     };
 
-     dom.getTextContent = function(element) {
 
-       return element.innerText;
 
-     };
 
-   } else {
 
-     dom.setTextContent = function(element, text) {
 
-       element.nodeValue = text;
 
-     };
 
-     dom.getTextContent = function(element) {
 
-       return element.nodeValue;
 
-     };
 
-   }
 
- })(wysihtml5.dom);
 
- ;/**
 
-  * Get a set of attribute from one element
 
-  *
 
-  * IE gives wrong results for hasAttribute/getAttribute, for example:
 
-  *    var td = document.createElement("td");
 
-  *    td.getAttribute("rowspan"); // => "1" in IE
 
-  *
 
-  * Therefore we have to check the element's outerHTML for the attribute
 
- */
 
- wysihtml5.dom.getAttribute = function(node, attributeName) {
 
-   var HAS_GET_ATTRIBUTE_BUG = !wysihtml5.browser.supportsGetAttributeCorrectly();
 
-   attributeName = attributeName.toLowerCase();
 
-   var nodeName = node.nodeName;
 
-   if (nodeName == "IMG" && attributeName == "src" && wysihtml5.dom.isLoadedImage(node) === true) {
 
-     // Get 'src' attribute value via object property since this will always contain the
 
-     // full absolute url (http://...)
 
-     // this fixes a very annoying bug in firefox (ver 3.6 & 4) and IE 8 where images copied from the same host
 
-     // will have relative paths, which the sanitizer strips out (see attributeCheckMethods.url)
 
-     return node.src;
 
-   } else if (HAS_GET_ATTRIBUTE_BUG && "outerHTML" in node) {
 
-     // Don't trust getAttribute/hasAttribute in IE 6-8, instead check the element's outerHTML
 
-     var outerHTML      = node.outerHTML.toLowerCase(),
 
-         // TODO: This might not work for attributes without value: <input disabled>
 
-         hasAttribute   = outerHTML.indexOf(" " + attributeName +  "=") != -1;
 
-     return hasAttribute ? node.getAttribute(attributeName) : null;
 
-   } else{
 
-     return node.getAttribute(attributeName);
 
-   }
 
- };
 
- ;/**
 
-  * Get all attributes of an element
 
-  *
 
-  * IE gives wrong results for hasAttribute/getAttribute, for example:
 
-  *    var td = document.createElement("td");
 
-  *    td.getAttribute("rowspan"); // => "1" in IE
 
-  *
 
-  * Therefore we have to check the element's outerHTML for the attribute
 
- */
 
- wysihtml5.dom.getAttributes = function(node) {
 
-   var HAS_GET_ATTRIBUTE_BUG = !wysihtml5.browser.supportsGetAttributeCorrectly(),
 
-       nodeName = node.nodeName,
 
-       attributes = [],
 
-       attr;
 
-   for (attr in node.attributes) {
 
-     if ((node.attributes.hasOwnProperty && node.attributes.hasOwnProperty(attr)) || (!node.attributes.hasOwnProperty && Object.prototype.hasOwnProperty.call(node.attributes, attr)))  {
 
-       if (node.attributes[attr].specified) {
 
-         if (nodeName == "IMG" && node.attributes[attr].name.toLowerCase() == "src" && wysihtml5.dom.isLoadedImage(node) === true) {
 
-           attributes['src'] = node.src;
 
-         } else if (wysihtml5.lang.array(['rowspan', 'colspan']).contains(node.attributes[attr].name.toLowerCase()) && HAS_GET_ATTRIBUTE_BUG) {
 
-           if (node.attributes[attr].value !== 1) {
 
-             attributes[node.attributes[attr].name] = node.attributes[attr].value;
 
-           }
 
-         } else {
 
-           attributes[node.attributes[attr].name] = node.attributes[attr].value;
 
-         }
 
-       }
 
-     }
 
-   }
 
-   return attributes;
 
- };;/**
 
-    * Check whether the given node is a proper loaded image
 
-    * FIXME: Returns undefined when unknown (Chrome, Safari)
 
- */
 
- wysihtml5.dom.isLoadedImage = function (node) {
 
-   try {
 
-     return node.complete && !node.mozMatchesSelector(":-moz-broken");
 
-   } catch(e) {
 
-     if (node.complete && node.readyState === "complete") {
 
-       return true;
 
-     }
 
-   }
 
- };
 
- ;(function(wysihtml5) {
 
-     var api = wysihtml5.dom;
 
-     var MapCell = function(cell) {
 
-       this.el = cell;
 
-       this.isColspan= false;
 
-       this.isRowspan= false;
 
-       this.firstCol= true;
 
-       this.lastCol= true;
 
-       this.firstRow= true;
 
-       this.lastRow= true;
 
-       this.isReal= true;
 
-       this.spanCollection= [];
 
-       this.modified = false;
 
-     };
 
-     var TableModifyerByCell = function (cell, table) {
 
-         if (cell) {
 
-             this.cell = cell;
 
-             this.table = api.getParentElement(cell, { nodeName: ["TABLE"] });
 
-         } else if (table) {
 
-             this.table = table;
 
-             this.cell = this.table.querySelectorAll('th, td')[0];
 
-         }
 
-     };
 
-     function queryInList(list, query) {
 
-         var ret = [],
 
-             q;
 
-         for (var e = 0, len = list.length; e < len; e++) {
 
-             q = list[e].querySelectorAll(query);
 
-             if (q) {
 
-                 for(var i = q.length; i--; ret.unshift(q[i]));
 
-             }
 
-         }
 
-         return ret;
 
-     }
 
-     function removeElement(el) {
 
-         el.parentNode.removeChild(el);
 
-     }
 
-     function insertAfter(referenceNode, newNode) {
 
-         referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
 
-     }
 
-     function nextNode(node, tag) {
 
-         var element = node.nextSibling;
 
-         while (element.nodeType !=1) {
 
-             element = element.nextSibling;
 
-             if (!tag || tag == element.tagName.toLowerCase()) {
 
-                 return element;
 
-             }
 
-         }
 
-         return null;
 
-     }
 
-     TableModifyerByCell.prototype = {
 
-         addSpannedCellToMap: function(cell, map, r, c, cspan, rspan) {
 
-             var spanCollect = [],
 
-                 rmax = r + ((rspan) ? parseInt(rspan, 10) - 1 : 0),
 
-                 cmax = c + ((cspan) ? parseInt(cspan, 10) - 1 : 0);
 
-             for (var rr = r; rr <= rmax; rr++) {
 
-                 if (typeof map[rr] == "undefined") { map[rr] = []; }
 
-                 for (var cc = c; cc <= cmax; cc++) {
 
-                     map[rr][cc] = new MapCell(cell);
 
-                     map[rr][cc].isColspan = (cspan && parseInt(cspan, 10) > 1);
 
-                     map[rr][cc].isRowspan = (rspan && parseInt(rspan, 10) > 1);
 
-                     map[rr][cc].firstCol = cc == c;
 
-                     map[rr][cc].lastCol = cc == cmax;
 
-                     map[rr][cc].firstRow = rr == r;
 
-                     map[rr][cc].lastRow = rr == rmax;
 
-                     map[rr][cc].isReal = cc == c && rr == r;
 
-                     map[rr][cc].spanCollection = spanCollect;
 
-                     spanCollect.push(map[rr][cc]);
 
-                 }
 
-             }
 
-         },
 
-         setCellAsModified: function(cell) {
 
-             cell.modified = true;
 
-             if (cell.spanCollection.length > 0) {
 
-               for (var s = 0, smax = cell.spanCollection.length; s < smax; s++) {
 
-                 cell.spanCollection[s].modified = true;
 
-               }
 
-             }
 
-         },
 
-         setTableMap: function() {
 
-             var map = [];
 
-             var tableRows = this.getTableRows(),
 
-                 ridx, row, cells, cidx, cell,
 
-                 c,
 
-                 cspan, rspan;
 
-             for (ridx = 0; ridx < tableRows.length; ridx++) {
 
-                 row = tableRows[ridx];
 
-                 cells = this.getRowCells(row);
 
-                 c = 0;
 
-                 if (typeof map[ridx] == "undefined") { map[ridx] = []; }
 
-                 for (cidx = 0; cidx < cells.length; cidx++) {
 
-                     cell = cells[cidx];
 
-                     // If cell allready set means it is set by col or rowspan,
 
-                     // so increase cols index until free col is found
 
-                     while (typeof map[ridx][c] != "undefined") { c++; }
 
-                     cspan = api.getAttribute(cell, 'colspan');
 
-                     rspan = api.getAttribute(cell, 'rowspan');
 
-                     if (cspan || rspan) {
 
-                         this.addSpannedCellToMap(cell, map, ridx, c, cspan, rspan);
 
-                         c = c + ((cspan) ? parseInt(cspan, 10) : 1);
 
-                     } else {
 
-                         map[ridx][c] = new MapCell(cell);
 
-                         c++;
 
-                     }
 
-                 }
 
-             }
 
-             this.map = map;
 
-             return map;
 
-         },
 
-         getRowCells: function(row) {
 
-             var inlineTables = this.table.querySelectorAll('table'),
 
-                 inlineCells = (inlineTables) ? queryInList(inlineTables, 'th, td') : [],
 
-                 allCells = row.querySelectorAll('th, td'),
 
-                 tableCells = (inlineCells.length > 0) ? wysihtml5.lang.array(allCells).without(inlineCells) : allCells;
 
-             return tableCells;
 
-         },
 
-         getTableRows: function() {
 
-           var inlineTables = this.table.querySelectorAll('table'),
 
-               inlineRows = (inlineTables) ? queryInList(inlineTables, 'tr') : [],
 
-               allRows = this.table.querySelectorAll('tr'),
 
-               tableRows = (inlineRows.length > 0) ? wysihtml5.lang.array(allRows).without(inlineRows) : allRows;
 
-           return tableRows;
 
-         },
 
-         getMapIndex: function(cell) {
 
-           var r_length = this.map.length,
 
-               c_length = (this.map && this.map[0]) ? this.map[0].length : 0;
 
-           for (var r_idx = 0;r_idx < r_length; r_idx++) {
 
-               for (var c_idx = 0;c_idx < c_length; c_idx++) {
 
-                   if (this.map[r_idx][c_idx].el === cell) {
 
-                       return {'row': r_idx, 'col': c_idx};
 
-                   }
 
-               }
 
-           }
 
-           return false;
 
-         },
 
-         getElementAtIndex: function(idx) {
 
-             this.setTableMap();
 
-             if (this.map[idx.row] && this.map[idx.row][idx.col] && this.map[idx.row][idx.col].el) {
 
-                 return this.map[idx.row][idx.col].el;
 
-             }
 
-             return null;
 
-         },
 
-         getMapElsTo: function(to_cell) {
 
-             var els = [];
 
-             this.setTableMap();
 
-             this.idx_start = this.getMapIndex(this.cell);
 
-             this.idx_end = this.getMapIndex(to_cell);
 
-             // switch indexes if start is bigger than end
 
-             if (this.idx_start.row > this.idx_end.row || (this.idx_start.row == this.idx_end.row && this.idx_start.col > this.idx_end.col)) {
 
-                 var temp_idx = this.idx_start;
 
-                 this.idx_start = this.idx_end;
 
-                 this.idx_end = temp_idx;
 
-             }
 
-             if (this.idx_start.col > this.idx_end.col) {
 
-                 var temp_cidx = this.idx_start.col;
 
-                 this.idx_start.col = this.idx_end.col;
 
-                 this.idx_end.col = temp_cidx;
 
-             }
 
-             if (this.idx_start != null && this.idx_end != null) {
 
-                 for (var row = this.idx_start.row, maxr = this.idx_end.row; row <= maxr; row++) {
 
-                     for (var col = this.idx_start.col, maxc = this.idx_end.col; col <= maxc; col++) {
 
-                         els.push(this.map[row][col].el);
 
-                     }
 
-                 }
 
-             }
 
-             return els;
 
-         },
 
-         orderSelectionEnds: function(secondcell) {
 
-             this.setTableMap();
 
-             this.idx_start = this.getMapIndex(this.cell);
 
-             this.idx_end = this.getMapIndex(secondcell);
 
-             // switch indexes if start is bigger than end
 
-             if (this.idx_start.row > this.idx_end.row || (this.idx_start.row == this.idx_end.row && this.idx_start.col > this.idx_end.col)) {
 
-                 var temp_idx = this.idx_start;
 
-                 this.idx_start = this.idx_end;
 
-                 this.idx_end = temp_idx;
 
-             }
 
-             if (this.idx_start.col > this.idx_end.col) {
 
-                 var temp_cidx = this.idx_start.col;
 
-                 this.idx_start.col = this.idx_end.col;
 
-                 this.idx_end.col = temp_cidx;
 
-             }
 
-             return {
 
-                 "start": this.map[this.idx_start.row][this.idx_start.col].el,
 
-                 "end": this.map[this.idx_end.row][this.idx_end.col].el
 
-             };
 
-         },
 
-         createCells: function(tag, nr, attrs) {
 
-             var doc = this.table.ownerDocument,
 
-                 frag = doc.createDocumentFragment(),
 
-                 cell;
 
-             for (var i = 0; i < nr; i++) {
 
-                 cell = doc.createElement(tag);
 
-                 if (attrs) {
 
-                     for (var attr in attrs) {
 
-                         if (attrs.hasOwnProperty(attr)) {
 
-                             cell.setAttribute(attr, attrs[attr]);
 
-                         }
 
-                     }
 
-                 }
 
-                 // add non breaking space
 
-                 cell.appendChild(document.createTextNode("\u00a0"));
 
-                 frag.appendChild(cell);
 
-             }
 
-             return frag;
 
-         },
 
-         // Returns next real cell (not part of spanned cell unless first) on row if selected index is not real. I no real cells -1 will be returned
 
-         correctColIndexForUnreals: function(col, row) {
 
-             var r = this.map[row],
 
-                 corrIdx = -1;
 
-             for (var i = 0, max = col; i < col; i++) {
 
-                 if (r[i].isReal){
 
-                     corrIdx++;
 
-                 }
 
-             }
 
-             return corrIdx;
 
-         },
 
-         getLastNewCellOnRow: function(row, rowLimit) {
 
-             var cells = this.getRowCells(row),
 
-                 cell, idx;
 
-             for (var cidx = 0, cmax = cells.length; cidx < cmax; cidx++) {
 
-                 cell = cells[cidx];
 
-                 idx = this.getMapIndex(cell);
 
-                 if (idx === false || (typeof rowLimit != "undefined" && idx.row != rowLimit)) {
 
-                     return cell;
 
-                 }
 
-             }
 
-             return null;
 
-         },
 
-         removeEmptyTable: function() {
 
-             var cells = this.table.querySelectorAll('td, th');
 
-             if (!cells || cells.length == 0) {
 
-                 removeElement(this.table);
 
-                 return true;
 
-             } else {
 
-                 return false;
 
-             }
 
-         },
 
-         // Splits merged cell on row to unique cells
 
-         splitRowToCells: function(cell) {
 
-             if (cell.isColspan) {
 
-                 var colspan = parseInt(api.getAttribute(cell.el, 'colspan') || 1, 10),
 
-                     cType = cell.el.tagName.toLowerCase();
 
-                 if (colspan > 1) {
 
-                     var newCells = this.createCells(cType, colspan -1);
 
-                     insertAfter(cell.el, newCells);
 
-                 }
 
-                 cell.el.removeAttribute('colspan');
 
-             }
 
-         },
 
-         getRealRowEl: function(force, idx) {
 
-             var r = null,
 
-                 c = null;
 
-             idx = idx || this.idx;
 
-             for (var cidx = 0, cmax = this.map[idx.row].length; cidx < cmax; cidx++) {
 
-                 c = this.map[idx.row][cidx];
 
-                 if (c.isReal) {
 
-                     r = api.getParentElement(c.el, { nodeName: ["TR"] });
 
-                     if (r) {
 
-                         return r;
 
-                     }
 
-                 }
 
-             }
 
-             if (r === null && force) {
 
-                 r = api.getParentElement(this.map[idx.row][idx.col].el, { nodeName: ["TR"] }) || null;
 
-             }
 
-             return r;
 
-         },
 
-         injectRowAt: function(row, col, colspan, cType, c) {
 
-             var r = this.getRealRowEl(false, {'row': row, 'col': col}),
 
-                 new_cells = this.createCells(cType, colspan);
 
-             if (r) {
 
-                 var n_cidx = this.correctColIndexForUnreals(col, row);
 
-                 if (n_cidx >= 0) {
 
-                     insertAfter(this.getRowCells(r)[n_cidx], new_cells);
 
-                 } else {
 
-                     r.insertBefore(new_cells, r.firstChild);
 
-                 }
 
-             } else {
 
-                 var rr = this.table.ownerDocument.createElement('tr');
 
-                 rr.appendChild(new_cells);
 
-                 insertAfter(api.getParentElement(c.el, { nodeName: ["TR"] }), rr);
 
-             }
 
-         },
 
-         canMerge: function(to) {
 
-             this.to = to;
 
-             this.setTableMap();
 
-             this.idx_start = this.getMapIndex(this.cell);
 
-             this.idx_end = this.getMapIndex(this.to);
 
-             // switch indexes if start is bigger than end
 
-             if (this.idx_start.row > this.idx_end.row || (this.idx_start.row == this.idx_end.row && this.idx_start.col > this.idx_end.col)) {
 
-                 var temp_idx = this.idx_start;
 
-                 this.idx_start = this.idx_end;
 
-                 this.idx_end = temp_idx;
 
-             }
 
-             if (this.idx_start.col > this.idx_end.col) {
 
-                 var temp_cidx = this.idx_start.col;
 
-                 this.idx_start.col = this.idx_end.col;
 
-                 this.idx_end.col = temp_cidx;
 
-             }
 
-             for (var row = this.idx_start.row, maxr = this.idx_end.row; row <= maxr; row++) {
 
-                 for (var col = this.idx_start.col, maxc = this.idx_end.col; col <= maxc; col++) {
 
-                     if (this.map[row][col].isColspan || this.map[row][col].isRowspan) {
 
-                         return false;
 
-                     }
 
-                 }
 
-             }
 
-             return true;
 
-         },
 
-         decreaseCellSpan: function(cell, span) {
 
-             var nr = parseInt(api.getAttribute(cell.el, span), 10) - 1;
 
-             if (nr >= 1) {
 
-                 cell.el.setAttribute(span, nr);
 
-             } else {
 
-                 cell.el.removeAttribute(span);
 
-                 if (span == 'colspan') {
 
-                     cell.isColspan = false;
 
-                 }
 
-                 if (span == 'rowspan') {
 
-                     cell.isRowspan = false;
 
-                 }
 
-                 cell.firstCol = true;
 
-                 cell.lastCol = true;
 
-                 cell.firstRow = true;
 
-                 cell.lastRow = true;
 
-                 cell.isReal = true;
 
-             }
 
-         },
 
-         removeSurplusLines: function() {
 
-             var row, cell, ridx, rmax, cidx, cmax, allRowspan;
 
-             this.setTableMap();
 
-             if (this.map) {
 
-                 ridx = 0;
 
-                 rmax = this.map.length;
 
-                 for (;ridx < rmax; ridx++) {
 
-                     row = this.map[ridx];
 
-                     allRowspan = true;
 
-                     cidx = 0;
 
-                     cmax = row.length;
 
-                     for (; cidx < cmax; cidx++) {
 
-                         cell = row[cidx];
 
-                         if (!(api.getAttribute(cell.el, "rowspan") && parseInt(api.getAttribute(cell.el, "rowspan"), 10) > 1 && cell.firstRow !== true)) {
 
-                             allRowspan = false;
 
-                             break;
 
-                         }
 
-                     }
 
-                     if (allRowspan) {
 
-                         cidx = 0;
 
-                         for (; cidx < cmax; cidx++) {
 
-                             this.decreaseCellSpan(row[cidx], 'rowspan');
 
-                         }
 
-                     }
 
-                 }
 
-                 // remove rows without cells
 
-                 var tableRows = this.getTableRows();
 
-                 ridx = 0;
 
-                 rmax = tableRows.length;
 
-                 for (;ridx < rmax; ridx++) {
 
-                     row = tableRows[ridx];
 
-                     if (row.childNodes.length == 0 && (/^\s*$/.test(row.textContent || row.innerText))) {
 
-                         removeElement(row);
 
-                     }
 
-                 }
 
-             }
 
-         },
 
-         fillMissingCells: function() {
 
-             var r_max = 0,
 
-                 c_max = 0,
 
-                 prevcell = null;
 
-             this.setTableMap();
 
-             if (this.map) {
 
-                 // find maximal dimensions of broken table
 
-                 r_max = this.map.length;
 
-                 for (var ridx = 0; ridx < r_max; ridx++) {
 
-                     if (this.map[ridx].length > c_max) { c_max = this.map[ridx].length; }
 
-                 }
 
-                 for (var row = 0; row < r_max; row++) {
 
-                     for (var col = 0; col < c_max; col++) {
 
-                         if (this.map[row] && !this.map[row][col]) {
 
-                             if (col > 0) {
 
-                                 this.map[row][col] = new MapCell(this.createCells('td', 1));
 
-                                 prevcell = this.map[row][col-1];
 
-                                 if (prevcell && prevcell.el && prevcell.el.parent) { // if parent does not exist element is removed from dom
 
-                                     insertAfter(this.map[row][col-1].el, this.map[row][col].el);
 
-                                 }
 
-                             }
 
-                         }
 
-                     }
 
-                 }
 
-             }
 
-         },
 
-         rectify: function() {
 
-             if (!this.removeEmptyTable()) {
 
-                 this.removeSurplusLines();
 
-                 this.fillMissingCells();
 
-                 return true;
 
-             } else {
 
-                 return false;
 
-             }
 
-         },
 
-         unmerge: function() {
 
-             if (this.rectify()) {
 
-                 this.setTableMap();
 
-                 this.idx = this.getMapIndex(this.cell);
 
-                 if (this.idx) {
 
-                     var thisCell = this.map[this.idx.row][this.idx.col],
 
-                         colspan = (api.getAttribute(thisCell.el, "colspan")) ? parseInt(api.getAttribute(thisCell.el, "colspan"), 10) : 1,
 
-                         cType = thisCell.el.tagName.toLowerCase();
 
-                     if (thisCell.isRowspan) {
 
-                         var rowspan = parseInt(api.getAttribute(thisCell.el, "rowspan"), 10);
 
-                         if (rowspan > 1) {
 
-                             for (var nr = 1, maxr = rowspan - 1; nr <= maxr; nr++){
 
-                                 this.injectRowAt(this.idx.row + nr, this.idx.col, colspan, cType, thisCell);
 
-                             }
 
-                         }
 
-                         thisCell.el.removeAttribute('rowspan');
 
-                     }
 
-                     this.splitRowToCells(thisCell);
 
-                 }
 
-             }
 
-         },
 
-         // merges cells from start cell (defined in creating obj) to "to" cell
 
-         merge: function(to) {
 
-             if (this.rectify()) {
 
-                 if (this.canMerge(to)) {
 
-                     var rowspan = this.idx_end.row - this.idx_start.row + 1,
 
-                         colspan = this.idx_end.col - this.idx_start.col + 1;
 
-                     for (var row = this.idx_start.row, maxr = this.idx_end.row; row <= maxr; row++) {
 
-                         for (var col = this.idx_start.col, maxc = this.idx_end.col; col <= maxc; col++) {
 
-                             if (row == this.idx_start.row && col == this.idx_start.col) {
 
-                                 if (rowspan > 1) {
 
-                                     this.map[row][col].el.setAttribute('rowspan', rowspan);
 
-                                 }
 
-                                 if (colspan > 1) {
 
-                                     this.map[row][col].el.setAttribute('colspan', colspan);
 
-                                 }
 
-                             } else {
 
-                                 // transfer content
 
-                                 if (!(/^\s*<br\/?>\s*$/.test(this.map[row][col].el.innerHTML.toLowerCase()))) {
 
-                                     this.map[this.idx_start.row][this.idx_start.col].el.innerHTML += ' ' + this.map[row][col].el.innerHTML;
 
-                                 }
 
-                                 removeElement(this.map[row][col].el);
 
-                             }
 
-                         }
 
-                     }
 
-                     this.rectify();
 
-                 } else {
 
-                     if (window.console) {
 
-                         console.log('Do not know how to merge allready merged cells.');
 
-                     }
 
-                 }
 
-             }
 
-         },
 
-         // Decreases rowspan of a cell if it is done on first cell of rowspan row (real cell)
 
-         // Cell is moved to next row (if it is real)
 
-         collapseCellToNextRow: function(cell) {
 
-             var cellIdx = this.getMapIndex(cell.el),
 
-                 newRowIdx = cellIdx.row + 1,
 
-                 newIdx = {'row': newRowIdx, 'col': cellIdx.col};
 
-             if (newRowIdx < this.map.length) {
 
-                 var row = this.getRealRowEl(false, newIdx);
 
-                 if (row !== null) {
 
-                     var n_cidx = this.correctColIndexForUnreals(newIdx.col, newIdx.row);
 
-                     if (n_cidx >= 0) {
 
-                         insertAfter(this.getRowCells(row)[n_cidx], cell.el);
 
-                     } else {
 
-                         var lastCell = this.getLastNewCellOnRow(row, newRowIdx);
 
-                         if (lastCell !== null) {
 
-                             insertAfter(lastCell, cell.el);
 
-                         } else {
 
-                             row.insertBefore(cell.el, row.firstChild);
 
-                         }
 
-                     }
 
-                     if (parseInt(api.getAttribute(cell.el, 'rowspan'), 10) > 2) {
 
-                         cell.el.setAttribute('rowspan', parseInt(api.getAttribute(cell.el, 'rowspan'), 10) - 1);
 
-                     } else {
 
-                         cell.el.removeAttribute('rowspan');
 
-                     }
 
-                 }
 
-             }
 
-         },
 
-         // Removes a cell when removing a row
 
-         // If is rowspan cell then decreases the rowspan
 
-         // and moves cell to next row if needed (is first cell of rowspan)
 
-         removeRowCell: function(cell) {
 
-             if (cell.isReal) {
 
-                if (cell.isRowspan) {
 
-                    this.collapseCellToNextRow(cell);
 
-                } else {
 
-                    removeElement(cell.el);
 
-                }
 
-             } else {
 
-                 if (parseInt(api.getAttribute(cell.el, 'rowspan'), 10) > 2) {
 
-                     cell.el.setAttribute('rowspan', parseInt(api.getAttribute(cell.el, 'rowspan'), 10) - 1);
 
-                 } else {
 
-                     cell.el.removeAttribute('rowspan');
 
-                 }
 
-             }
 
-         },
 
-         getRowElementsByCell: function() {
 
-             var cells = [];
 
-             this.setTableMap();
 
-             this.idx = this.getMapIndex(this.cell);
 
-             if (this.idx !== false) {
 
-                 var modRow = this.map[this.idx.row];
 
-                 for (var cidx = 0, cmax = modRow.length; cidx < cmax; cidx++) {
 
-                     if (modRow[cidx].isReal) {
 
-                         cells.push(modRow[cidx].el);
 
-                     }
 
-                 }
 
-             }
 
-             return cells;
 
-         },
 
-         getColumnElementsByCell: function() {
 
-             var cells = [];
 
-             this.setTableMap();
 
-             this.idx = this.getMapIndex(this.cell);
 
-             if (this.idx !== false) {
 
-                 for (var ridx = 0, rmax = this.map.length; ridx < rmax; ridx++) {
 
-                     if (this.map[ridx][this.idx.col] && this.map[ridx][this.idx.col].isReal) {
 
-                         cells.push(this.map[ridx][this.idx.col].el);
 
-                     }
 
-                 }
 
-             }
 
-             return cells;
 
-         },
 
-         // Removes the row of selected cell
 
-         removeRow: function() {
 
-             var oldRow = api.getParentElement(this.cell, { nodeName: ["TR"] });
 
-             if (oldRow) {
 
-                 this.setTableMap();
 
-                 this.idx = this.getMapIndex(this.cell);
 
-                 if (this.idx !== false) {
 
-                     var modRow = this.map[this.idx.row];
 
-                     for (var cidx = 0, cmax = modRow.length; cidx < cmax; cidx++) {
 
-                         if (!modRow[cidx].modified) {
 
-                             this.setCellAsModified(modRow[cidx]);
 
-                             this.removeRowCell(modRow[cidx]);
 
-                         }
 
-                     }
 
-                 }
 
-                 removeElement(oldRow);
 
-             }
 
-         },
 
-         removeColCell: function(cell) {
 
-             if (cell.isColspan) {
 
-                 if (parseInt(api.getAttribute(cell.el, 'colspan'), 10) > 2) {
 
-                     cell.el.setAttribute('colspan', parseInt(api.getAttribute(cell.el, 'colspan'), 10) - 1);
 
-                 } else {
 
-                     cell.el.removeAttribute('colspan');
 
-                 }
 
-             } else if (cell.isReal) {
 
-                 removeElement(cell.el);
 
-             }
 
-         },
 
-         removeColumn: function() {
 
-             this.setTableMap();
 
-             this.idx = this.getMapIndex(this.cell);
 
-             if (this.idx !== false) {
 
-                 for (var ridx = 0, rmax = this.map.length; ridx < rmax; ridx++) {
 
-                     if (!this.map[ridx][this.idx.col].modified) {
 
-                         this.setCellAsModified(this.map[ridx][this.idx.col]);
 
-                         this.removeColCell(this.map[ridx][this.idx.col]);
 
-                     }
 
-                 }
 
-             }
 
-         },
 
-         // removes row or column by selected cell element
 
-         remove: function(what) {
 
-             if (this.rectify()) {
 
-                 switch (what) {
 
-                     case 'row':
 
-                         this.removeRow();
 
-                     break;
 
-                     case 'column':
 
-                         this.removeColumn();
 
-                     break;
 
-                 }
 
-                 this.rectify();
 
-             }
 
-         },
 
-         addRow: function(where) {
 
-             var doc = this.table.ownerDocument;
 
-             this.setTableMap();
 
-             this.idx = this.getMapIndex(this.cell);
 
-             if (where == "below" && api.getAttribute(this.cell, 'rowspan')) {
 
-                 this.idx.row = this.idx.row + parseInt(api.getAttribute(this.cell, 'rowspan'), 10) - 1;
 
-             }
 
-             if (this.idx !== false) {
 
-                 var modRow = this.map[this.idx.row],
 
-                     newRow = doc.createElement('tr');
 
-                 for (var ridx = 0, rmax = modRow.length; ridx < rmax; ridx++) {
 
-                     if (!modRow[ridx].modified) {
 
-                         this.setCellAsModified(modRow[ridx]);
 
-                         this.addRowCell(modRow[ridx], newRow, where);
 
-                     }
 
-                 }
 
-                 switch (where) {
 
-                     case 'below':
 
-                         insertAfter(this.getRealRowEl(true), newRow);
 
-                     break;
 
-                     case 'above':
 
-                         var cr = api.getParentElement(this.map[this.idx.row][this.idx.col].el, { nodeName: ["TR"] });
 
-                         if (cr) {
 
-                             cr.parentNode.insertBefore(newRow, cr);
 
-                         }
 
-                     break;
 
-                 }
 
-             }
 
-         },
 
-         addRowCell: function(cell, row, where) {
 
-             var colSpanAttr = (cell.isColspan) ? {"colspan" : api.getAttribute(cell.el, 'colspan')} : null;
 
-             if (cell.isReal) {
 
-                 if (where != 'above' && cell.isRowspan) {
 
-                     cell.el.setAttribute('rowspan', parseInt(api.getAttribute(cell.el,'rowspan'), 10) + 1);
 
-                 } else {
 
-                     row.appendChild(this.createCells('td', 1, colSpanAttr));
 
-                 }
 
-             } else {
 
-                 if (where != 'above' && cell.isRowspan && cell.lastRow) {
 
-                     row.appendChild(this.createCells('td', 1, colSpanAttr));
 
-                 } else if (c.isRowspan) {
 
-                     cell.el.attr('rowspan', parseInt(api.getAttribute(cell.el, 'rowspan'), 10) + 1);
 
-                 }
 
-             }
 
-         },
 
-         add: function(where) {
 
-             if (this.rectify()) {
 
-                 if (where == 'below' || where == 'above') {
 
-                     this.addRow(where);
 
-                 }
 
-                 if (where == 'before' || where == 'after') {
 
-                     this.addColumn(where);
 
-                 }
 
-             }
 
-         },
 
-         addColCell: function (cell, ridx, where) {
 
-             var doAdd,
 
-                 cType = cell.el.tagName.toLowerCase();
 
-             // defines add cell vs expand cell conditions
 
-             // true means add
 
-             switch (where) {
 
-                 case "before":
 
-                     doAdd = (!cell.isColspan || cell.firstCol);
 
-                 break;
 
-                 case "after":
 
-                     doAdd = (!cell.isColspan || cell.lastCol || (cell.isColspan && c.el == this.cell));
 
-                 break;
 
-             }
 
-             if (doAdd){
 
-                 // adds a cell before or after current cell element
 
-                 switch (where) {
 
-                     case "before":
 
-                         cell.el.parentNode.insertBefore(this.createCells(cType, 1), cell.el);
 
-                     break;
 
-                     case "after":
 
-                         insertAfter(cell.el, this.createCells(cType, 1));
 
-                     break;
 
-                 }
 
-                 // handles if cell has rowspan
 
-                 if (cell.isRowspan) {
 
-                     this.handleCellAddWithRowspan(cell, ridx+1, where);
 
-                 }
 
-             } else {
 
-                 // expands cell
 
-                 cell.el.setAttribute('colspan',  parseInt(api.getAttribute(cell.el, 'colspan'), 10) + 1);
 
-             }
 
-         },
 
-         addColumn: function(where) {
 
-             var row, modCell;
 
-             this.setTableMap();
 
-             this.idx = this.getMapIndex(this.cell);
 
-             if (where == "after" && api.getAttribute(this.cell, 'colspan')) {
 
-               this.idx.col = this.idx.col + parseInt(api.getAttribute(this.cell, 'colspan'), 10) - 1;
 
-             }
 
-             if (this.idx !== false) {
 
-                 for (var ridx = 0, rmax = this.map.length; ridx < rmax; ridx++ ) {
 
-                     row = this.map[ridx];
 
-                     if (row[this.idx.col]) {
 
-                         modCell = row[this.idx.col];
 
-                         if (!modCell.modified) {
 
-                             this.setCellAsModified(modCell);
 
-                             this.addColCell(modCell, ridx , where);
 
-                         }
 
-                     }
 
-                 }
 
-             }
 
-         },
 
-         handleCellAddWithRowspan: function (cell, ridx, where) {
 
-             var addRowsNr = parseInt(api.getAttribute(this.cell, 'rowspan'), 10) - 1,
 
-                 crow = api.getParentElement(cell.el, { nodeName: ["TR"] }),
 
-                 cType = cell.el.tagName.toLowerCase(),
 
-                 cidx, temp_r_cells,
 
-                 doc = this.table.ownerDocument,
 
-                 nrow;
 
-             for (var i = 0; i < addRowsNr; i++) {
 
-                 cidx = this.correctColIndexForUnreals(this.idx.col, (ridx + i));
 
-                 crow = nextNode(crow, 'tr');
 
-                 if (crow) {
 
-                     if (cidx > 0) {
 
-                         switch (where) {
 
-                             case "before":
 
-                                 temp_r_cells = this.getRowCells(crow);
 
-                                 if (cidx > 0 && this.map[ridx + i][this.idx.col].el != temp_r_cells[cidx] && cidx == temp_r_cells.length - 1) {
 
-                                      insertAfter(temp_r_cells[cidx], this.createCells(cType, 1));
 
-                                 } else {
 
-                                     temp_r_cells[cidx].parentNode.insertBefore(this.createCells(cType, 1), temp_r_cells[cidx]);
 
-                                 }
 
-                             break;
 
-                             case "after":
 
-                                 insertAfter(this.getRowCells(crow)[cidx], this.createCells(cType, 1));
 
-                             break;
 
-                         }
 
-                     } else {
 
-                         crow.insertBefore(this.createCells(cType, 1), crow.firstChild);
 
-                     }
 
-                 } else {
 
-                     nrow = doc.createElement('tr');
 
-                     nrow.appendChild(this.createCells(cType, 1));
 
-                     this.table.appendChild(nrow);
 
-                 }
 
-             }
 
-         }
 
-     };
 
-     api.table = {
 
-         getCellsBetween: function(cell1, cell2) {
 
-             var c1 = new TableModifyerByCell(cell1);
 
-             return c1.getMapElsTo(cell2);
 
-         },
 
-         addCells: function(cell, where) {
 
-             var c = new TableModifyerByCell(cell);
 
-             c.add(where);
 
-         },
 
-         removeCells: function(cell, what) {
 
-             var c = new TableModifyerByCell(cell);
 
-             c.remove(what);
 
-         },
 
-         mergeCellsBetween: function(cell1, cell2) {
 
-             var c1 = new TableModifyerByCell(cell1);
 
-             c1.merge(cell2);
 
-         },
 
-         unmergeCell: function(cell) {
 
-             var c = new TableModifyerByCell(cell);
 
-             c.unmerge();
 
-         },
 
-         orderSelectionEnds: function(cell, cell2) {
 
-             var c = new TableModifyerByCell(cell);
 
-             return c.orderSelectionEnds(cell2);
 
-         },
 
-         indexOf: function(cell) {
 
-             var c = new TableModifyerByCell(cell);
 
-             c.setTableMap();
 
-             return c.getMapIndex(cell);
 
-         },
 
-         findCell: function(table, idx) {
 
-             var c = new TableModifyerByCell(null, table);
 
-             return c.getElementAtIndex(idx);
 
-         },
 
-         findRowByCell: function(cell) {
 
-             var c = new TableModifyerByCell(cell);
 
-             return c.getRowElementsByCell();
 
-         },
 
-         findColumnByCell: function(cell) {
 
-             var c = new TableModifyerByCell(cell);
 
-             return c.getColumnElementsByCell();
 
-         },
 
-         canMerge: function(cell1, cell2) {
 
-             var c = new TableModifyerByCell(cell1);
 
-             return c.canMerge(cell2);
 
-         }
 
-     };
 
- })(wysihtml5);
 
- ;// does a selector query on element or array of elements
 
- wysihtml5.dom.query = function(elements, query) {
 
-     var ret = [],
 
-         q;
 
-     if (elements.nodeType) {
 
-         elements = [elements];
 
-     }
 
-     for (var e = 0, len = elements.length; e < len; e++) {
 
-         q = elements[e].querySelectorAll(query);
 
-         if (q) {
 
-             for(var i = q.length; i--; ret.unshift(q[i]));
 
-         }
 
-     }
 
-     return ret;
 
- };
 
- ;wysihtml5.dom.compareDocumentPosition = (function() {
 
-   var documentElement = document.documentElement;
 
-   if (documentElement.compareDocumentPosition) {
 
-     return function(container, element) {
 
-       return container.compareDocumentPosition(element);
 
-     };
 
-   } else {
 
-     return function( container, element ) {
 
-       // implementation borrowed from https://github.com/tmpvar/jsdom/blob/681a8524b663281a0f58348c6129c8c184efc62c/lib/jsdom/level3/core.js // MIT license
 
-       var thisOwner, otherOwner;
 
-       if( container.nodeType === 9) // Node.DOCUMENT_NODE
 
-         thisOwner = container;
 
-       else
 
-         thisOwner = container.ownerDocument;
 
-       if( element.nodeType === 9) // Node.DOCUMENT_NODE
 
-         otherOwner = element;
 
-       else
 
-         otherOwner = element.ownerDocument;
 
-       if( container === element ) return 0;
 
-       if( container === element.ownerDocument ) return 4 + 16; //Node.DOCUMENT_POSITION_FOLLOWING + Node.DOCUMENT_POSITION_CONTAINED_BY;
 
-       if( container.ownerDocument === element ) return 2 + 8;  //Node.DOCUMENT_POSITION_PRECEDING + Node.DOCUMENT_POSITION_CONTAINS;
 
-       if( thisOwner !== otherOwner ) return 1; // Node.DOCUMENT_POSITION_DISCONNECTED;
 
-       // Text nodes for attributes does not have a _parentNode. So we need to find them as attribute child.
 
-       if( container.nodeType === 2 /*Node.ATTRIBUTE_NODE*/ && container.childNodes && wysihtml5.lang.array(container.childNodes).indexOf( element ) !== -1)
 
-         return 4 + 16; //Node.DOCUMENT_POSITION_FOLLOWING + Node.DOCUMENT_POSITION_CONTAINED_BY;
 
-       if( element.nodeType === 2 /*Node.ATTRIBUTE_NODE*/ && element.childNodes && wysihtml5.lang.array(element.childNodes).indexOf( container ) !== -1)
 
-         return 2 + 8; //Node.DOCUMENT_POSITION_PRECEDING + Node.DOCUMENT_POSITION_CONTAINS;
 
-       var point = container;
 
-       var parents = [ ];
 
-       var previous = null;
 
-       while( point ) {
 
-         if( point == element ) return 2 + 8; //Node.DOCUMENT_POSITION_PRECEDING + Node.DOCUMENT_POSITION_CONTAINS;
 
-         parents.push( point );
 
-         point = point.parentNode;
 
-       }
 
-       point = element;
 
-       previous = null;
 
-       while( point ) {
 
-         if( point == container ) return 4 + 16; //Node.DOCUMENT_POSITION_FOLLOWING + Node.DOCUMENT_POSITION_CONTAINED_BY;
 
-         var location_index = wysihtml5.lang.array(parents).indexOf( point );
 
-         if( location_index !== -1) {
 
-          var smallest_common_ancestor = parents[ location_index ];
 
-          var this_index = wysihtml5.lang.array(smallest_common_ancestor.childNodes).indexOf( parents[location_index - 1]);//smallest_common_ancestor.childNodes.toArray().indexOf( parents[location_index - 1] );
 
-          var other_index = wysihtml5.lang.array(smallest_common_ancestor.childNodes).indexOf( previous ); //smallest_common_ancestor.childNodes.toArray().indexOf( previous );
 
-          if( this_index > other_index ) {
 
-                return 2; //Node.DOCUMENT_POSITION_PRECEDING;
 
-          }
 
-          else {
 
-            return 4; //Node.DOCUMENT_POSITION_FOLLOWING;
 
-          }
 
-         }
 
-         previous = point;
 
-         point = point.parentNode;
 
-       }
 
-       return 1; //Node.DOCUMENT_POSITION_DISCONNECTED;
 
-     };
 
-   }
 
- })();
 
- ;wysihtml5.dom.unwrap = function(node) {
 
-   if (node.parentNode) {
 
-     while (node.lastChild) {
 
-       wysihtml5.dom.insert(node.lastChild).after(node);
 
-     }
 
-     node.parentNode.removeChild(node);
 
-   }
 
- };;/* 
 
-  * Methods for fetching pasted html before it gets inserted into content
 
- **/
 
- /* Modern event.clipboardData driven approach.
 
-  * Advantage is that it does not have to loose selection or modify dom to catch the data. 
 
-  * IE does not support though.
 
- **/
 
- wysihtml5.dom.getPastedHtml = function(event) {
 
-   var html;
 
-   if (event.clipboardData) {
 
-     if (wysihtml5.lang.array(event.clipboardData.types).contains('text/html')) {
 
-       html = event.clipboardData.getData('text/html');
 
-     } else if (wysihtml5.lang.array(event.clipboardData.types).contains('text/plain')) {
 
-       html = wysihtml5.lang.string(event.clipboardData.getData('text/plain')).escapeHTML(true, true);
 
-     }
 
-   }
 
-   return html;
 
- };
 
- /* Older temprorary contenteditable as paste source catcher method for fallbacks */
 
- wysihtml5.dom.getPastedHtmlWithDiv = function (composer, f) {
 
-   var selBookmark = composer.selection.getBookmark(),
 
-       doc = composer.element.ownerDocument,
 
-       cleanerDiv = doc.createElement('DIV');
 
-   
 
-   doc.body.appendChild(cleanerDiv);
 
-   cleanerDiv.style.width = "1px";
 
-   cleanerDiv.style.height = "1px";
 
-   cleanerDiv.style.overflow = "hidden";
 
-   cleanerDiv.setAttribute('contenteditable', 'true');
 
-   cleanerDiv.focus();
 
-   setTimeout(function () {
 
-     composer.selection.setBookmark(selBookmark);
 
-     f(cleanerDiv.innerHTML);
 
-     cleanerDiv.parentNode.removeChild(cleanerDiv);
 
-   }, 0);
 
- };;/**
 
-  * Fix most common html formatting misbehaviors of browsers implementation when inserting
 
-  * content via copy & paste contentEditable
 
-  *
 
-  * @author Christopher Blum
 
-  */
 
- wysihtml5.quirks.cleanPastedHTML = (function() {
 
-   var styleToRegex = function (styleStr) {
 
-     var trimmedStr = wysihtml5.lang.string(styleStr).trim(),
 
-         escapedStr = trimmedStr.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
 
-     return new RegExp("^((?!^" + escapedStr + "$).)*$", "i");
 
-   };
 
-   var extendRulesWithStyleExceptions = function (rules, exceptStyles) {
 
-     var newRules = wysihtml5.lang.object(rules).clone(true),
 
-         tag, style;
 
-     for (tag in newRules.tags) {
 
-       if (newRules.tags.hasOwnProperty(tag)) {
 
-         if (newRules.tags[tag].keep_styles) {
 
-           for (style in newRules.tags[tag].keep_styles) {
 
-             if (newRules.tags[tag].keep_styles.hasOwnProperty(style)) {
 
-               if (exceptStyles[style]) {
 
-                 newRules.tags[tag].keep_styles[style] = styleToRegex(exceptStyles[style]);
 
-               }
 
-             }
 
-           }
 
-         }
 
-       }
 
-     }
 
-     return newRules;
 
-   };
 
-   var pickRuleset = function(ruleset, html) {
 
-     var pickedSet, defaultSet;
 
-     if (!ruleset) {
 
-       return null;
 
-     }
 
-     for (var i = 0, max = ruleset.length; i < max; i++) {
 
-       if (!ruleset[i].condition) {
 
-         defaultSet = ruleset[i].set;
 
-       }
 
-       if (ruleset[i].condition && ruleset[i].condition.test(html)) {
 
-         return ruleset[i].set;
 
-       }
 
-     }
 
-     return defaultSet;
 
-   };
 
-   return function(html, options) {
 
-     var exceptStyles = {
 
-           'color': wysihtml5.dom.getStyle("color").from(options.referenceNode),
 
-           'fontSize': wysihtml5.dom.getStyle("font-size").from(options.referenceNode)
 
-         },
 
-         rules = extendRulesWithStyleExceptions(pickRuleset(options.rules, html) || {}, exceptStyles),
 
-         newHtml;
 
-     newHtml = wysihtml5.dom.parse(html, {
 
-       "rules": rules,
 
-       "cleanUp": true, // <span> elements, empty or without attributes, should be removed/replaced with their content
 
-       "context": options.referenceNode.ownerDocument,
 
-       "uneditableClass": options.uneditableClass,
 
-       "clearInternals" : true, // don't paste temprorary selection and other markings
 
-       "unjoinNbsps" : true
 
-     });
 
-     return newHtml;
 
-   };
 
- })();;/**
 
-  * IE and Opera leave an empty paragraph in the contentEditable element after clearing it
 
-  *
 
-  * @param {Object} contentEditableElement The contentEditable element to observe for clearing events
 
-  * @exaple
 
-  *    wysihtml5.quirks.ensureProperClearing(myContentEditableElement);
 
-  */
 
- wysihtml5.quirks.ensureProperClearing = (function() {
 
-   var clearIfNecessary = function() {
 
-     var element = this;
 
-     setTimeout(function() {
 
-       var innerHTML = element.innerHTML.toLowerCase();
 
-       if (innerHTML == "<p> </p>" ||
 
-           innerHTML == "<p> </p><p> </p>") {
 
-         element.innerHTML = "";
 
-       }
 
-     }, 0);
 
-   };
 
-   return function(composer) {
 
-     wysihtml5.dom.observe(composer.element, ["cut", "keydown"], clearIfNecessary);
 
-   };
 
- })();
 
- ;// See https://bugzilla.mozilla.org/show_bug.cgi?id=664398
 
- //
 
- // In Firefox this:
 
- //      var d = document.createElement("div");
 
- //      d.innerHTML ='<a href="~"></a>';
 
- //      d.innerHTML;
 
- // will result in:
 
- //      <a href="%7E"></a>
 
- // which is wrong
 
- (function(wysihtml5) {
 
-   var TILDE_ESCAPED = "%7E";
 
-   wysihtml5.quirks.getCorrectInnerHTML = function(element) {
 
-     var innerHTML = element.innerHTML;
 
-     if (innerHTML.indexOf(TILDE_ESCAPED) === -1) {
 
-       return innerHTML;
 
-     }
 
-     var elementsWithTilde = element.querySelectorAll("[href*='~'], [src*='~']"),
 
-         url,
 
-         urlToSearch,
 
-         length,
 
-         i;
 
-     for (i=0, length=elementsWithTilde.length; i<length; i++) {
 
-       url         = elementsWithTilde[i].href || elementsWithTilde[i].src;
 
-       urlToSearch = wysihtml5.lang.string(url).replace("~").by(TILDE_ESCAPED);
 
-       innerHTML   = wysihtml5.lang.string(innerHTML).replace(urlToSearch).by(url);
 
-     }
 
-     return innerHTML;
 
-   };
 
- })(wysihtml5);
 
- ;/**
 
-  * Force rerendering of a given element
 
-  * Needed to fix display misbehaviors of IE
 
-  *
 
-  * @param {Element} element The element object which needs to be rerendered
 
-  * @example
 
-  *    wysihtml5.quirks.redraw(document.body);
 
-  */
 
- (function(wysihtml5) {
 
-   var CLASS_NAME = "wysihtml5-quirks-redraw";
 
-   wysihtml5.quirks.redraw = function(element) {
 
-     wysihtml5.dom.addClass(element, CLASS_NAME);
 
-     wysihtml5.dom.removeClass(element, CLASS_NAME);
 
-     // Following hack is needed for firefox to make sure that image resize handles are properly removed
 
-     try {
 
-       var doc = element.ownerDocument;
 
-       doc.execCommand("italic", false, null);
 
-       doc.execCommand("italic", false, null);
 
-     } catch(e) {}
 
-   };
 
- })(wysihtml5);
 
- ;wysihtml5.quirks.tableCellsSelection = function(editable, editor) {
 
-     var dom = wysihtml5.dom,
 
-         select = {
 
-             table: null,
 
-             start: null,
 
-             end: null,
 
-             cells: null,
 
-             select: selectCells
 
-         },
 
-         selection_class = "wysiwyg-tmp-selected-cell",
 
-         moveHandler = null,
 
-         upHandler = null;
 
-     function init () {
 
-         dom.observe(editable, "mousedown", function(event) {
 
-           var target = wysihtml5.dom.getParentElement(event.target, { nodeName: ["TD", "TH"] });
 
-           if (target) {
 
-               handleSelectionMousedown(target);
 
-           }
 
-         });
 
-         return select;
 
-     }
 
-     function handleSelectionMousedown (target) {
 
-       select.start = target;
 
-       select.end = target;
 
-       select.cells = [target];
 
-       select.table = dom.getParentElement(select.start, { nodeName: ["TABLE"] });
 
-       if (select.table) {
 
-         removeCellSelections();
 
-         dom.addClass(target, selection_class);
 
-         moveHandler = dom.observe(editable, "mousemove", handleMouseMove);
 
-         upHandler = dom.observe(editable, "mouseup", handleMouseUp);
 
-         editor.fire("tableselectstart").fire("tableselectstart:composer");
 
-       }
 
-     }
 
-     // remove all selection classes
 
-     function removeCellSelections () {
 
-         if (editable) {
 
-             var selectedCells = editable.querySelectorAll('.' + selection_class);
 
-             if (selectedCells.length > 0) {
 
-               for (var i = 0; i < selectedCells.length; i++) {
 
-                   dom.removeClass(selectedCells[i], selection_class);
 
-               }
 
-             }
 
-         }
 
-     }
 
-     function addSelections (cells) {
 
-       for (var i = 0; i < cells.length; i++) {
 
-         dom.addClass(cells[i], selection_class);
 
-       }
 
-     }
 
-     function handleMouseMove (event) {
 
-       var curTable = null,
 
-           cell = dom.getParentElement(event.target, { nodeName: ["TD","TH"] }),
 
-           oldEnd;
 
-       if (cell && select.table && select.start) {
 
-         curTable =  dom.getParentElement(cell, { nodeName: ["TABLE"] });
 
-         if (curTable && curTable === select.table) {
 
-           removeCellSelections();
 
-           oldEnd = select.end;
 
-           select.end = cell;
 
-           select.cells = dom.table.getCellsBetween(select.start, cell);
 
-           if (select.cells.length > 1) {
 
-             editor.composer.selection.deselect();
 
-           }
 
-           addSelections(select.cells);
 
-           if (select.end !== oldEnd) {
 
-             editor.fire("tableselectchange").fire("tableselectchange:composer");
 
-           }
 
-         }
 
-       }
 
-     }
 
-     function handleMouseUp (event) {
 
-       moveHandler.stop();
 
-       upHandler.stop();
 
-       editor.fire("tableselect").fire("tableselect:composer");
 
-       setTimeout(function() {
 
-         bindSideclick();
 
-       },0);
 
-     }
 
-     function bindSideclick () {
 
-         var sideClickHandler = dom.observe(editable.ownerDocument, "click", function(event) {
 
-           sideClickHandler.stop();
 
-           if (dom.getParentElement(event.target, { nodeName: ["TABLE"] }) != select.table) {
 
-               removeCellSelections();
 
-               select.table = null;
 
-               select.start = null;
 
-               select.end = null;
 
-               editor.fire("tableunselect").fire("tableunselect:composer");
 
-           }
 
-         });
 
-     }
 
-     function selectCells (start, end) {
 
-         select.start = start;
 
-         select.end = end;
 
-         select.table = dom.getParentElement(select.start, { nodeName: ["TABLE"] });
 
-         selectedCells = dom.table.getCellsBetween(select.start, select.end);
 
-         addSelections(selectedCells);
 
-         bindSideclick();
 
-         editor.fire("tableselect").fire("tableselect:composer");
 
-     }
 
-     return init();
 
- };
 
- ;(function(wysihtml5) {
 
-   var RGBA_REGEX     = /^rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*([\d\.]+)\s*\)/i,
 
-       RGB_REGEX      = /^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/i,
 
-       HEX6_REGEX     = /^#([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])/i,
 
-       HEX3_REGEX     = /^#([0-9a-f])([0-9a-f])([0-9a-f])/i;
 
-   var param_REGX = function (p) {
 
-     return new RegExp("(^|\\s|;)" + p + "\\s*:\\s*[^;$]+" , "gi");
 
-   };
 
-   wysihtml5.quirks.styleParser = {
 
-     parseColor: function(stylesStr, paramName) {
 
-       var paramRegex = param_REGX(paramName),
 
-           params = stylesStr.match(paramRegex),
 
-           radix = 10,
 
-           str, colorMatch;
 
-       if (params) {
 
-         for (var i = params.length; i--;) {
 
-           params[i] = wysihtml5.lang.string(params[i].split(':')[1]).trim();
 
-         }
 
-         str = params[params.length-1];
 
-         if (RGBA_REGEX.test(str)) {
 
-           colorMatch = str.match(RGBA_REGEX);
 
-         } else if (RGB_REGEX.test(str)) {
 
-           colorMatch = str.match(RGB_REGEX);
 
-         } else if (HEX6_REGEX.test(str)) {
 
-           colorMatch = str.match(HEX6_REGEX);
 
-           radix = 16;
 
-         } else if (HEX3_REGEX.test(str)) {
 
-           colorMatch = str.match(HEX3_REGEX);
 
-           colorMatch.shift();
 
-           colorMatch.push(1);
 
-           return wysihtml5.lang.array(colorMatch).map(function(d, idx) {
 
-             return (idx < 3) ? (parseInt(d, 16) * 16) + parseInt(d, 16): parseFloat(d);
 
-           });
 
-         }
 
-         if (colorMatch) {
 
-           colorMatch.shift();
 
-           if (!colorMatch[3]) {
 
-             colorMatch.push(1);
 
-           }
 
-           return wysihtml5.lang.array(colorMatch).map(function(d, idx) {
 
-             return (idx < 3) ? parseInt(d, radix): parseFloat(d);
 
-           });
 
-         }
 
-       }
 
-       return false;
 
-     },
 
-     unparseColor: function(val, props) {
 
-       if (props) {
 
-         if (props == "hex") {
 
-           return (val[0].toString(16).toUpperCase()) + (val[1].toString(16).toUpperCase()) + (val[2].toString(16).toUpperCase());
 
-         } else if (props == "hash") {
 
-           return "#" + (val[0].toString(16).toUpperCase()) + (val[1].toString(16).toUpperCase()) + (val[2].toString(16).toUpperCase());
 
-         } else if (props == "rgb") {
 
-           return "rgb(" + val[0] + "," + val[1] + "," + val[2] + ")";
 
-         } else if (props == "rgba") {
 
-           return "rgba(" + val[0] + "," + val[1] + "," + val[2] + "," + val[3] + ")";
 
-         } else if (props == "csv") {
 
-           return  val[0] + "," + val[1] + "," + val[2] + "," + val[3];
 
-         }
 
-       }
 
-       if (val[3] && val[3] !== 1) {
 
-         return "rgba(" + val[0] + "," + val[1] + "," + val[2] + "," + val[3] + ")";
 
-       } else {
 
-         return "rgb(" + val[0] + "," + val[1] + "," + val[2] + ")";
 
-       }
 
-     },
 
-     parseFontSize: function(stylesStr) {
 
-       var params = stylesStr.match(param_REGX('font-size'));
 
-       if (params) {
 
-         return wysihtml5.lang.string(params[params.length - 1].split(':')[1]).trim();
 
-       }
 
-       return false;
 
-     }
 
-   };
 
- })(wysihtml5);
 
- ;/**
 
-  * Selection API
 
-  *
 
-  * @example
 
-  *    var selection = new wysihtml5.Selection(editor);
 
-  */
 
- (function(wysihtml5) {
 
-   var dom = wysihtml5.dom;
 
-   function _getCumulativeOffsetTop(element) {
 
-     var top = 0;
 
-     if (element.parentNode) {
 
-       do {
 
-         top += element.offsetTop || 0;
 
-         element = element.offsetParent;
 
-       } while (element);
 
-     }
 
-     return top;
 
-   }
 
-   // Provides the depth of ``descendant`` relative to ``ancestor``
 
-   function getDepth(ancestor, descendant) {
 
-       var ret = 0;
 
-       while (descendant !== ancestor) {
 
-           ret++;
 
-           descendant = descendant.parentNode;
 
-           if (!descendant)
 
-               throw new Error("not a descendant of ancestor!");
 
-       }
 
-       return ret;
 
-   }
 
-   // Should fix the obtained ranges that cannot surrond contents normally to apply changes upon
 
-   // Being considerate to firefox that sets range start start out of span and end inside on doubleclick initiated selection
 
-   function expandRangeToSurround(range) {
 
-       if (range.canSurroundContents()) return;
 
-       var common = range.commonAncestorContainer,
 
-           start_depth = getDepth(common, range.startContainer),
 
-           end_depth = getDepth(common, range.endContainer);
 
-       while(!range.canSurroundContents()) {
 
-         // In the following branches, we cannot just decrement the depth variables because the setStartBefore/setEndAfter may move the start or end of the range more than one level relative to ``common``. So we need to recompute the depth.
 
-         if (start_depth > end_depth) {
 
-             range.setStartBefore(range.startContainer);
 
-             start_depth = getDepth(common, range.startContainer);
 
-         }
 
-         else {
 
-             range.setEndAfter(range.endContainer);
 
-             end_depth = getDepth(common, range.endContainer);
 
-         }
 
-       }
 
-   }
 
-   wysihtml5.Selection = Base.extend(
 
-     /** @scope wysihtml5.Selection.prototype */ {
 
-     constructor: function(editor, contain, unselectableClass) {
 
-       // Make sure that our external range library is initialized
 
-       window.rangy.init();
 
-       this.editor   = editor;
 
-       this.composer = editor.composer;
 
-       this.doc      = this.composer.doc;
 
-       this.contain = contain;
 
-       this.unselectableClass = unselectableClass || false;
 
-     },
 
-     /**
 
-      * Get the current selection as a bookmark to be able to later restore it
 
-      *
 
-      * @return {Object} An object that represents the current selection
 
-      */
 
-     getBookmark: function() {
 
-       var range = this.getRange();
 
-       if (range) expandRangeToSurround(range);
 
-       return range && range.cloneRange();
 
-     },
 
-     /**
 
-      * Restore a selection retrieved via wysihtml5.Selection.prototype.getBookmark
 
-      *
 
-      * @param {Object} bookmark An object that represents the current selection
 
-      */
 
-     setBookmark: function(bookmark) {
 
-       if (!bookmark) {
 
-         return;
 
-       }
 
-       this.setSelection(bookmark);
 
-     },
 
-     /**
 
-      * Set the caret in front of the given node
 
-      *
 
-      * @param {Object} node The element or text node where to position the caret in front of
 
-      * @example
 
-      *    selection.setBefore(myElement);
 
-      */
 
-     setBefore: function(node) {
 
-       var range = rangy.createRange(this.doc);
 
-       range.setStartBefore(node);
 
-       range.setEndBefore(node);
 
-       return this.setSelection(range);
 
-     },
 
-     /**
 
-      * Set the caret after the given node
 
-      *
 
-      * @param {Object} node The element or text node where to position the caret in front of
 
-      * @example
 
-      *    selection.setBefore(myElement);
 
-      */
 
-     setAfter: function(node) {
 
-       var range = rangy.createRange(this.doc);
 
-       range.setStartAfter(node);
 
-       range.setEndAfter(node);
 
-       return this.setSelection(range);
 
-     },
 
-     /**
 
-      * Ability to select/mark nodes
 
-      *
 
-      * @param {Element} node The node/element to select
 
-      * @example
 
-      *    selection.selectNode(document.getElementById("my-image"));
 
-      */
 
-     selectNode: function(node, avoidInvisibleSpace) {
 
-       var range           = rangy.createRange(this.doc),
 
-           isElement       = node.nodeType === wysihtml5.ELEMENT_NODE,
 
-           canHaveHTML     = "canHaveHTML" in node ? node.canHaveHTML : (node.nodeName !== "IMG"),
 
-           content         = isElement ? node.innerHTML : node.data,
 
-           isEmpty         = (content === "" || content === wysihtml5.INVISIBLE_SPACE),
 
-           displayStyle    = dom.getStyle("display").from(node),
 
-           isBlockElement  = (displayStyle === "block" || displayStyle === "list-item");
 
-       if (isEmpty && isElement && canHaveHTML && !avoidInvisibleSpace) {
 
-         // Make sure that caret is visible in node by inserting a zero width no breaking space
 
-         try { node.innerHTML = wysihtml5.INVISIBLE_SPACE; } catch(e) {}
 
-       }
 
-       if (canHaveHTML) {
 
-         range.selectNodeContents(node);
 
-       } else {
 
-         range.selectNode(node);
 
-       }
 
-       if (canHaveHTML && isEmpty && isElement) {
 
-         range.collapse(isBlockElement);
 
-       } else if (canHaveHTML && isEmpty) {
 
-         range.setStartAfter(node);
 
-         range.setEndAfter(node);
 
-       }
 
-       this.setSelection(range);
 
-     },
 
-     /**
 
-      * Get the node which contains the selection
 
-      *
 
-      * @param {Boolean} [controlRange] (only IE) Whether it should return the selected ControlRange element when the selection type is a "ControlRange"
 
-      * @return {Object} The node that contains the caret
 
-      * @example
 
-      *    var nodeThatContainsCaret = selection.getSelectedNode();
 
-      */
 
-     getSelectedNode: function(controlRange) {
 
-       var selection,
 
-           range;
 
-       if (controlRange && this.doc.selection && this.doc.selection.type === "Control") {
 
-         range = this.doc.selection.createRange();
 
-         if (range && range.length) {
 
-           return range.item(0);
 
-         }
 
-       }
 
-       selection = this.getSelection(this.doc);
 
-       if (selection.focusNode === selection.anchorNode) {
 
-         return selection.focusNode;
 
-       } else {
 
-         range = this.getRange(this.doc);
 
-         return range ? range.commonAncestorContainer : this.doc.body;
 
-       }
 
-     },
 
-     fixSelBorders: function() {
 
-       var range = this.getRange();
 
-       expandRangeToSurround(range);
 
-       this.setSelection(range);
 
-     },
 
-     getSelectedOwnNodes: function(controlRange) {
 
-       var selection,
 
-           ranges = this.getOwnRanges(),
 
-           ownNodes = [];
 
-       for (var i = 0, maxi = ranges.length; i < maxi; i++) {
 
-           ownNodes.push(ranges[i].commonAncestorContainer || this.doc.body);
 
-       }
 
-       return ownNodes;
 
-     },
 
-     findNodesInSelection: function(nodeTypes) {
 
-       var ranges = this.getOwnRanges(),
 
-           nodes = [], curNodes;
 
-       for (var i = 0, maxi = ranges.length; i < maxi; i++) {
 
-         curNodes = ranges[i].getNodes([1], function(node) {
 
-             return wysihtml5.lang.array(nodeTypes).contains(node.nodeName);
 
-         });
 
-         nodes = nodes.concat(curNodes);
 
-       }
 
-       return nodes;
 
-     },
 
-     containsUneditable: function() {
 
-       var uneditables = this.getOwnUneditables(),
 
-           selection = this.getSelection();
 
-       for (var i = 0, maxi = uneditables.length; i < maxi; i++) {
 
-         if (selection.containsNode(uneditables[i])) {
 
-           return true;
 
-         }
 
-       }
 
-       return false;
 
-     },
 
-     deleteContents: function()  {
 
-       var ranges = this.getOwnRanges();
 
-       for (var i = ranges.length; i--;) {
 
-         ranges[i].deleteContents();
 
-       }
 
-       this.setSelection(ranges[0]);
 
-     },
 
-     getPreviousNode: function(node, ignoreEmpty) {
 
-       if (!node) {
 
-         var selection = this.getSelection();
 
-         node = selection.anchorNode;
 
-       }
 
-       if (node === this.contain) {
 
-           return false;
 
-       }
 
-       var ret = node.previousSibling,
 
-           parent;
 
-       if (ret === this.contain) {
 
-           return false;
 
-       }
 
-       if (ret && ret.nodeType !== 3 && ret.nodeType !== 1) {
 
-          // do not count comments and other node types
 
-          ret = this.getPreviousNode(ret, ignoreEmpty);
 
-       } else if (ret && ret.nodeType === 3 && (/^\s*$/).test(ret.textContent)) {
 
-         // do not count empty textnodes as previus nodes
 
-         ret = this.getPreviousNode(ret, ignoreEmpty);
 
-       } else if (ignoreEmpty && ret && ret.nodeType === 1 && !wysihtml5.lang.array(["BR", "HR", "IMG"]).contains(ret.nodeName) && (/^[\s]*$/).test(ret.innerHTML)) {
 
-         // Do not count empty nodes if param set.
 
-         // Contenteditable tends to bypass and delete these silently when deleting with caret
 
-         ret = this.getPreviousNode(ret, ignoreEmpty);
 
-       } else if (!ret && node !== this.contain) {
 
-         parent = node.parentNode;
 
-         if (parent !== this.contain) {
 
-             ret = this.getPreviousNode(parent, ignoreEmpty);
 
-         }
 
-       }
 
-       return (ret !== this.contain) ? ret : false;
 
-     },
 
-     getSelectionParentsByTag: function(tagName) {
 
-       var nodes = this.getSelectedOwnNodes(),
 
-           curEl, parents = [];
 
-       for (var i = 0, maxi = nodes.length; i < maxi; i++) {
 
-         curEl = (nodes[i].nodeName &&  nodes[i].nodeName === 'LI') ? nodes[i] : wysihtml5.dom.getParentElement(nodes[i], { nodeName: ['LI']}, false, this.contain);
 
-         if (curEl) {
 
-           parents.push(curEl);
 
-         }
 
-       }
 
-       return (parents.length) ? parents : null;
 
-     },
 
-     getRangeToNodeEnd: function() {
 
-       if (this.isCollapsed()) {
 
-         var range = this.getRange(),
 
-             sNode = range.startContainer,
 
-             pos = range.startOffset,
 
-             lastR = rangy.createRange(this.doc);
 
-         lastR.selectNodeContents(sNode);
 
-         lastR.setStart(sNode, pos);
 
-         return lastR;
 
-       }
 
-     },
 
-     caretIsLastInSelection: function() {
 
-       var r = rangy.createRange(this.doc),
 
-           s = this.getSelection(),
 
-           endc = this.getRangeToNodeEnd().cloneContents(),
 
-           endtxt = endc.textContent;
 
-       return (/^\s*$/).test(endtxt);
 
-     },
 
-     caretIsFirstInSelection: function() {
 
-       var r = rangy.createRange(this.doc),
 
-           s = this.getSelection(),
 
-           range = this.getRange(),
 
-           startNode = range.startContainer;
 
-       
 
-       if (startNode.nodeType === wysihtml5.TEXT_NODE) {
 
-         return this.isCollapsed() && (startNode.nodeType === wysihtml5.TEXT_NODE && (/^\s*$/).test(startNode.data.substr(0,range.startOffset)));
 
-       } else {
 
-         r.selectNodeContents(this.getRange().commonAncestorContainer);
 
-         r.collapse(true);
 
-         return (this.isCollapsed() && (r.startContainer === s.anchorNode || r.endContainer === s.anchorNode) && r.startOffset === s.anchorOffset);
 
-       }
 
-     },
 
-     caretIsInTheBeginnig: function(ofNode) {
 
-         var selection = this.getSelection(),
 
-             node = selection.anchorNode,
 
-             offset = selection.anchorOffset;
 
-         if (ofNode) {
 
-           return (offset === 0 && (node.nodeName && node.nodeName === ofNode.toUpperCase() || wysihtml5.dom.getParentElement(node.parentNode, { nodeName: ofNode }, 1)));
 
-         } else {
 
-           return (offset === 0 && !this.getPreviousNode(node, true));
 
-         }
 
-     },
 
-     caretIsBeforeUneditable: function() {
 
-       var selection = this.getSelection(),
 
-           node = selection.anchorNode,
 
-           offset = selection.anchorOffset;
 
-       if (offset === 0) {
 
-         var prevNode = this.getPreviousNode(node, true);
 
-         if (prevNode) {
 
-           var uneditables = this.getOwnUneditables();
 
-           for (var i = 0, maxi = uneditables.length; i < maxi; i++) {
 
-             if (prevNode === uneditables[i]) {
 
-               return uneditables[i];
 
-             }
 
-           }
 
-         }
 
-       }
 
-       return false;
 
-     },
 
-     // TODO: Figure out a method from following 2 that would work universally
 
-     executeAndRestoreRangy: function(method, restoreScrollPosition) {
 
-       var win = this.doc.defaultView || this.doc.parentWindow,
 
-           sel = rangy.saveSelection(win);
 
-       if (!sel) {
 
-         method();
 
-       } else {
 
-         try {
 
-           method();
 
-         } catch(e) {
 
-           setTimeout(function() { throw e; }, 0);
 
-         }
 
-       }
 
-       rangy.restoreSelection(sel);
 
-     },
 
-     // TODO: has problems in chrome 12. investigate block level and uneditable area inbetween
 
-     executeAndRestore: function(method, restoreScrollPosition) {
 
-       var body                  = this.doc.body,
 
-           oldScrollTop          = restoreScrollPosition && body.scrollTop,
 
-           oldScrollLeft         = restoreScrollPosition && body.scrollLeft,
 
-           className             = "_wysihtml5-temp-placeholder",
 
-           placeholderHtml       = '<span class="' + className + '">' + wysihtml5.INVISIBLE_SPACE + '</span>',
 
-           range                 = this.getRange(true),
 
-           caretPlaceholder,
 
-           newCaretPlaceholder,
 
-           nextSibling, prevSibling,
 
-           node, node2, range2,
 
-           newRange;
 
-       // Nothing selected, execute and say goodbye
 
-       if (!range) {
 
-         method(body, body);
 
-         return;
 
-       }
 
-       if (!range.collapsed) {
 
-         range2 = range.cloneRange();
 
-         node2 = range2.createContextualFragment(placeholderHtml);
 
-         range2.collapse(false);
 
-         range2.insertNode(node2);
 
-         range2.detach();
 
-       }
 
-       node = range.createContextualFragment(placeholderHtml);
 
-       range.insertNode(node);
 
-       if (node2) {
 
-         caretPlaceholder = this.contain.querySelectorAll("." + className);
 
-         range.setStartBefore(caretPlaceholder[0]);
 
-         range.setEndAfter(caretPlaceholder[caretPlaceholder.length -1]);
 
-       }
 
-       this.setSelection(range);
 
-       // Make sure that a potential error doesn't cause our placeholder element to be left as a placeholder
 
-       try {
 
-         method(range.startContainer, range.endContainer);
 
-       } catch(e) {
 
-         setTimeout(function() { throw e; }, 0);
 
-       }
 
-       caretPlaceholder = this.contain.querySelectorAll("." + className);
 
-       if (caretPlaceholder && caretPlaceholder.length) {
 
-         newRange = rangy.createRange(this.doc);
 
-         nextSibling = caretPlaceholder[0].nextSibling;
 
-         if (caretPlaceholder.length > 1) {
 
-           prevSibling = caretPlaceholder[caretPlaceholder.length -1].previousSibling;
 
-         }
 
-         if (prevSibling && nextSibling) {
 
-           newRange.setStartBefore(nextSibling);
 
-           newRange.setEndAfter(prevSibling);
 
-         } else {
 
-           newCaretPlaceholder = this.doc.createTextNode(wysihtml5.INVISIBLE_SPACE);
 
-           dom.insert(newCaretPlaceholder).after(caretPlaceholder[0]);
 
-           newRange.setStartBefore(newCaretPlaceholder);
 
-           newRange.setEndAfter(newCaretPlaceholder);
 
-         }
 
-         this.setSelection(newRange);
 
-         for (var i = caretPlaceholder.length; i--;) {
 
-          caretPlaceholder[i].parentNode.removeChild(caretPlaceholder[i]);
 
-         }
 
-       } else {
 
-         // fallback for when all hell breaks loose
 
-         this.contain.focus();
 
-       }
 
-       if (restoreScrollPosition) {
 
-         body.scrollTop  = oldScrollTop;
 
-         body.scrollLeft = oldScrollLeft;
 
-       }
 
-       // Remove it again, just to make sure that the placeholder is definitely out of the dom tree
 
-       try {
 
-         caretPlaceholder.parentNode.removeChild(caretPlaceholder);
 
-       } catch(e2) {}
 
-     },
 
-     set: function(node, offset) {
 
-       var newRange = rangy.createRange(this.doc);
 
-       newRange.setStart(node, offset || 0);
 
-       this.setSelection(newRange);
 
-     },
 
-     /**
 
-      * Insert html at the caret position and move the cursor after the inserted html
 
-      *
 
-      * @param {String} html HTML string to insert
 
-      * @example
 
-      *    selection.insertHTML("<p>foobar</p>");
 
-      */
 
-     insertHTML: function(html) {
 
-       var range     = rangy.createRange(this.doc),
 
-           node = this.doc.createElement('DIV'),
 
-           fragment = this.doc.createDocumentFragment(),
 
-           lastChild;
 
-       node.innerHTML = html;
 
-       lastChild = node.lastChild;
 
-       while (node.firstChild) {
 
-         fragment.appendChild(node.firstChild);
 
-       }
 
-       this.insertNode(fragment);
 
-       if (lastChild) {
 
-         this.setAfter(lastChild);
 
-       }
 
-     },
 
-     /**
 
-      * Insert a node at the caret position and move the cursor behind it
 
-      *
 
-      * @param {Object} node HTML string to insert
 
-      * @example
 
-      *    selection.insertNode(document.createTextNode("foobar"));
 
-      */
 
-     insertNode: function(node) {
 
-       var range = this.getRange();
 
-       if (range) {
 
-         range.insertNode(node);
 
-       }
 
-     },
 
-     /**
 
-      * Wraps current selection with the given node
 
-      *
 
-      * @param {Object} node The node to surround the selected elements with
 
-      */
 
-     surround: function(nodeOptions) {
 
-       var ranges = this.getOwnRanges(),
 
-           node, nodes = [];
 
-       if (ranges.length == 0) {
 
-         return nodes;
 
-       }
 
-       for (var i = ranges.length; i--;) {
 
-         node = this.doc.createElement(nodeOptions.nodeName);
 
-         nodes.push(node);
 
-         if (nodeOptions.className) {
 
-           node.className = nodeOptions.className;
 
-         }
 
-         if (nodeOptions.cssStyle) {
 
-           node.setAttribute('style', nodeOptions.cssStyle);
 
-         }
 
-         try {
 
-           // This only works when the range boundaries are not overlapping other elements
 
-           ranges[i].surroundContents(node);
 
-           this.selectNode(node);
 
-         } catch(e) {
 
-           // fallback
 
-           node.appendChild(ranges[i].extractContents());
 
-           ranges[i].insertNode(node);
 
-         }
 
-       }
 
-       return nodes;
 
-     },
 
-     deblockAndSurround: function(nodeOptions) {
 
-       var tempElement = this.doc.createElement('div'),
 
-           range = rangy.createRange(this.doc),
 
-           tempDivElements,
 
-           tempElements,
 
-           firstChild;
 
-       tempElement.className = nodeOptions.className;
 
-       this.composer.commands.exec("formatBlock", nodeOptions.nodeName, nodeOptions.className);
 
-       tempDivElements = this.contain.querySelectorAll("." + nodeOptions.className);
 
-       if (tempDivElements[0]) {
 
-         tempDivElements[0].parentNode.insertBefore(tempElement, tempDivElements[0]);
 
-         range.setStartBefore(tempDivElements[0]);
 
-         range.setEndAfter(tempDivElements[tempDivElements.length - 1]);
 
-         tempElements = range.extractContents();
 
-         while (tempElements.firstChild) {
 
-           firstChild = tempElements.firstChild;
 
-           if (firstChild.nodeType == 1 && wysihtml5.dom.hasClass(firstChild, nodeOptions.className)) {
 
-             while (firstChild.firstChild) {
 
-               tempElement.appendChild(firstChild.firstChild);
 
-             }
 
-             if (firstChild.nodeName !== "BR") { tempElement.appendChild(this.doc.createElement('br')); }
 
-             tempElements.removeChild(firstChild);
 
-           } else {
 
-             tempElement.appendChild(firstChild);
 
-           }
 
-         }
 
-       } else {
 
-         tempElement = null;
 
-       }
 
-       return tempElement;
 
-     },
 
-     /**
 
-      * Scroll the current caret position into the view
 
-      * FIXME: This is a bit hacky, there might be a smarter way of doing this
 
-      *
 
-      * @example
 
-      *    selection.scrollIntoView();
 
-      */
 
-     scrollIntoView: function() {
 
-       var doc           = this.doc,
 
-           tolerance     = 5, // px
 
-           hasScrollBars = doc.documentElement.scrollHeight > doc.documentElement.offsetHeight,
 
-           tempElement   = doc._wysihtml5ScrollIntoViewElement = doc._wysihtml5ScrollIntoViewElement || (function() {
 
-             var element = doc.createElement("span");
 
-             // The element needs content in order to be able to calculate it's position properly
 
-             element.innerHTML = wysihtml5.INVISIBLE_SPACE;
 
-             return element;
 
-           })(),
 
-           offsetTop;
 
-       if (hasScrollBars) {
 
-         this.insertNode(tempElement);
 
-         offsetTop = _getCumulativeOffsetTop(tempElement);
 
-         tempElement.parentNode.removeChild(tempElement);
 
-         if (offsetTop >= (doc.body.scrollTop + doc.documentElement.offsetHeight - tolerance)) {
 
-           doc.body.scrollTop = offsetTop;
 
-         }
 
-       }
 
-     },
 
-     /**
 
-      * Select line where the caret is in
 
-      */
 
-     selectLine: function() {
 
-       if (wysihtml5.browser.supportsSelectionModify()) {
 
-         this._selectLine_W3C();
 
-       } else if (this.doc.selection) {
 
-         this._selectLine_MSIE();
 
-       }
 
-     },
 
-     /**
 
-      * See https://developer.mozilla.org/en/DOM/Selection/modify
 
-      */
 
-     _selectLine_W3C: function() {
 
-       var win       = this.doc.defaultView,
 
-           selection = win.getSelection();
 
-       selection.modify("move", "left", "lineboundary");
 
-       selection.modify("extend", "right", "lineboundary");
 
-     },
 
-     _selectLine_MSIE: function() {
 
-       var range       = this.doc.selection.createRange(),
 
-           rangeTop    = range.boundingTop,
 
-           scrollWidth = this.doc.body.scrollWidth,
 
-           rangeBottom,
 
-           rangeEnd,
 
-           measureNode,
 
-           i,
 
-           j;
 
-       if (!range.moveToPoint) {
 
-         return;
 
-       }
 
-       if (rangeTop === 0) {
 
-         // Don't know why, but when the selection ends at the end of a line
 
-         // range.boundingTop is 0
 
-         measureNode = this.doc.createElement("span");
 
-         this.insertNode(measureNode);
 
-         rangeTop = measureNode.offsetTop;
 
-         measureNode.parentNode.removeChild(measureNode);
 
-       }
 
-       rangeTop += 1;
 
-       for (i=-10; i<scrollWidth; i+=2) {
 
-         try {
 
-           range.moveToPoint(i, rangeTop);
 
-           break;
 
-         } catch(e1) {}
 
-       }
 
-       // Investigate the following in order to handle multi line selections
 
-       // rangeBottom = rangeTop + (rangeHeight ? (rangeHeight - 1) : 0);
 
-       rangeBottom = rangeTop;
 
-       rangeEnd = this.doc.selection.createRange();
 
-       for (j=scrollWidth; j>=0; j--) {
 
-         try {
 
-           rangeEnd.moveToPoint(j, rangeBottom);
 
-           break;
 
-         } catch(e2) {}
 
-       }
 
-       range.setEndPoint("EndToEnd", rangeEnd);
 
-       range.select();
 
-     },
 
-     getText: function() {
 
-       var selection = this.getSelection();
 
-       return selection ? selection.toString() : "";
 
-     },
 
-     getNodes: function(nodeType, filter) {
 
-       var range = this.getRange();
 
-       if (range) {
 
-         return range.getNodes([nodeType], filter);
 
-       } else {
 
-         return [];
 
-       }
 
-     },
 
-     fixRangeOverflow: function(range) {
 
-       if (this.contain && this.contain.firstChild && range) {
 
-         var containment = range.compareNode(this.contain);
 
-         if (containment !== 2) {
 
-           if (containment === 1) {
 
-             range.setStartBefore(this.contain.firstChild);
 
-           }
 
-           if (containment === 0) {
 
-             range.setEndAfter(this.contain.lastChild);
 
-           }
 
-           if (containment === 3) {
 
-             range.setStartBefore(this.contain.firstChild);
 
-             range.setEndAfter(this.contain.lastChild);
 
-           }
 
-         } else if (this._detectInlineRangeProblems(range)) {
 
-           var previousElementSibling = range.endContainer.previousElementSibling;
 
-           if (previousElementSibling) {
 
-             range.setEnd(previousElementSibling, this._endOffsetForNode(previousElementSibling));
 
-           }
 
-         }
 
-       }
 
-     },
 
-     _endOffsetForNode: function(node) {
 
-       var range = document.createRange();
 
-       range.selectNodeContents(node);
 
-       return range.endOffset;
 
-     },
 
-     _detectInlineRangeProblems: function(range) {
 
-       var position = dom.compareDocumentPosition(range.startContainer, range.endContainer);
 
-       return (
 
-         range.endOffset == 0 &&
 
-         position & 4 //Node.DOCUMENT_POSITION_FOLLOWING
 
-       );
 
-     },
 
-     getRange: function(dontFix) {
 
-       var selection = this.getSelection(),
 
-           range = selection && selection.rangeCount && selection.getRangeAt(0);
 
-       if (dontFix !== true) {
 
-         this.fixRangeOverflow(range);
 
-       }
 
-       return range;
 
-     },
 
-     getOwnUneditables: function() {
 
-       var allUneditables = dom.query(this.contain, '.' + this.unselectableClass),
 
-           deepUneditables = dom.query(allUneditables, '.' + this.unselectableClass);
 
-       return wysihtml5.lang.array(allUneditables).without(deepUneditables);
 
-     },
 
-     // Returns an array of ranges that belong only to this editable
 
-     // Needed as uneditable block in contenteditabel can split range into pieces
 
-     // If manipulating content reverse loop is usually needed as manipulation can shift subsequent ranges
 
-     getOwnRanges: function()  {
 
-       var ranges = [],
 
-           r = this.getRange(),
 
-           tmpRanges;
 
-       if (r) { ranges.push(r); }
 
-       if (this.unselectableClass && this.contain && r) {
 
-           var uneditables = this.getOwnUneditables(),
 
-               tmpRange;
 
-           if (uneditables.length > 0) {
 
-             for (var i = 0, imax = uneditables.length; i < imax; i++) {
 
-               tmpRanges = [];
 
-               for (var j = 0, jmax = ranges.length; j < jmax; j++) {
 
-                 if (ranges[j]) {
 
-                   switch (ranges[j].compareNode(uneditables[i])) {
 
-                     case 2:
 
-                       // all selection inside uneditable. remove
 
-                     break;
 
-                     case 3:
 
-                       //section begins before and ends after uneditable. spilt
 
-                       tmpRange = ranges[j].cloneRange();
 
-                       tmpRange.setEndBefore(uneditables[i]);
 
-                       tmpRanges.push(tmpRange);
 
-                       tmpRange = ranges[j].cloneRange();
 
-                       tmpRange.setStartAfter(uneditables[i]);
 
-                       tmpRanges.push(tmpRange);
 
-                     break;
 
-                     default:
 
-                       // in all other cases uneditable does not touch selection. dont modify
 
-                       tmpRanges.push(ranges[j]);
 
-                   }
 
-                 }
 
-                 ranges = tmpRanges;
 
-               }
 
-             }
 
-           }
 
-       }
 
-       return ranges;
 
-     },
 
-     getSelection: function() {
 
-       return rangy.getSelection(this.doc.defaultView || this.doc.parentWindow);
 
-     },
 
-     setSelection: function(range) {
 
-       var win       = this.doc.defaultView || this.doc.parentWindow,
 
-           selection = rangy.getSelection(win);
 
-       return selection.setSingleRange(range);
 
-     },
 
-     createRange: function() {
 
-       return rangy.createRange(this.doc);
 
-     },
 
-     isCollapsed: function() {
 
-         return this.getSelection().isCollapsed;
 
-     },
 
-     getHtml: function() {
 
-       return this.getSelection().toHtml();
 
-     },
 
-     isEndToEndInNode: function(nodeNames) {
 
-       var range = this.getRange(),
 
-           parentElement = range.commonAncestorContainer,
 
-           startNode = range.startContainer,
 
-           endNode = range.endContainer;
 
-         if (parentElement.nodeType === wysihtml5.TEXT_NODE) {
 
-           parentElement = parentElement.parentNode;
 
-         }
 
-         if (startNode.nodeType === wysihtml5.TEXT_NODE && !(/^\s*$/).test(startNode.data.substr(range.startOffset))) {
 
-           return false;
 
-         }
 
-         if (endNode.nodeType === wysihtml5.TEXT_NODE && !(/^\s*$/).test(endNode.data.substr(range.endOffset))) {
 
-           return false;
 
-         }
 
-         while (startNode && startNode !== parentElement) {
 
-           if (startNode.nodeType !== wysihtml5.TEXT_NODE && !wysihtml5.dom.contains(parentElement, startNode)) {
 
-             return false;
 
-           }
 
-           if (wysihtml5.dom.domNode(startNode).prev({ignoreBlankTexts: true})) {
 
-             return false;
 
-           }
 
-           startNode = startNode.parentNode;
 
-         }
 
-         while (endNode && endNode !== parentElement) {
 
-           if (endNode.nodeType !== wysihtml5.TEXT_NODE && !wysihtml5.dom.contains(parentElement, endNode)) {
 
-             return false;
 
-           }
 
-           if (wysihtml5.dom.domNode(endNode).next({ignoreBlankTexts: true})) {
 
-             return false;
 
-           }
 
-           endNode = endNode.parentNode;
 
-         }
 
-         return (wysihtml5.lang.array(nodeNames).contains(parentElement.nodeName)) ? parentElement : false;
 
-     },
 
-     deselect: function() {
 
-       var sel = this.getSelection();
 
-       sel && sel.removeAllRanges();
 
-     }
 
-   });
 
- })(wysihtml5);
 
- ;/**
 
-  * Inspired by the rangy CSS Applier module written by Tim Down and licensed under the MIT license.
 
-  * http://code.google.com/p/rangy/
 
-  *
 
-  * changed in order to be able ...
 
-  *    - to use custom tags
 
-  *    - to detect and replace similar css classes via reg exp
 
-  */
 
- (function(wysihtml5, rangy) {
 
-   var defaultTagName = "span";
 
-   var REG_EXP_WHITE_SPACE = /\s+/g;
 
-   function hasClass(el, cssClass, regExp) {
 
-     if (!el.className) {
 
-       return false;
 
-     }
 
-     var matchingClassNames = el.className.match(regExp) || [];
 
-     return matchingClassNames[matchingClassNames.length - 1] === cssClass;
 
-   }
 
-   function hasStyleAttr(el, regExp) {
 
-     if (!el.getAttribute || !el.getAttribute('style')) {
 
-       return false;
 
-     }
 
-     var matchingStyles = el.getAttribute('style').match(regExp);
 
-     return  (el.getAttribute('style').match(regExp)) ? true : false;
 
-   }
 
-   function addStyle(el, cssStyle, regExp) {
 
-     if (el.getAttribute('style')) {
 
-       removeStyle(el, regExp);
 
-       if (el.getAttribute('style') && !(/^\s*$/).test(el.getAttribute('style'))) {
 
-         el.setAttribute('style', cssStyle + ";" + el.getAttribute('style'));
 
-       } else {
 
-         el.setAttribute('style', cssStyle);
 
-       }
 
-     } else {
 
-       el.setAttribute('style', cssStyle);
 
-     }
 
-   }
 
-   function addClass(el, cssClass, regExp) {
 
-     if (el.className) {
 
-       removeClass(el, regExp);
 
-       el.className += " " + cssClass;
 
-     } else {
 
-       el.className = cssClass;
 
-     }
 
-   }
 
-   function removeClass(el, regExp) {
 
-     if (el.className) {
 
-       el.className = el.className.replace(regExp, "");
 
-     }
 
-   }
 
-   function removeStyle(el, regExp) {
 
-     var s,
 
-         s2 = [];
 
-     if (el.getAttribute('style')) {
 
-       s = el.getAttribute('style').split(';');
 
-       for (var i = s.length; i--;) {
 
-         if (!s[i].match(regExp) && !(/^\s*$/).test(s[i])) {
 
-           s2.push(s[i]);
 
-         }
 
-       }
 
-       if (s2.length) {
 
-         el.setAttribute('style', s2.join(';'));
 
-       } else {
 
-         el.removeAttribute('style');
 
-       }
 
-     }
 
-   }
 
-   function getMatchingStyleRegexp(el, style) {
 
-     var regexes = [],
 
-         sSplit = style.split(';'),
 
-         elStyle = el.getAttribute('style');
 
-     if (elStyle) {
 
-       elStyle = elStyle.replace(/\s/gi, '').toLowerCase();
 
-       regexes.push(new RegExp("(^|\\s|;)" + style.replace(/\s/gi, '').replace(/([\(\)])/gi, "\\$1").toLowerCase().replace(";", ";?").replace(/rgb\\\((\d+),(\d+),(\d+)\\\)/gi, "\\s?rgb\\($1,\\s?$2,\\s?$3\\)"), "gi"));
 
-       for (var i = sSplit.length; i-- > 0;) {
 
-         if (!(/^\s*$/).test(sSplit[i])) {
 
-           regexes.push(new RegExp("(^|\\s|;)" + sSplit[i].replace(/\s/gi, '').replace(/([\(\)])/gi, "\\$1").toLowerCase().replace(";", ";?").replace(/rgb\\\((\d+),(\d+),(\d+)\\\)/gi, "\\s?rgb\\($1,\\s?$2,\\s?$3\\)"), "gi"));
 
-         }
 
-       }
 
-       for (var j = 0, jmax = regexes.length; j < jmax; j++) {
 
-         if (elStyle.match(regexes[j])) {
 
-           return regexes[j];
 
-         }
 
-       }
 
-     }
 
-     return false;
 
-   }
 
-   function isMatchingAllready(node, tags, style, className) {
 
-     if (style) {
 
-       return getMatchingStyleRegexp(node, style);
 
-     } else if (className) {
 
-       return wysihtml5.dom.hasClass(node, className);
 
-     } else {
 
-       return rangy.dom.arrayContains(tags, node.tagName.toLowerCase());
 
-     }
 
-   }
 
-   function areMatchingAllready(nodes, tags, style, className) {
 
-     for (var i = nodes.length; i--;) {
 
-       if (!isMatchingAllready(nodes[i], tags, style, className)) {
 
-         return false;
 
-       }
 
-     }
 
-     return nodes.length ? true : false;
 
-   }
 
-   function removeOrChangeStyle(el, style, regExp) {
 
-     var exactRegex = getMatchingStyleRegexp(el, style);
 
-     if (exactRegex) {
 
-       // adding same style value on property again removes style
 
-       removeStyle(el, exactRegex);
 
-       return "remove";
 
-     } else {
 
-       // adding new style value changes value
 
-       addStyle(el, style, regExp);
 
-       return "change";
 
-     }
 
-   }
 
-   function hasSameClasses(el1, el2) {
 
-     return el1.className.replace(REG_EXP_WHITE_SPACE, " ") == el2.className.replace(REG_EXP_WHITE_SPACE, " ");
 
-   }
 
-   function replaceWithOwnChildren(el) {
 
-     var parent = el.parentNode;
 
-     while (el.firstChild) {
 
-       parent.insertBefore(el.firstChild, el);
 
-     }
 
-     parent.removeChild(el);
 
-   }
 
-   function elementsHaveSameNonClassAttributes(el1, el2) {
 
-     if (el1.attributes.length != el2.attributes.length) {
 
-       return false;
 
-     }
 
-     for (var i = 0, len = el1.attributes.length, attr1, attr2, name; i < len; ++i) {
 
-       attr1 = el1.attributes[i];
 
-       name = attr1.name;
 
-       if (name != "class") {
 
-         attr2 = el2.attributes.getNamedItem(name);
 
-         if (attr1.specified != attr2.specified) {
 
-           return false;
 
-         }
 
-         if (attr1.specified && attr1.nodeValue !== attr2.nodeValue) {
 
-           return false;
 
-         }
 
-       }
 
-     }
 
-     return true;
 
-   }
 
-   function isSplitPoint(node, offset) {
 
-     if (rangy.dom.isCharacterDataNode(node)) {
 
-       if (offset == 0) {
 
-         return !!node.previousSibling;
 
-       } else if (offset == node.length) {
 
-         return !!node.nextSibling;
 
-       } else {
 
-         return true;
 
-       }
 
-     }
 
-     return offset > 0 && offset < node.childNodes.length;
 
-   }
 
-   function splitNodeAt(node, descendantNode, descendantOffset, container) {
 
-     var newNode;
 
-     if (rangy.dom.isCharacterDataNode(descendantNode)) {
 
-       if (descendantOffset == 0) {
 
-         descendantOffset = rangy.dom.getNodeIndex(descendantNode);
 
-         descendantNode = descendantNode.parentNode;
 
-       } else if (descendantOffset == descendantNode.length) {
 
-         descendantOffset = rangy.dom.getNodeIndex(descendantNode) + 1;
 
-         descendantNode = descendantNode.parentNode;
 
-       } else {
 
-         newNode = rangy.dom.splitDataNode(descendantNode, descendantOffset);
 
-       }
 
-     }
 
-     if (!newNode) {
 
-       if (!container || descendantNode !== container) {
 
-         newNode = descendantNode.cloneNode(false);
 
-         if (newNode.id) {
 
-           newNode.removeAttribute("id");
 
-         }
 
-         var child;
 
-         while ((child = descendantNode.childNodes[descendantOffset])) {
 
-           newNode.appendChild(child);
 
-         }
 
-         rangy.dom.insertAfter(newNode, descendantNode);
 
-       }
 
-     }
 
-     return (descendantNode == node) ? newNode :  splitNodeAt(node, newNode.parentNode, rangy.dom.getNodeIndex(newNode), container);
 
-   }
 
-   function Merge(firstNode) {
 
-     this.isElementMerge = (firstNode.nodeType == wysihtml5.ELEMENT_NODE);
 
-     this.firstTextNode = this.isElementMerge ? firstNode.lastChild : firstNode;
 
-     this.textNodes = [this.firstTextNode];
 
-   }
 
-   Merge.prototype = {
 
-     doMerge: function() {
 
-       var textBits = [], textNode, parent, text;
 
-       for (var i = 0, len = this.textNodes.length; i < len; ++i) {
 
-         textNode = this.textNodes[i];
 
-         parent = textNode.parentNode;
 
-         textBits[i] = textNode.data;
 
-         if (i) {
 
-           parent.removeChild(textNode);
 
-           if (!parent.hasChildNodes()) {
 
-             parent.parentNode.removeChild(parent);
 
-           }
 
-         }
 
-       }
 
-       this.firstTextNode.data = text = textBits.join("");
 
-       return text;
 
-     },
 
-     getLength: function() {
 
-       var i = this.textNodes.length, len = 0;
 
-       while (i--) {
 
-         len += this.textNodes[i].length;
 
-       }
 
-       return len;
 
-     },
 
-     toString: function() {
 
-       var textBits = [];
 
-       for (var i = 0, len = this.textNodes.length; i < len; ++i) {
 
-         textBits[i] = "'" + this.textNodes[i].data + "'";
 
-       }
 
-       return "[Merge(" + textBits.join(",") + ")]";
 
-     }
 
-   };
 
-   function HTMLApplier(tagNames, cssClass, similarClassRegExp, normalize, cssStyle, similarStyleRegExp, container) {
 
-     this.tagNames = tagNames || [defaultTagName];
 
-     this.cssClass = cssClass || ((cssClass === false) ? false : "");
 
-     this.similarClassRegExp = similarClassRegExp;
 
-     this.cssStyle = cssStyle || "";
 
-     this.similarStyleRegExp = similarStyleRegExp;
 
-     this.normalize = normalize;
 
-     this.applyToAnyTagName = false;
 
-     this.container = container;
 
-   }
 
-   HTMLApplier.prototype = {
 
-     getAncestorWithClass: function(node) {
 
-       var cssClassMatch;
 
-       while (node) {
 
-         cssClassMatch = this.cssClass ? hasClass(node, this.cssClass, this.similarClassRegExp) : (this.cssStyle !== "") ? false : true;
 
-         if (node.nodeType == wysihtml5.ELEMENT_NODE && node.getAttribute("contenteditable") != "false" &&  rangy.dom.arrayContains(this.tagNames, node.tagName.toLowerCase()) && cssClassMatch) {
 
-           return node;
 
-         }
 
-         node = node.parentNode;
 
-       }
 
-       return false;
 
-     },
 
-     // returns parents of node with given style attribute
 
-     getAncestorWithStyle: function(node) {
 
-       var cssStyleMatch;
 
-       while (node) {
 
-         cssStyleMatch = this.cssStyle ? hasStyleAttr(node, this.similarStyleRegExp) : false;
 
-         if (node.nodeType == wysihtml5.ELEMENT_NODE && node.getAttribute("contenteditable") != "false" && rangy.dom.arrayContains(this.tagNames, node.tagName.toLowerCase()) && cssStyleMatch) {
 
-           return node;
 
-         }
 
-         node = node.parentNode;
 
-       }
 
-       return false;
 
-     },
 
-     getMatchingAncestor: function(node) {
 
-       var ancestor = this.getAncestorWithClass(node),
 
-           matchType = false;
 
-       if (!ancestor) {
 
-         ancestor = this.getAncestorWithStyle(node);
 
-         if (ancestor) {
 
-           matchType = "style";
 
-         }
 
-       } else {
 
-         if (this.cssStyle) {
 
-           matchType = "class";
 
-         }
 
-       }
 
-       return {
 
-         "element": ancestor,
 
-         "type": matchType
 
-       };
 
-     },
 
-     // Normalizes nodes after applying a CSS class to a Range.
 
-     postApply: function(textNodes, range) {
 
-       var firstNode = textNodes[0], lastNode = textNodes[textNodes.length - 1];
 
-       var merges = [], currentMerge;
 
-       var rangeStartNode = firstNode, rangeEndNode = lastNode;
 
-       var rangeStartOffset = 0, rangeEndOffset = lastNode.length;
 
-       var textNode, precedingTextNode;
 
-       for (var i = 0, len = textNodes.length; i < len; ++i) {
 
-         textNode = textNodes[i];
 
-         precedingTextNode = null;
 
-         if (textNode && textNode.parentNode) {
 
-           precedingTextNode = this.getAdjacentMergeableTextNode(textNode.parentNode, false);
 
-         }
 
-         if (precedingTextNode) {
 
-           if (!currentMerge) {
 
-             currentMerge = new Merge(precedingTextNode);
 
-             merges.push(currentMerge);
 
-           }
 
-           currentMerge.textNodes.push(textNode);
 
-           if (textNode === firstNode) {
 
-             rangeStartNode = currentMerge.firstTextNode;
 
-             rangeStartOffset = rangeStartNode.length;
 
-           }
 
-           if (textNode === lastNode) {
 
-             rangeEndNode = currentMerge.firstTextNode;
 
-             rangeEndOffset = currentMerge.getLength();
 
-           }
 
-         } else {
 
-           currentMerge = null;
 
-         }
 
-       }
 
-       // Test whether the first node after the range needs merging
 
-       if(lastNode && lastNode.parentNode) {
 
-         var nextTextNode = this.getAdjacentMergeableTextNode(lastNode.parentNode, true);
 
-         if (nextTextNode) {
 
-           if (!currentMerge) {
 
-             currentMerge = new Merge(lastNode);
 
-             merges.push(currentMerge);
 
-           }
 
-           currentMerge.textNodes.push(nextTextNode);
 
-         }
 
-       }
 
-       // Do the merges
 
-       if (merges.length) {
 
-         for (i = 0, len = merges.length; i < len; ++i) {
 
-           merges[i].doMerge();
 
-         }
 
-         // Set the range boundaries
 
-         range.setStart(rangeStartNode, rangeStartOffset);
 
-         range.setEnd(rangeEndNode, rangeEndOffset);
 
-       }
 
-     },
 
-     getAdjacentMergeableTextNode: function(node, forward) {
 
-         var isTextNode = (node.nodeType == wysihtml5.TEXT_NODE);
 
-         var el = isTextNode ? node.parentNode : node;
 
-         var adjacentNode;
 
-         var propName = forward ? "nextSibling" : "previousSibling";
 
-         if (isTextNode) {
 
-           // Can merge if the node's previous/next sibling is a text node
 
-           adjacentNode = node[propName];
 
-           if (adjacentNode && adjacentNode.nodeType == wysihtml5.TEXT_NODE) {
 
-             return adjacentNode;
 
-           }
 
-         } else {
 
-           // Compare element with its sibling
 
-           adjacentNode = el[propName];
 
-           if (adjacentNode && this.areElementsMergeable(node, adjacentNode)) {
 
-             return adjacentNode[forward ? "firstChild" : "lastChild"];
 
-           }
 
-         }
 
-         return null;
 
-     },
 
-     areElementsMergeable: function(el1, el2) {
 
-       return rangy.dom.arrayContains(this.tagNames, (el1.tagName || "").toLowerCase())
 
-         && rangy.dom.arrayContains(this.tagNames, (el2.tagName || "").toLowerCase())
 
-         && hasSameClasses(el1, el2)
 
-         && elementsHaveSameNonClassAttributes(el1, el2);
 
-     },
 
-     createContainer: function(doc) {
 
-       var el = doc.createElement(this.tagNames[0]);
 
-       if (this.cssClass) {
 
-         el.className = this.cssClass;
 
-       }
 
-       if (this.cssStyle) {
 
-         el.setAttribute('style', this.cssStyle);
 
-       }
 
-       return el;
 
-     },
 
-     applyToTextNode: function(textNode) {
 
-       var parent = textNode.parentNode;
 
-       if (parent.childNodes.length == 1 && rangy.dom.arrayContains(this.tagNames, parent.tagName.toLowerCase())) {
 
-         if (this.cssClass) {
 
-           addClass(parent, this.cssClass, this.similarClassRegExp);
 
-         }
 
-         if (this.cssStyle) {
 
-           addStyle(parent, this.cssStyle, this.similarStyleRegExp);
 
-         }
 
-       } else {
 
-         var el = this.createContainer(rangy.dom.getDocument(textNode));
 
-         textNode.parentNode.insertBefore(el, textNode);
 
-         el.appendChild(textNode);
 
-       }
 
-     },
 
-     isRemovable: function(el) {
 
-       return rangy.dom.arrayContains(this.tagNames, el.tagName.toLowerCase()) &&
 
-               wysihtml5.lang.string(el.className).trim() === "" &&
 
-               (
 
-                 !el.getAttribute('style') ||
 
-                 wysihtml5.lang.string(el.getAttribute('style')).trim() === ""
 
-               );
 
-     },
 
-     undoToTextNode: function(textNode, range, ancestorWithClass, ancestorWithStyle) {
 
-       var styleMode = (ancestorWithClass) ? false : true,
 
-           ancestor = ancestorWithClass || ancestorWithStyle,
 
-           styleChanged = false;
 
-       if (!range.containsNode(ancestor)) {
 
-         // Split out the portion of the ancestor from which we can remove the CSS class
 
-         var ancestorRange = range.cloneRange();
 
-             ancestorRange.selectNode(ancestor);
 
-         if (ancestorRange.isPointInRange(range.endContainer, range.endOffset) && isSplitPoint(range.endContainer, range.endOffset)) {
 
-             splitNodeAt(ancestor, range.endContainer, range.endOffset, this.container);
 
-             range.setEndAfter(ancestor);
 
-         }
 
-         if (ancestorRange.isPointInRange(range.startContainer, range.startOffset) && isSplitPoint(range.startContainer, range.startOffset)) {
 
-             ancestor = splitNodeAt(ancestor, range.startContainer, range.startOffset, this.container);
 
-         }
 
-       }
 
-       if (!styleMode && this.similarClassRegExp) {
 
-         removeClass(ancestor, this.similarClassRegExp);
 
-       }
 
-       if (styleMode && this.similarStyleRegExp) {
 
-         styleChanged = (removeOrChangeStyle(ancestor, this.cssStyle, this.similarStyleRegExp) === "change");
 
-       }
 
-       if (this.isRemovable(ancestor) && !styleChanged) {
 
-         replaceWithOwnChildren(ancestor);
 
-       }
 
-     },
 
-     applyToRange: function(range) {
 
-         var textNodes;
 
-         for (var ri = range.length; ri--;) {
 
-             textNodes = range[ri].getNodes([wysihtml5.TEXT_NODE]);
 
-             if (!textNodes.length) {
 
-               try {
 
-                 var node = this.createContainer(range[ri].endContainer.ownerDocument);
 
-                 range[ri].surroundContents(node);
 
-                 this.selectNode(range[ri], node);
 
-                 return;
 
-               } catch(e) {}
 
-             }
 
-             range[ri].splitBoundaries();
 
-             textNodes = range[ri].getNodes([wysihtml5.TEXT_NODE]);
 
-             if (textNodes.length) {
 
-               var textNode;
 
-               for (var i = 0, len = textNodes.length; i < len; ++i) {
 
-                 textNode = textNodes[i];
 
-                 if (!this.getMatchingAncestor(textNode).element) {
 
-                   this.applyToTextNode(textNode);
 
-                 }
 
-               }
 
-               range[ri].setStart(textNodes[0], 0);
 
-               textNode = textNodes[textNodes.length - 1];
 
-               range[ri].setEnd(textNode, textNode.length);
 
-               if (this.normalize) {
 
-                 this.postApply(textNodes, range[ri]);
 
-               }
 
-             }
 
-         }
 
-     },
 
-     undoToRange: function(range) {
 
-       var textNodes, textNode, ancestorWithClass, ancestorWithStyle, ancestor;
 
-       for (var ri = range.length; ri--;) {
 
-           textNodes = range[ri].getNodes([wysihtml5.TEXT_NODE]);
 
-           if (textNodes.length) {
 
-             range[ri].splitBoundaries();
 
-             textNodes = range[ri].getNodes([wysihtml5.TEXT_NODE]);
 
-           } else {
 
-             var doc = range[ri].endContainer.ownerDocument,
 
-                 node = doc.createTextNode(wysihtml5.INVISIBLE_SPACE);
 
-             range[ri].insertNode(node);
 
-             range[ri].selectNode(node);
 
-             textNodes = [node];
 
-           }
 
-           for (var i = 0, len = textNodes.length; i < len; ++i) {
 
-             if (range[ri].isValid()) {
 
-               textNode = textNodes[i];
 
-               ancestor = this.getMatchingAncestor(textNode);
 
-               if (ancestor.type === "style") {
 
-                 this.undoToTextNode(textNode, range[ri], false, ancestor.element);
 
-               } else if (ancestor.element) {
 
-                 this.undoToTextNode(textNode, range[ri], ancestor.element);
 
-               }
 
-             }
 
-           }
 
-           if (len == 1) {
 
-             this.selectNode(range[ri], textNodes[0]);
 
-           } else {
 
-             range[ri].setStart(textNodes[0], 0);
 
-             textNode = textNodes[textNodes.length - 1];
 
-             range[ri].setEnd(textNode, textNode.length);
 
-             if (this.normalize) {
 
-               this.postApply(textNodes, range[ri]);
 
-             }
 
-           }
 
-       }
 
-     },
 
-     selectNode: function(range, node) {
 
-       var isElement       = node.nodeType === wysihtml5.ELEMENT_NODE,
 
-           canHaveHTML     = "canHaveHTML" in node ? node.canHaveHTML : true,
 
-           content         = isElement ? node.innerHTML : node.data,
 
-           isEmpty         = (content === "" || content === wysihtml5.INVISIBLE_SPACE);
 
-       if (isEmpty && isElement && canHaveHTML) {
 
-         // Make sure that caret is visible in node by inserting a zero width no breaking space
 
-         try { node.innerHTML = wysihtml5.INVISIBLE_SPACE; } catch(e) {}
 
-       }
 
-       range.selectNodeContents(node);
 
-       if (isEmpty && isElement) {
 
-         range.collapse(false);
 
-       } else if (isEmpty) {
 
-         range.setStartAfter(node);
 
-         range.setEndAfter(node);
 
-       }
 
-     },
 
-     getTextSelectedByRange: function(textNode, range) {
 
-       var textRange = range.cloneRange();
 
-       textRange.selectNodeContents(textNode);
 
-       var intersectionRange = textRange.intersection(range);
 
-       var text = intersectionRange ? intersectionRange.toString() : "";
 
-       textRange.detach();
 
-       return text;
 
-     },
 
-     isAppliedToRange: function(range) {
 
-       var ancestors = [],
 
-           appliedType = "full",
 
-           ancestor, styleAncestor, textNodes;
 
-       for (var ri = range.length; ri--;) {
 
-         textNodes = range[ri].getNodes([wysihtml5.TEXT_NODE]);
 
-         if (!textNodes.length) {
 
-           ancestor = this.getMatchingAncestor(range[ri].startContainer).element;
 
-           return (ancestor) ? {
 
-             "elements": [ancestor],
 
-             "coverage": appliedType
 
-           } : false;
 
-         }
 
-         for (var i = 0, len = textNodes.length, selectedText; i < len; ++i) {
 
-           selectedText = this.getTextSelectedByRange(textNodes[i], range[ri]);
 
-           ancestor = this.getMatchingAncestor(textNodes[i]).element;
 
-           if (ancestor && selectedText != "") {
 
-             ancestors.push(ancestor);
 
-             if (wysihtml5.dom.getTextNodes(ancestor, true).length === 1) {
 
-               appliedType = "full";
 
-             } else if (appliedType === "full") {
 
-               appliedType = "inline";
 
-             }
 
-           } else if (!ancestor) {
 
-             appliedType = "partial";
 
-           }
 
-         }
 
-       }
 
-       return (ancestors.length) ? {
 
-         "elements": ancestors,
 
-         "coverage": appliedType
 
-       } : false;
 
-     },
 
-     toggleRange: function(range) {
 
-       var isApplied = this.isAppliedToRange(range),
 
-           parentsExactMatch;
 
-       if (isApplied) {
 
-         if (isApplied.coverage === "full") {
 
-           this.undoToRange(range);
 
-         } else if (isApplied.coverage === "inline") {
 
-           parentsExactMatch = areMatchingAllready(isApplied.elements, this.tagNames, this.cssStyle, this.cssClass);
 
-           this.undoToRange(range);
 
-           if (!parentsExactMatch) {
 
-             this.applyToRange(range);
 
-           }
 
-         } else {
 
-           // partial
 
-           if (!areMatchingAllready(isApplied.elements, this.tagNames, this.cssStyle, this.cssClass)) {
 
-             this.undoToRange(range);
 
-           }
 
-           this.applyToRange(range);
 
-         }
 
-       } else {
 
-         this.applyToRange(range);
 
-       }
 
-     }
 
-   };
 
-   wysihtml5.selection.HTMLApplier = HTMLApplier;
 
- })(wysihtml5, rangy);
 
- ;/**
 
-  * Rich Text Query/Formatting Commands
 
-  *
 
-  * @example
 
-  *    var commands = new wysihtml5.Commands(editor);
 
-  */
 
- wysihtml5.Commands = Base.extend(
 
-   /** @scope wysihtml5.Commands.prototype */ {
 
-   constructor: function(editor) {
 
-     this.editor   = editor;
 
-     this.composer = editor.composer;
 
-     this.doc      = this.composer.doc;
 
-   },
 
-   /**
 
-    * Check whether the browser supports the given command
 
-    *
 
-    * @param {String} command The command string which to check (eg. "bold", "italic", "insertUnorderedList")
 
-    * @example
 
-    *    commands.supports("createLink");
 
-    */
 
-   support: function(command) {
 
-     return wysihtml5.browser.supportsCommand(this.doc, command);
 
-   },
 
-   /**
 
-    * Check whether the browser supports the given command
 
-    *
 
-    * @param {String} command The command string which to execute (eg. "bold", "italic", "insertUnorderedList")
 
-    * @param {String} [value] The command value parameter, needed for some commands ("createLink", "insertImage", ...), optional for commands that don't require one ("bold", "underline", ...)
 
-    * @example
 
-    *    commands.exec("insertImage", "http://a1.twimg.com/profile_images/113868655/schrei_twitter_reasonably_small.jpg");
 
-    */
 
-   exec: function(command, value) {
 
-     var obj     = wysihtml5.commands[command],
 
-         args    = wysihtml5.lang.array(arguments).get(),
 
-         method  = obj && obj.exec,
 
-         result  = null;
 
-     this.editor.fire("beforecommand:composer");
 
-     if (method) {
 
-       args.unshift(this.composer);
 
-       result = method.apply(obj, args);
 
-     } else {
 
-       try {
 
-         // try/catch for buggy firefox
 
-         result = this.doc.execCommand(command, false, value);
 
-       } catch(e) {}
 
-     }
 
-     this.editor.fire("aftercommand:composer");
 
-     return result;
 
-   },
 
-   /**
 
-    * Check whether the current command is active
 
-    * If the caret is within a bold text, then calling this with command "bold" should return true
 
-    *
 
-    * @param {String} command The command string which to check (eg. "bold", "italic", "insertUnorderedList")
 
-    * @param {String} [commandValue] The command value parameter (eg. for "insertImage" the image src)
 
-    * @return {Boolean} Whether the command is active
 
-    * @example
 
-    *    var isCurrentSelectionBold = commands.state("bold");
 
-    */
 
-   state: function(command, commandValue) {
 
-     var obj     = wysihtml5.commands[command],
 
-         args    = wysihtml5.lang.array(arguments).get(),
 
-         method  = obj && obj.state;
 
-     if (method) {
 
-       args.unshift(this.composer);
 
-       return method.apply(obj, args);
 
-     } else {
 
-       try {
 
-         // try/catch for buggy firefox
 
-         return this.doc.queryCommandState(command);
 
-       } catch(e) {
 
-         return false;
 
-       }
 
-     }
 
-   },
 
-   /* Get command state parsed value if command has stateValue parsing function */
 
-   stateValue: function(command) {
 
-     var obj     = wysihtml5.commands[command],
 
-         args    = wysihtml5.lang.array(arguments).get(),
 
-         method  = obj && obj.stateValue;
 
-     if (method) {
 
-       args.unshift(this.composer);
 
-       return method.apply(obj, args);
 
-     } else {
 
-       return false;
 
-     }
 
-   }
 
- });
 
- ;wysihtml5.commands.bold = {
 
-   exec: function(composer, command) {
 
-     wysihtml5.commands.formatInline.execWithToggle(composer, command, "b");
 
-   },
 
-   state: function(composer, command) {
 
-     // element.ownerDocument.queryCommandState("bold") results:
 
-     // firefox: only <b>
 
-     // chrome:  <b>, <strong>, <h1>, <h2>, ...
 
-     // ie:      <b>, <strong>
 
-     // opera:   <b>, <strong>
 
-     return wysihtml5.commands.formatInline.state(composer, command, "b");
 
-   }
 
- };
 
- ;(function(wysihtml5) {
 
-   var undef,
 
-       NODE_NAME = "A",
 
-       dom       = wysihtml5.dom;
 
-   function _format(composer, attributes) {
 
-     var doc             = composer.doc,
 
-         tempClass       = "_wysihtml5-temp-" + (+new Date()),
 
-         tempClassRegExp = /non-matching-class/g,
 
-         i               = 0,
 
-         length,
 
-         anchors,
 
-         anchor,
 
-         hasElementChild,
 
-         isEmpty,
 
-         elementToSetCaretAfter,
 
-         textContent,
 
-         whiteSpace,
 
-         j;
 
-     wysihtml5.commands.formatInline.exec(composer, undef, NODE_NAME, tempClass, tempClassRegExp, undef, undef, true, true);
 
-     anchors = doc.querySelectorAll(NODE_NAME + "." + tempClass);
 
-     length  = anchors.length;
 
-     for (; i<length; i++) {
 
-       anchor = anchors[i];
 
-       anchor.removeAttribute("class");
 
-       for (j in attributes) {
 
-         // Do not set attribute "text" as it is meant for setting string value if created link has no textual data
 
-         if (j !== "text") {
 
-           anchor.setAttribute(j, attributes[j]);
 
-         }
 
-       }
 
-     }
 
-     elementToSetCaretAfter = anchor;
 
-     if (length === 1) {
 
-       textContent = dom.getTextContent(anchor);
 
-       hasElementChild = !!anchor.querySelector("*");
 
-       isEmpty = textContent === "" || textContent === wysihtml5.INVISIBLE_SPACE;
 
-       if (!hasElementChild && isEmpty) {
 
-         dom.setTextContent(anchor, attributes.text || anchor.href);
 
-         whiteSpace = doc.createTextNode(" ");
 
-         composer.selection.setAfter(anchor);
 
-         dom.insert(whiteSpace).after(anchor);
 
-         elementToSetCaretAfter = whiteSpace;
 
-       }
 
-     }
 
-     composer.selection.setAfter(elementToSetCaretAfter);
 
-   }
 
-   // Changes attributes of links
 
-   function _changeLinks(composer, anchors, attributes) {
 
-     var oldAttrs;
 
-     for (var a = anchors.length; a--;) {
 
-       // Remove all old attributes
 
-       oldAttrs = anchors[a].attributes;
 
-       for (var oa = oldAttrs.length; oa--;) {
 
-         anchors[a].removeAttribute(oldAttrs.item(oa).name);
 
-       }
 
-       // Set new attributes
 
-       for (var j in attributes) {
 
-         if (attributes.hasOwnProperty(j)) {
 
-           anchors[a].setAttribute(j, attributes[j]);
 
-         }
 
-       }
 
-     }
 
-   }
 
-   wysihtml5.commands.createLink = {
 
-     /**
 
-      * TODO: Use HTMLApplier or formatInline here
 
-      *
 
-      * Turns selection into a link
 
-      * If selection is already a link, it just changes the attributes
 
-      *
 
-      * @example
 
-      *    // either ...
 
-      *    wysihtml5.commands.createLink.exec(composer, "createLink", "http://www.google.de");
 
-      *    // ... or ...
 
-      *    wysihtml5.commands.createLink.exec(composer, "createLink", { href: "http://www.google.de", target: "_blank" });
 
-      */
 
-     exec: function(composer, command, value) {
 
-       var anchors = this.state(composer, command);
 
-       if (anchors) {
 
-         // Selection contains links then change attributes of these links
 
-         composer.selection.executeAndRestore(function() {
 
-           _changeLinks(composer, anchors, value);
 
-         });
 
-       } else {
 
-         // Create links
 
-         value = typeof(value) === "object" ? value : { href: value };
 
-         _format(composer, value);
 
-       }
 
-     },
 
-     state: function(composer, command) {
 
-       return wysihtml5.commands.formatInline.state(composer, command, "A");
 
-     }
 
-   };
 
- })(wysihtml5);
 
- ;(function(wysihtml5) {
 
-   var dom = wysihtml5.dom;
 
-   function _removeFormat(composer, anchors) {
 
-     var length  = anchors.length,
 
-         i       = 0,
 
-         anchor,
 
-         codeElement,
 
-         textContent;
 
-     for (; i<length; i++) {
 
-       anchor      = anchors[i];
 
-       codeElement = dom.getParentElement(anchor, { nodeName: "code" });
 
-       textContent = dom.getTextContent(anchor);
 
-       // if <a> contains url-like text content, rename it to <code> to prevent re-autolinking
 
-       // else replace <a> with its childNodes
 
-       if (textContent.match(dom.autoLink.URL_REG_EXP) && !codeElement) {
 
-         // <code> element is used to prevent later auto-linking of the content
 
-         codeElement = dom.renameElement(anchor, "code");
 
-       } else {
 
-         dom.replaceWithChildNodes(anchor);
 
-       }
 
-     }
 
-   }
 
-   wysihtml5.commands.removeLink = {
 
-     /*
 
-      * If selection is a link, it removes the link and wraps it with a <code> element
 
-      * The <code> element is needed to avoid auto linking
 
-      *
 
-      * @example
 
-      *    wysihtml5.commands.createLink.exec(composer, "removeLink");
 
-      */
 
-     exec: function(composer, command) {
 
-       var anchors = this.state(composer, command);
 
-       if (anchors) {
 
-         composer.selection.executeAndRestore(function() {
 
-           _removeFormat(composer, anchors);
 
-         });
 
-       }
 
-     },
 
-     state: function(composer, command) {
 
-       return wysihtml5.commands.formatInline.state(composer, command, "A");
 
-     }
 
-   };
 
- })(wysihtml5);
 
- ;/**
 
-  * document.execCommand("fontSize") will create either inline styles (firefox, chrome) or use font tags
 
-  * which we don't want
 
-  * Instead we set a css class
 
-  */
 
- (function(wysihtml5) {
 
-   var REG_EXP = /wysiwyg-font-size-[0-9a-z\-]+/g;
 
-   wysihtml5.commands.fontSize = {
 
-     exec: function(composer, command, size) {
 
-         wysihtml5.commands.formatInline.execWithToggle(composer, command, "span", "wysiwyg-font-size-" + size, REG_EXP);
 
-     },
 
-     state: function(composer, command, size) {
 
-       return wysihtml5.commands.formatInline.state(composer, command, "span", "wysiwyg-font-size-" + size, REG_EXP);
 
-     }
 
-   };
 
- })(wysihtml5);
 
- ;/* In case font size adjustment to any number defined by user is preferred, we cannot use classes and must use inline styles. */
 
- (function(wysihtml5) {
 
-   var REG_EXP = /(\s|^)font-size\s*:\s*[^;\s]+;?/gi;
 
-   wysihtml5.commands.fontSizeStyle = {
 
-     exec: function(composer, command, size) {
 
-       size = (typeof(size) == "object") ? size.size : size;
 
-       if (!(/^\s*$/).test(size)) {
 
-         wysihtml5.commands.formatInline.execWithToggle(composer, command, "span", false, false, "font-size:" + size, REG_EXP);
 
-       }
 
-     },
 
-     state: function(composer, command, size) {
 
-       return wysihtml5.commands.formatInline.state(composer, command, "span", false, false, "font-size", REG_EXP);
 
-     },
 
-     stateValue: function(composer, command) {
 
-       var st = this.state(composer, command),
 
-           styleStr, fontsizeMatches,
 
-           val = false;
 
-       if (st && wysihtml5.lang.object(st).isArray()) {
 
-           st = st[0];
 
-       }
 
-       if (st) {
 
-         styleStr = st.getAttribute('style');
 
-         if (styleStr) {
 
-           return wysihtml5.quirks.styleParser.parseFontSize(styleStr);
 
-         }
 
-       }
 
-       return false;
 
-     }
 
-   };
 
- })(wysihtml5);
 
- ;/**
 
-  * document.execCommand("foreColor") will create either inline styles (firefox, chrome) or use font tags
 
-  * which we don't want
 
-  * Instead we set a css class
 
-  */
 
- (function(wysihtml5) {
 
-   var REG_EXP = /wysiwyg-color-[0-9a-z]+/g;
 
-   wysihtml5.commands.foreColor = {
 
-     exec: function(composer, command, color) {
 
-         wysihtml5.commands.formatInline.execWithToggle(composer, command, "span", "wysiwyg-color-" + color, REG_EXP);
 
-     },
 
-     state: function(composer, command, color) {
 
-       return wysihtml5.commands.formatInline.state(composer, command, "span", "wysiwyg-color-" + color, REG_EXP);
 
-     }
 
-   };
 
- })(wysihtml5);
 
- ;/**
 
-  * document.execCommand("foreColor") will create either inline styles (firefox, chrome) or use font tags
 
-  * which we don't want
 
-  * Instead we set a css class
 
-  */
 
- (function(wysihtml5) {
 
-   var REG_EXP = /(\s|^)color\s*:\s*[^;\s]+;?/gi;
 
-   wysihtml5.commands.foreColorStyle = {
 
-     exec: function(composer, command, color) {
 
-       var colorVals  = wysihtml5.quirks.styleParser.parseColor((typeof(color) == "object") ? "color:" + color.color : "color:" + color, "color"),
 
-           colString;
 
-       if (colorVals) {
 
-         colString = "color: rgb(" + colorVals[0] + ',' + colorVals[1] + ',' + colorVals[2] + ');';
 
-         if (colorVals[3] !== 1) {
 
-           colString += "color: rgba(" + colorVals[0] + ',' + colorVals[1] + ',' + colorVals[2] + ',' + colorVals[3] + ');';
 
-         }
 
-         wysihtml5.commands.formatInline.execWithToggle(composer, command, "span", false, false, colString, REG_EXP);
 
-       }
 
-     },
 
-     state: function(composer, command) {
 
-       return wysihtml5.commands.formatInline.state(composer, command, "span", false, false, "color", REG_EXP);
 
-     },
 
-     stateValue: function(composer, command, props) {
 
-       var st = this.state(composer, command),
 
-           colorStr;
 
-       if (st && wysihtml5.lang.object(st).isArray()) {
 
-         st = st[0];
 
-       }
 
-       if (st) {
 
-         colorStr = st.getAttribute('style');
 
-         if (colorStr) {
 
-           if (colorStr) {
 
-             val = wysihtml5.quirks.styleParser.parseColor(colorStr, "color");
 
-             return wysihtml5.quirks.styleParser.unparseColor(val, props);
 
-           }
 
-         }
 
-       }
 
-       return false;
 
-     }
 
-   };
 
- })(wysihtml5);
 
- ;/* In case background adjustment to any color defined by user is preferred, we cannot use classes and must use inline styles. */
 
- (function(wysihtml5) {
 
-   var REG_EXP = /(\s|^)background-color\s*:\s*[^;\s]+;?/gi;
 
-   wysihtml5.commands.bgColorStyle = {
 
-     exec: function(composer, command, color) {
 
-       var colorVals  = wysihtml5.quirks.styleParser.parseColor((typeof(color) == "object") ? "background-color:" + color.color : "background-color:" + color, "background-color"),
 
-           colString;
 
-       if (colorVals) {
 
-         colString = "background-color: rgb(" + colorVals[0] + ',' + colorVals[1] + ',' + colorVals[2] + ');';
 
-         if (colorVals[3] !== 1) {
 
-           colString += "background-color: rgba(" + colorVals[0] + ',' + colorVals[1] + ',' + colorVals[2] + ',' + colorVals[3] + ');';
 
-         }
 
-         wysihtml5.commands.formatInline.execWithToggle(composer, command, "span", false, false, colString, REG_EXP);
 
-       }
 
-     },
 
-     state: function(composer, command) {
 
-       return wysihtml5.commands.formatInline.state(composer, command, "span", false, false, "background-color", REG_EXP);
 
-     },
 
-     stateValue: function(composer, command, props) {
 
-       var st = this.state(composer, command),
 
-           colorStr,
 
-           val = false;
 
-       if (st && wysihtml5.lang.object(st).isArray()) {
 
-         st = st[0];
 
-       }
 
-       if (st) {
 
-         colorStr = st.getAttribute('style');
 
-         if (colorStr) {
 
-           val = wysihtml5.quirks.styleParser.parseColor(colorStr, "background-color");
 
-           return wysihtml5.quirks.styleParser.unparseColor(val, props);
 
-         }
 
-       }
 
-       return false;
 
-     }
 
-   };
 
- })(wysihtml5);
 
- ;(function(wysihtml5) {
 
-   var dom                     = wysihtml5.dom,
 
-       // Following elements are grouped
 
-       // when the caret is within a H1 and the H4 is invoked, the H1 should turn into H4
 
-       // instead of creating a H4 within a H1 which would result in semantically invalid html
 
-       BLOCK_ELEMENTS_GROUP    = ["H1", "H2", "H3", "H4", "H5", "H6", "P", "PRE", "DIV"];
 
-   /**
 
-    * Remove similiar classes (based on classRegExp)
 
-    * and add the desired class name
 
-    */
 
-   function _addClass(element, className, classRegExp) {
 
-     if (element.className) {
 
-       _removeClass(element, classRegExp);
 
-       element.className = wysihtml5.lang.string(element.className + " " + className).trim();
 
-     } else {
 
-       element.className = className;
 
-     }
 
-   }
 
-   function _addStyle(element, cssStyle, styleRegExp) {
 
-     _removeStyle(element, styleRegExp);
 
-     if (element.getAttribute('style')) {
 
-       element.setAttribute('style', wysihtml5.lang.string(element.getAttribute('style') + " " + cssStyle).trim());
 
-     } else {
 
-       element.setAttribute('style', cssStyle);
 
-     }
 
-   }
 
-   function _removeClass(element, classRegExp) {
 
-     var ret = classRegExp.test(element.className);
 
-     element.className = element.className.replace(classRegExp, "");
 
-     if (wysihtml5.lang.string(element.className).trim() == '') {
 
-         element.removeAttribute('class');
 
-     }
 
-     return ret;
 
-   }
 
-   function _removeStyle(element, styleRegExp) {
 
-     var ret = styleRegExp.test(element.getAttribute('style'));
 
-     element.setAttribute('style', (element.getAttribute('style') || "").replace(styleRegExp, ""));
 
-     if (wysihtml5.lang.string(element.getAttribute('style') || "").trim() == '') {
 
-       element.removeAttribute('style');
 
-     }
 
-     return ret;
 
-   }
 
-   function _removeLastChildIfLineBreak(node) {
 
-     var lastChild = node.lastChild;
 
-     if (lastChild && _isLineBreak(lastChild)) {
 
-       lastChild.parentNode.removeChild(lastChild);
 
-     }
 
-   }
 
-   function _isLineBreak(node) {
 
-     return node.nodeName === "BR";
 
-   }
 
-   /**
 
-    * Execute native query command
 
-    * and if necessary modify the inserted node's className
 
-    */
 
-   function _execCommand(doc, composer, command, nodeName, className) {
 
-     var ranges = composer.selection.getOwnRanges();
 
-     for (var i = ranges.length; i--;){
 
-       composer.selection.getSelection().removeAllRanges();
 
-       composer.selection.setSelection(ranges[i]);
 
-       if (className) {
 
-         var eventListener = dom.observe(doc, "DOMNodeInserted", function(event) {
 
-           var target = event.target,
 
-               displayStyle;
 
-           if (target.nodeType !== wysihtml5.ELEMENT_NODE) {
 
-             return;
 
-           }
 
-           displayStyle = dom.getStyle("display").from(target);
 
-           if (displayStyle.substr(0, 6) !== "inline") {
 
-             // Make sure that only block elements receive the given class
 
-             target.className += " " + className;
 
-           }
 
-         });
 
-       }
 
-       doc.execCommand(command, false, nodeName);
 
-       if (eventListener) {
 
-         eventListener.stop();
 
-       }
 
-     }
 
-   }
 
-   function _selectionWrap(composer, options) {
 
-     if (composer.selection.isCollapsed()) {
 
-         composer.selection.selectLine();
 
-     }
 
-     var surroundedNodes = composer.selection.surround(options);
 
-     for (var i = 0, imax = surroundedNodes.length; i < imax; i++) {
 
-       wysihtml5.dom.lineBreaks(surroundedNodes[i]).remove();
 
-       _removeLastChildIfLineBreak(surroundedNodes[i]);
 
-     }
 
-     // rethink restoring selection
 
-     // composer.selection.selectNode(element, wysihtml5.browser.displaysCaretInEmptyContentEditableCorrectly());
 
-   }
 
-   function _hasClasses(element) {
 
-     return !!wysihtml5.lang.string(element.className).trim();
 
-   }
 
-   function _hasStyles(element) {
 
-     return !!wysihtml5.lang.string(element.getAttribute('style') || '').trim();
 
-   }
 
-   wysihtml5.commands.formatBlock = {
 
-     exec: function(composer, command, nodeName, className, classRegExp, cssStyle, styleRegExp) {
 
-       var doc             = composer.doc,
 
-           blockElements    = this.state(composer, command, nodeName, className, classRegExp, cssStyle, styleRegExp),
 
-           useLineBreaks   = composer.config.useLineBreaks,
 
-           defaultNodeName = useLineBreaks ? "DIV" : "P",
 
-           selectedNodes, classRemoveAction, blockRenameFound, styleRemoveAction, blockElement;
 
-       nodeName = typeof(nodeName) === "string" ? nodeName.toUpperCase() : nodeName;
 
-       if (blockElements.length) {
 
-         composer.selection.executeAndRestoreRangy(function() {
 
-           for (var b = blockElements.length; b--;) {
 
-             if (classRegExp) {
 
-               classRemoveAction = _removeClass(blockElements[b], classRegExp);
 
-             }
 
-             if (styleRegExp) {
 
-               styleRemoveAction = _removeStyle(blockElements[b], styleRegExp);
 
-             }
 
-             if ((styleRemoveAction || classRemoveAction) && nodeName === null && blockElements[b].nodeName != defaultNodeName) {
 
-               // dont rename or remove element when just setting block formating class or style
 
-               return;
 
-             }
 
-             var hasClasses = _hasClasses(blockElements[b]),
 
-                 hasStyles = _hasStyles(blockElements[b]);
 
-             if (!hasClasses && !hasStyles && (useLineBreaks || nodeName === "P")) {
 
-               // Insert a line break afterwards and beforewards when there are siblings
 
-               // that are not of type line break or block element
 
-               wysihtml5.dom.lineBreaks(blockElements[b]).add();
 
-               dom.replaceWithChildNodes(blockElements[b]);
 
-             } else {
 
-               // Make sure that styling is kept by renaming the element to a <div> or <p> and copying over the class name
 
-               dom.renameElement(blockElements[b], nodeName === "P" ? "DIV" : defaultNodeName);
 
-             }
 
-           }
 
-         });
 
-         return;
 
-       }
 
-       // Find similiar block element and rename it (<h2 class="foo"></h2>  =>  <h1 class="foo"></h1>)
 
-       if (nodeName === null || wysihtml5.lang.array(BLOCK_ELEMENTS_GROUP).contains(nodeName)) {
 
-         selectedNodes = composer.selection.findNodesInSelection(BLOCK_ELEMENTS_GROUP).concat(composer.selection.getSelectedOwnNodes());
 
-         composer.selection.executeAndRestoreRangy(function() {
 
-           for (var n = selectedNodes.length; n--;) {
 
-             blockElement = dom.getParentElement(selectedNodes[n], {
 
-               nodeName: BLOCK_ELEMENTS_GROUP
 
-             });
 
-             if (blockElement == composer.element) {
 
-               blockElement = null;
 
-             }
 
-             if (blockElement) {
 
-                 // Rename current block element to new block element and add class
 
-                 if (nodeName) {
 
-                   blockElement = dom.renameElement(blockElement, nodeName);
 
-                 }
 
-                 if (className) {
 
-                   _addClass(blockElement, className, classRegExp);
 
-                 }
 
-                 if (cssStyle) {
 
-                   _addStyle(blockElement, cssStyle, styleRegExp);
 
-                 }
 
-               blockRenameFound = true;
 
-             }
 
-           }
 
-         });
 
-         if (blockRenameFound) {
 
-           return;
 
-         }
 
-       }
 
-       _selectionWrap(composer, {
 
-         "nodeName": (nodeName || defaultNodeName),
 
-         "className": className || null,
 
-         "cssStyle": cssStyle || null
 
-       });
 
-     },
 
-     state: function(composer, command, nodeName, className, classRegExp, cssStyle, styleRegExp) {
 
-       var nodes = composer.selection.getSelectedOwnNodes(),
 
-           parents = [],
 
-           parent;
 
-       nodeName = typeof(nodeName) === "string" ? nodeName.toUpperCase() : nodeName;
 
-       //var selectedNode = composer.selection.getSelectedNode();
 
-       for (var i = 0, maxi = nodes.length; i < maxi; i++) {
 
-         parent = dom.getParentElement(nodes[i], {
 
-           nodeName:     nodeName,
 
-           className:    className,
 
-           classRegExp:  classRegExp,
 
-           cssStyle:     cssStyle,
 
-           styleRegExp:  styleRegExp
 
-         });
 
-         if (parent && wysihtml5.lang.array(parents).indexOf(parent) == -1) {
 
-           parents.push(parent);
 
-         }
 
-       }
 
-       if (parents.length == 0) {
 
-         return false;
 
-       }
 
-       return parents;
 
-     }
 
-   };
 
- })(wysihtml5);
 
- ;/* Formats block for as a <pre><code class="classname"></code></pre> block
 
-  * Useful in conjuction for sytax highlight utility: highlight.js
 
-  *
 
-  * Usage:
 
-  *
 
-  * editorInstance.composer.commands.exec("formatCode", "language-html");
 
- */
 
- wysihtml5.commands.formatCode = {
 
-   exec: function(composer, command, classname) {
 
-     var pre = this.state(composer),
 
-         code, range, selectedNodes;
 
-     if (pre) {
 
-       // caret is already within a <pre><code>...</code></pre>
 
-       composer.selection.executeAndRestore(function() {
 
-         code = pre.querySelector("code");
 
-         wysihtml5.dom.replaceWithChildNodes(pre);
 
-         if (code) {
 
-           wysihtml5.dom.replaceWithChildNodes(code);
 
-         }
 
-       });
 
-     } else {
 
-       // Wrap in <pre><code>...</code></pre>
 
-       range = composer.selection.getRange();
 
-       selectedNodes = range.extractContents();
 
-       pre = composer.doc.createElement("pre");
 
-       code = composer.doc.createElement("code");
 
-       if (classname) {
 
-         code.className = classname;
 
-       }
 
-       pre.appendChild(code);
 
-       code.appendChild(selectedNodes);
 
-       range.insertNode(pre);
 
-       composer.selection.selectNode(pre);
 
-     }
 
-   },
 
-   state: function(composer) {
 
-     var selectedNode = composer.selection.getSelectedNode();
 
-     if (selectedNode && selectedNode.nodeName && selectedNode.nodeName == "PRE"&&
 
-         selectedNode.firstChild && selectedNode.firstChild.nodeName && selectedNode.firstChild.nodeName == "CODE") {
 
-       return selectedNode;
 
-     } else {
 
-       return wysihtml5.dom.getParentElement(selectedNode, { nodeName: "CODE" }) && wysihtml5.dom.getParentElement(selectedNode, { nodeName: "PRE" });
 
-     }
 
-   }
 
- };;/**
 
-  * formatInline scenarios for tag "B" (| = caret, |foo| = selected text)
 
-  *
 
-  *   #1 caret in unformatted text:
 
-  *      abcdefg|
 
-  *   output:
 
-  *      abcdefg<b>|</b>
 
-  *
 
-  *   #2 unformatted text selected:
 
-  *      abc|deg|h
 
-  *   output:
 
-  *      abc<b>|deg|</b>h
 
-  *
 
-  *   #3 unformatted text selected across boundaries:
 
-  *      ab|c <span>defg|h</span>
 
-  *   output:
 
-  *      ab<b>|c </b><span><b>defg</b>|h</span>
 
-  *
 
-  *   #4 formatted text entirely selected
 
-  *      <b>|abc|</b>
 
-  *   output:
 
-  *      |abc|
 
-  *
 
-  *   #5 formatted text partially selected
 
-  *      <b>ab|c|</b>
 
-  *   output:
 
-  *      <b>ab</b>|c|
 
-  *
 
-  *   #6 formatted text selected across boundaries
 
-  *      <span>ab|c</span> <b>de|fgh</b>
 
-  *   output:
 
-  *      <span>ab|c</span> de|<b>fgh</b>
 
-  */
 
- (function(wysihtml5) {
 
-   var // Treat <b> as <strong> and vice versa
 
-       ALIAS_MAPPING = {
 
-         "strong": "b",
 
-         "em":     "i",
 
-         "b":      "strong",
 
-         "i":      "em"
 
-       },
 
-       htmlApplier = {};
 
-   function _getTagNames(tagName) {
 
-     var alias = ALIAS_MAPPING[tagName];
 
-     return alias ? [tagName.toLowerCase(), alias.toLowerCase()] : [tagName.toLowerCase()];
 
-   }
 
-   function _getApplier(tagName, className, classRegExp, cssStyle, styleRegExp, container) {
 
-     var identifier = tagName;
 
-     
 
-     if (className) {
 
-       identifier += ":" + className;
 
-     }
 
-     if (cssStyle) {
 
-       identifier += ":" + cssStyle;
 
-     }
 
-     if (!htmlApplier[identifier]) {
 
-       htmlApplier[identifier] = new wysihtml5.selection.HTMLApplier(_getTagNames(tagName), className, classRegExp, true, cssStyle, styleRegExp, container);
 
-     }
 
-     return htmlApplier[identifier];
 
-   }
 
-   wysihtml5.commands.formatInline = {
 
-     exec: function(composer, command, tagName, className, classRegExp, cssStyle, styleRegExp, dontRestoreSelect, noCleanup) {
 
-       var range = composer.selection.createRange(),
 
-           ownRanges = composer.selection.getOwnRanges();
 
-       if (!ownRanges || ownRanges.length == 0) {
 
-         return false;
 
-       }
 
-       composer.selection.getSelection().removeAllRanges();
 
-       _getApplier(tagName, className, classRegExp, cssStyle, styleRegExp, composer.element).toggleRange(ownRanges);
 
-       if (!dontRestoreSelect) {
 
-         range.setStart(ownRanges[0].startContainer,  ownRanges[0].startOffset);
 
-         range.setEnd(
 
-           ownRanges[ownRanges.length - 1].endContainer,
 
-           ownRanges[ownRanges.length - 1].endOffset
 
-         );
 
-         composer.selection.setSelection(range);
 
-         composer.selection.executeAndRestore(function() {
 
-           if (!noCleanup) {
 
-             composer.cleanUp();
 
-           }
 
-         }, true, true);
 
-       } else if (!noCleanup) {
 
-         composer.cleanUp();
 
-       }
 
-     },
 
-     // Executes so that if collapsed caret is in a state and executing that state it should unformat that state
 
-     // It is achieved by selecting the entire state element before executing.
 
-     // This works on built in contenteditable inline format commands
 
-     execWithToggle: function(composer, command, tagName, className, classRegExp, cssStyle, styleRegExp) {
 
-       var that = this;
 
-       if (this.state(composer, command, tagName, className, classRegExp, cssStyle, styleRegExp) &&
 
-         composer.selection.isCollapsed() &&
 
-         !composer.selection.caretIsLastInSelection() &&
 
-         !composer.selection.caretIsFirstInSelection()
 
-       ) {
 
-         var state_element = that.state(composer, command, tagName, className, classRegExp)[0];
 
-         composer.selection.executeAndRestoreRangy(function() {
 
-           var parent = state_element.parentNode;
 
-           composer.selection.selectNode(state_element, true);
 
-           wysihtml5.commands.formatInline.exec(composer, command, tagName, className, classRegExp, cssStyle, styleRegExp, true, true);
 
-         });
 
-       } else {
 
-         if (this.state(composer, command, tagName, className, classRegExp, cssStyle, styleRegExp) && !composer.selection.isCollapsed()) {
 
-           composer.selection.executeAndRestoreRangy(function() {
 
-             wysihtml5.commands.formatInline.exec(composer, command, tagName, className, classRegExp, cssStyle, styleRegExp, true, true);
 
-           });
 
-         } else {
 
-           wysihtml5.commands.formatInline.exec(composer, command, tagName, className, classRegExp, cssStyle, styleRegExp);
 
-         }
 
-       }
 
-     },
 
-     state: function(composer, command, tagName, className, classRegExp, cssStyle, styleRegExp) {
 
-       var doc           = composer.doc,
 
-           aliasTagName  = ALIAS_MAPPING[tagName] || tagName,
 
-           ownRanges, isApplied;
 
-       // Check whether the document contains a node with the desired tagName
 
-       if (!wysihtml5.dom.hasElementWithTagName(doc, tagName) &&
 
-           !wysihtml5.dom.hasElementWithTagName(doc, aliasTagName)) {
 
-         return false;
 
-       }
 
-        // Check whether the document contains a node with the desired className
 
-       if (className && !wysihtml5.dom.hasElementWithClassName(doc, className)) {
 
-          return false;
 
-       }
 
-       ownRanges = composer.selection.getOwnRanges();
 
-       if (!ownRanges || ownRanges.length === 0) {
 
-         return false;
 
-       }
 
-       isApplied = _getApplier(tagName, className, classRegExp, cssStyle, styleRegExp, composer.element).isAppliedToRange(ownRanges);
 
-       return (isApplied && isApplied.elements) ? isApplied.elements : false;
 
-     }
 
-   };
 
- })(wysihtml5);
 
- ;(function(wysihtml5) {
 
-   wysihtml5.commands.insertBlockQuote = {
 
-     exec: function(composer, command) {
 
-       var state = this.state(composer, command),
 
-           endToEndParent = composer.selection.isEndToEndInNode(['H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'P']),
 
-           prevNode, nextNode;
 
-       composer.selection.executeAndRestore(function() {
 
-         if (state) {
 
-           if (composer.config.useLineBreaks) {
 
-              wysihtml5.dom.lineBreaks(state).add();
 
-           }
 
-           wysihtml5.dom.unwrap(state);
 
-         } else {
 
-           if (composer.selection.isCollapsed()) {
 
-             composer.selection.selectLine();
 
-           }
 
-           
 
-           if (endToEndParent) {
 
-             var qouteEl = endToEndParent.ownerDocument.createElement('blockquote');
 
-             wysihtml5.dom.insert(qouteEl).after(endToEndParent);
 
-             qouteEl.appendChild(endToEndParent);
 
-           } else {
 
-             composer.selection.surround({nodeName: "blockquote"});
 
-           }
 
-         }
 
-       });
 
-     },
 
-     state: function(composer, command) {
 
-       var selectedNode  = composer.selection.getSelectedNode(),
 
-           node = wysihtml5.dom.getParentElement(selectedNode, { nodeName: "BLOCKQUOTE" }, false, composer.element);
 
-       return (node) ? node : false;
 
-     }
 
-   };
 
- })(wysihtml5);;wysihtml5.commands.insertHTML = {
 
-   exec: function(composer, command, html) {
 
-     if (composer.commands.support(command)) {
 
-       composer.doc.execCommand(command, false, html);
 
-     } else {
 
-       composer.selection.insertHTML(html);
 
-     }
 
-   },
 
-   state: function() {
 
-     return false;
 
-   }
 
- };
 
- ;(function(wysihtml5) {
 
-   var NODE_NAME = "IMG";
 
-   wysihtml5.commands.insertImage = {
 
-     /**
 
-      * Inserts an <img>
 
-      * If selection is already an image link, it removes it
 
-      *
 
-      * @example
 
-      *    // either ...
 
-      *    wysihtml5.commands.insertImage.exec(composer, "insertImage", "http://www.google.de/logo.jpg");
 
-      *    // ... or ...
 
-      *    wysihtml5.commands.insertImage.exec(composer, "insertImage", { src: "http://www.google.de/logo.jpg", title: "foo" });
 
-      */
 
-     exec: function(composer, command, value) {
 
-       value = typeof(value) === "object" ? value : { src: value };
 
-       var doc     = composer.doc,
 
-           image   = this.state(composer),
 
-           textNode,
 
-           parent;
 
-       if (image) {
 
-         // Image already selected, set the caret before it and delete it
 
-         composer.selection.setBefore(image);
 
-         parent = image.parentNode;
 
-         parent.removeChild(image);
 
-         // and it's parent <a> too if it hasn't got any other relevant child nodes
 
-         wysihtml5.dom.removeEmptyTextNodes(parent);
 
-         if (parent.nodeName === "A" && !parent.firstChild) {
 
-           composer.selection.setAfter(parent);
 
-           parent.parentNode.removeChild(parent);
 
-         }
 
-         // firefox and ie sometimes don't remove the image handles, even though the image got removed
 
-         wysihtml5.quirks.redraw(composer.element);
 
-         return;
 
-       }
 
-       image = doc.createElement(NODE_NAME);
 
-       for (var i in value) {
 
-         image.setAttribute(i === "className" ? "class" : i, value[i]);
 
-       }
 
-       composer.selection.insertNode(image);
 
-       if (wysihtml5.browser.hasProblemsSettingCaretAfterImg()) {
 
-         textNode = doc.createTextNode(wysihtml5.INVISIBLE_SPACE);
 
-         composer.selection.insertNode(textNode);
 
-         composer.selection.setAfter(textNode);
 
-       } else {
 
-         composer.selection.setAfter(image);
 
-       }
 
-     },
 
-     state: function(composer) {
 
-       var doc = composer.doc,
 
-           selectedNode,
 
-           text,
 
-           imagesInSelection;
 
-       if (!wysihtml5.dom.hasElementWithTagName(doc, NODE_NAME)) {
 
-         return false;
 
-       }
 
-       selectedNode = composer.selection.getSelectedNode();
 
-       if (!selectedNode) {
 
-         return false;
 
-       }
 
-       if (selectedNode.nodeName === NODE_NAME) {
 
-         // This works perfectly in IE
 
-         return selectedNode;
 
-       }
 
-       if (selectedNode.nodeType !== wysihtml5.ELEMENT_NODE) {
 
-         return false;
 
-       }
 
-       text = composer.selection.getText();
 
-       text = wysihtml5.lang.string(text).trim();
 
-       if (text) {
 
-         return false;
 
-       }
 
-       imagesInSelection = composer.selection.getNodes(wysihtml5.ELEMENT_NODE, function(node) {
 
-         return node.nodeName === "IMG";
 
-       });
 
-       if (imagesInSelection.length !== 1) {
 
-         return false;
 
-       }
 
-       return imagesInSelection[0];
 
-     }
 
-   };
 
- })(wysihtml5);
 
- ;(function(wysihtml5) {
 
-   var LINE_BREAK = "<br>" + (wysihtml5.browser.needsSpaceAfterLineBreak() ? " " : "");
 
-   wysihtml5.commands.insertLineBreak = {
 
-     exec: function(composer, command) {
 
-       if (composer.commands.support(command)) {
 
-         composer.doc.execCommand(command, false, null);
 
-         if (!wysihtml5.browser.autoScrollsToCaret()) {
 
-           composer.selection.scrollIntoView();
 
-         }
 
-       } else {
 
-         composer.commands.exec("insertHTML", LINE_BREAK);
 
-       }
 
-     },
 
-     state: function() {
 
-       return false;
 
-     }
 
-   };
 
- })(wysihtml5);
 
- ;wysihtml5.commands.insertOrderedList = {
 
-   exec: function(composer, command) {
 
-     wysihtml5.commands.insertList.exec(composer, command, "OL");
 
-   },
 
-   state: function(composer, command) {
 
-     return wysihtml5.commands.insertList.state(composer, command, "OL");
 
-   }
 
- };
 
- ;wysihtml5.commands.insertUnorderedList = {
 
-   exec: function(composer, command) {
 
-     wysihtml5.commands.insertList.exec(composer, command, "UL");
 
-   },
 
-   state: function(composer, command) {
 
-     return wysihtml5.commands.insertList.state(composer, command, "UL");
 
-   }
 
- };
 
- ;wysihtml5.commands.insertList = (function(wysihtml5) {
 
-   var isNode = function(node, name) {
 
-     if (node && node.nodeName) {
 
-       if (typeof name === 'string') {
 
-         name = [name];
 
-       }
 
-       for (var n = name.length; n--;) {
 
-         if (node.nodeName === name[n]) {
 
-           return true;
 
-         }
 
-       }
 
-     }
 
-     return false;
 
-   };
 
-   var findListEl = function(node, nodeName, composer) {
 
-     var ret = {
 
-           el: null,
 
-           other: false
 
-         };
 
-     if (node) {
 
-       var parentLi = wysihtml5.dom.getParentElement(node, { nodeName: "LI" }),
 
-           otherNodeName = (nodeName === "UL") ? "OL" : "UL";
 
-       if (isNode(node, nodeName)) {
 
-         ret.el = node;
 
-       } else if (isNode(node, otherNodeName)) {
 
-         ret = {
 
-           el: node,
 
-           other: true
 
-         };
 
-       } else if (parentLi) {
 
-         if (isNode(parentLi.parentNode, nodeName)) {
 
-           ret.el = parentLi.parentNode;
 
-         } else if (isNode(parentLi.parentNode, otherNodeName)) {
 
-           ret = {
 
-             el : parentLi.parentNode,
 
-             other: true
 
-           };
 
-         }
 
-       }
 
-     }
 
-     // do not count list elements outside of composer
 
-     if (ret.el && !composer.element.contains(ret.el)) {
 
-       ret.el = null;
 
-     }
 
-     return ret;
 
-   };
 
-   var handleSameTypeList = function(el, nodeName, composer) {
 
-     var otherNodeName = (nodeName === "UL") ? "OL" : "UL",
 
-         otherLists, innerLists;
 
-     // Unwrap list
 
-     // <ul><li>foo</li><li>bar</li></ul>
 
-     // becomes:
 
-     // foo<br>bar<br>
 
-     composer.selection.executeAndRestore(function() {
 
-       var otherLists = getListsInSelection(otherNodeName, composer);
 
-       if (otherLists.length) {
 
-         for (var l = otherLists.length; l--;) {
 
-           wysihtml5.dom.renameElement(otherLists[l], nodeName.toLowerCase());
 
-         }
 
-       } else {
 
-         innerLists = getListsInSelection(['OL', 'UL'], composer);
 
-         for (var i = innerLists.length; i--;) {
 
-           wysihtml5.dom.resolveList(innerLists[i], composer.config.useLineBreaks);
 
-         }
 
-         wysihtml5.dom.resolveList(el, composer.config.useLineBreaks);
 
-       }
 
-     });
 
-   };
 
-   var handleOtherTypeList =  function(el, nodeName, composer) {
 
-     var otherNodeName = (nodeName === "UL") ? "OL" : "UL";
 
-     // Turn an ordered list into an unordered list
 
-     // <ol><li>foo</li><li>bar</li></ol>
 
-     // becomes:
 
-     // <ul><li>foo</li><li>bar</li></ul>
 
-     // Also rename other lists in selection
 
-     composer.selection.executeAndRestore(function() {
 
-       var renameLists = [el].concat(getListsInSelection(otherNodeName, composer));
 
-       // All selection inner lists get renamed too
 
-       for (var l = renameLists.length; l--;) {
 
-         wysihtml5.dom.renameElement(renameLists[l], nodeName.toLowerCase());
 
-       }
 
-     });
 
-   };
 
-   var getListsInSelection = function(nodeName, composer) {
 
-       var ranges = composer.selection.getOwnRanges(),
 
-           renameLists = [];
 
-       for (var r = ranges.length; r--;) {
 
-         renameLists = renameLists.concat(ranges[r].getNodes([1], function(node) {
 
-           return isNode(node, nodeName);
 
-         }));
 
-       }
 
-       return renameLists;
 
-   };
 
-   var createListFallback = function(nodeName, composer) {
 
-     // Fallback for Create list
 
-     composer.selection.executeAndRestoreRangy(function() {
 
-       var tempClassName =  "_wysihtml5-temp-" + new Date().getTime(),
 
-           tempElement = composer.selection.deblockAndSurround({
 
-             "nodeName": "div",
 
-             "className": tempClassName
 
-           }),
 
-           isEmpty, list;
 
-       // This space causes new lists to never break on enter 
 
-       var INVISIBLE_SPACE_REG_EXP = /\uFEFF/g;
 
-       tempElement.innerHTML = tempElement.innerHTML.replace(INVISIBLE_SPACE_REG_EXP, "");
 
-       
 
-       if (tempElement) {
 
-         isEmpty = wysihtml5.lang.array(["", "<br>", wysihtml5.INVISIBLE_SPACE]).contains(tempElement.innerHTML);
 
-         list = wysihtml5.dom.convertToList(tempElement, nodeName.toLowerCase(), composer.parent.config.uneditableContainerClassname);
 
-         if (isEmpty) {
 
-           composer.selection.selectNode(list.querySelector("li"), true);
 
-         }
 
-       }
 
-     });
 
-   };
 
-   return {
 
-     exec: function(composer, command, nodeName) {
 
-       var doc           = composer.doc,
 
-           cmd           = (nodeName === "OL") ? "insertOrderedList" : "insertUnorderedList",
 
-           selectedNode  = composer.selection.getSelectedNode(),
 
-           list          = findListEl(selectedNode, nodeName, composer);
 
-       if (!list.el) {
 
-         if (composer.commands.support(cmd)) {
 
-           doc.execCommand(cmd, false, null);
 
-         } else {
 
-           createListFallback(nodeName, composer);
 
-         }
 
-       } else if (list.other) {
 
-         handleOtherTypeList(list.el, nodeName, composer);
 
-       } else {
 
-         handleSameTypeList(list.el, nodeName, composer);
 
-       }
 
-     },
 
-     state: function(composer, command, nodeName) {
 
-       var selectedNode = composer.selection.getSelectedNode(),
 
-           list         = findListEl(selectedNode, nodeName, composer);
 
-       return (list.el && !list.other) ? list.el : false;
 
-     }
 
-   };
 
- })(wysihtml5);;wysihtml5.commands.italic = {
 
-   exec: function(composer, command) {
 
-     wysihtml5.commands.formatInline.execWithToggle(composer, command, "i");
 
-   },
 
-   state: function(composer, command) {
 
-     // element.ownerDocument.queryCommandState("italic") results:
 
-     // firefox: only <i>
 
-     // chrome:  <i>, <em>, <blockquote>, ...
 
-     // ie:      <i>, <em>
 
-     // opera:   only <i>
 
-     return wysihtml5.commands.formatInline.state(composer, command, "i");
 
-   }
 
- };
 
- ;(function(wysihtml5) {
 
-   var CLASS_NAME  = "wysiwyg-text-align-center",
 
-       REG_EXP     = /wysiwyg-text-align-[0-9a-z]+/g;
 
-   wysihtml5.commands.justifyCenter = {
 
-     exec: function(composer, command) {
 
-       return wysihtml5.commands.formatBlock.exec(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
 
-     },
 
-     state: function(composer, command) {
 
-       return wysihtml5.commands.formatBlock.state(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
 
-     }
 
-   };
 
- })(wysihtml5);
 
- ;(function(wysihtml5) {
 
-   var CLASS_NAME  = "wysiwyg-text-align-left",
 
-       REG_EXP     = /wysiwyg-text-align-[0-9a-z]+/g;
 
-   wysihtml5.commands.justifyLeft = {
 
-     exec: function(composer, command) {
 
-       return wysihtml5.commands.formatBlock.exec(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
 
-     },
 
-     state: function(composer, command) {
 
-       return wysihtml5.commands.formatBlock.state(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
 
-     }
 
-   };
 
- })(wysihtml5);
 
- ;(function(wysihtml5) {
 
-   var CLASS_NAME  = "wysiwyg-text-align-right",
 
-       REG_EXP     = /wysiwyg-text-align-[0-9a-z]+/g;
 
-   wysihtml5.commands.justifyRight = {
 
-     exec: function(composer, command) {
 
-       return wysihtml5.commands.formatBlock.exec(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
 
-     },
 
-     state: function(composer, command) {
 
-       return wysihtml5.commands.formatBlock.state(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
 
-     }
 
-   };
 
- })(wysihtml5);
 
- ;(function(wysihtml5) {
 
-   var CLASS_NAME  = "wysiwyg-text-align-justify",
 
-       REG_EXP     = /wysiwyg-text-align-[0-9a-z]+/g;
 
-   wysihtml5.commands.justifyFull = {
 
-     exec: function(composer, command) {
 
-       return wysihtml5.commands.formatBlock.exec(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
 
-     },
 
-     state: function(composer, command) {
 
-       return wysihtml5.commands.formatBlock.state(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
 
-     }
 
-   };
 
- })(wysihtml5);
 
- ;(function(wysihtml5) {
 
-   var STYLE_STR  = "text-align: right;",
 
-       REG_EXP = /(\s|^)text-align\s*:\s*[^;\s]+;?/gi;
 
-   wysihtml5.commands.alignRightStyle = {
 
-     exec: function(composer, command) {
 
-       return wysihtml5.commands.formatBlock.exec(composer, "formatBlock", null, null, null, STYLE_STR, REG_EXP);
 
-     },
 
-     state: function(composer, command) {
 
-       return wysihtml5.commands.formatBlock.state(composer, "formatBlock", null, null, null, STYLE_STR, REG_EXP);
 
-     }
 
-   };
 
- })(wysihtml5);
 
- ;(function(wysihtml5) {
 
-   var STYLE_STR  = "text-align: left;",
 
-       REG_EXP = /(\s|^)text-align\s*:\s*[^;\s]+;?/gi;
 
-   wysihtml5.commands.alignLeftStyle = {
 
-     exec: function(composer, command) {
 
-       return wysihtml5.commands.formatBlock.exec(composer, "formatBlock", null, null, null, STYLE_STR, REG_EXP);
 
-     },
 
-     state: function(composer, command) {
 
-       return wysihtml5.commands.formatBlock.state(composer, "formatBlock", null, null, null, STYLE_STR, REG_EXP);
 
-     }
 
-   };
 
- })(wysihtml5);
 
- ;(function(wysihtml5) {
 
-   var STYLE_STR  = "text-align: center;",
 
-       REG_EXP = /(\s|^)text-align\s*:\s*[^;\s]+;?/gi;
 
-   wysihtml5.commands.alignCenterStyle = {
 
-     exec: function(composer, command) {
 
-       return wysihtml5.commands.formatBlock.exec(composer, "formatBlock", null, null, null, STYLE_STR, REG_EXP);
 
-     },
 
-     state: function(composer, command) {
 
-       return wysihtml5.commands.formatBlock.state(composer, "formatBlock", null, null, null, STYLE_STR, REG_EXP);
 
-     }
 
-   };
 
- })(wysihtml5);
 
- ;wysihtml5.commands.redo = {
 
-   exec: function(composer) {
 
-     return composer.undoManager.redo();
 
-   },
 
-   state: function(composer) {
 
-     return false;
 
-   }
 
- };
 
- ;wysihtml5.commands.underline = {
 
-   exec: function(composer, command) {
 
-     wysihtml5.commands.formatInline.execWithToggle(composer, command, "u");
 
-   },
 
-   state: function(composer, command) {
 
-     return wysihtml5.commands.formatInline.state(composer, command, "u");
 
-   }
 
- };
 
- ;wysihtml5.commands.undo = {
 
-   exec: function(composer) {
 
-     return composer.undoManager.undo();
 
-   },
 
-   state: function(composer) {
 
-     return false;
 
-   }
 
- };
 
- ;wysihtml5.commands.createTable = {
 
-   exec: function(composer, command, value) {
 
-       var col, row, html;
 
-       if (value && value.cols && value.rows && parseInt(value.cols, 10) > 0 && parseInt(value.rows, 10) > 0) {
 
-           if (value.tableStyle) {
 
-             html = "<table style=\"" + value.tableStyle + "\">";
 
-           } else {
 
-             html = "<table>";
 
-           }
 
-           html += "<tbody>";
 
-           for (row = 0; row < value.rows; row ++) {
 
-               html += '<tr>';
 
-               for (col = 0; col < value.cols; col ++) {
 
-                   html += "<td> </td>";
 
-               }
 
-               html += '</tr>';
 
-           }
 
-           html += "</tbody></table>";
 
-           composer.commands.exec("insertHTML", html);
 
-           //composer.selection.insertHTML(html);
 
-       }
 
-   },
 
-   state: function(composer, command) {
 
-       return false;
 
-   }
 
- };
 
- ;wysihtml5.commands.mergeTableCells = {
 
-   exec: function(composer, command) {
 
-       if (composer.tableSelection && composer.tableSelection.start && composer.tableSelection.end) {
 
-           if (this.state(composer, command)) {
 
-               wysihtml5.dom.table.unmergeCell(composer.tableSelection.start);
 
-           } else {
 
-               wysihtml5.dom.table.mergeCellsBetween(composer.tableSelection.start, composer.tableSelection.end);
 
-           }
 
-       }
 
-   },
 
-   state: function(composer, command) {
 
-       if (composer.tableSelection) {
 
-           var start = composer.tableSelection.start,
 
-               end = composer.tableSelection.end;
 
-           if (start && end && start == end &&
 
-               ((
 
-                   wysihtml5.dom.getAttribute(start, "colspan") &&
 
-                   parseInt(wysihtml5.dom.getAttribute(start, "colspan"), 10) > 1
 
-               ) || (
 
-                   wysihtml5.dom.getAttribute(start, "rowspan") &&
 
-                   parseInt(wysihtml5.dom.getAttribute(start, "rowspan"), 10) > 1
 
-               ))
 
-           ) {
 
-               return [start];
 
-           }
 
-       }
 
-       return false;
 
-   }
 
- };
 
- ;wysihtml5.commands.addTableCells = {
 
-   exec: function(composer, command, value) {
 
-       if (composer.tableSelection && composer.tableSelection.start && composer.tableSelection.end) {
 
-           // switches start and end if start is bigger than end (reverse selection)
 
-           var tableSelect = wysihtml5.dom.table.orderSelectionEnds(composer.tableSelection.start, composer.tableSelection.end);
 
-           if (value == "before" || value == "above") {
 
-               wysihtml5.dom.table.addCells(tableSelect.start, value);
 
-           } else if (value == "after" || value == "below") {
 
-               wysihtml5.dom.table.addCells(tableSelect.end, value);
 
-           }
 
-           setTimeout(function() {
 
-               composer.tableSelection.select(tableSelect.start, tableSelect.end);
 
-           },0);
 
-       }
 
-   },
 
-   state: function(composer, command) {
 
-       return false;
 
-   }
 
- };
 
- ;wysihtml5.commands.deleteTableCells = {
 
-   exec: function(composer, command, value) {
 
-       if (composer.tableSelection && composer.tableSelection.start && composer.tableSelection.end) {
 
-           var tableSelect = wysihtml5.dom.table.orderSelectionEnds(composer.tableSelection.start, composer.tableSelection.end),
 
-               idx = wysihtml5.dom.table.indexOf(tableSelect.start),
 
-               selCell,
 
-               table = composer.tableSelection.table;
 
-           wysihtml5.dom.table.removeCells(tableSelect.start, value);
 
-           setTimeout(function() {
 
-               // move selection to next or previous if not present
 
-               selCell = wysihtml5.dom.table.findCell(table, idx);
 
-               if (!selCell){
 
-                   if (value == "row") {
 
-                       selCell = wysihtml5.dom.table.findCell(table, {
 
-                           "row": idx.row - 1,
 
-                           "col": idx.col
 
-                       });
 
-                   }
 
-                   if (value == "column") {
 
-                       selCell = wysihtml5.dom.table.findCell(table, {
 
-                           "row": idx.row,
 
-                           "col": idx.col - 1
 
-                       });
 
-                   }
 
-               }
 
-               if (selCell) {
 
-                   composer.tableSelection.select(selCell, selCell);
 
-               }
 
-           }, 0);
 
-       }
 
-   },
 
-   state: function(composer, command) {
 
-       return false;
 
-   }
 
- };
 
- ;wysihtml5.commands.indentList = {
 
-   exec: function(composer, command, value) {
 
-     var listEls = composer.selection.getSelectionParentsByTag('LI');
 
-     if (listEls) {
 
-       return this.tryToPushLiLevel(listEls, composer.selection);
 
-     }
 
-     return false;
 
-   },
 
-   state: function(composer, command) {
 
-       return false;
 
-   },
 
-   tryToPushLiLevel: function(liNodes, selection) {
 
-     var listTag, list, prevLi, liNode, prevLiList,
 
-         found = false;
 
-     selection.executeAndRestoreRangy(function() {
 
-       for (var i = liNodes.length; i--;) {
 
-         liNode = liNodes[i];
 
-         listTag = (liNode.parentNode.nodeName === 'OL') ? 'OL' : 'UL';
 
-         list = liNode.ownerDocument.createElement(listTag);
 
-         prevLi = wysihtml5.dom.domNode(liNode).prev({nodeTypes: [wysihtml5.ELEMENT_NODE]});
 
-         prevLiList = (prevLi) ? prevLi.querySelector('ul, ol') : null;
 
-         if (prevLi) {
 
-           if (prevLiList) {
 
-             prevLiList.appendChild(liNode);
 
-           } else {
 
-             list.appendChild(liNode);
 
-             prevLi.appendChild(list);
 
-           }
 
-           found = true;
 
-         }
 
-       }
 
-     });
 
-     return found;
 
-   }
 
- };
 
- ;wysihtml5.commands.outdentList = {
 
-   exec: function(composer, command, value) {
 
-     var listEls = composer.selection.getSelectionParentsByTag('LI');
 
-     if (listEls) {
 
-       return this.tryToPullLiLevel(listEls, composer);
 
-     }
 
-     return false;
 
-   },
 
-   state: function(composer, command) {
 
-       return false;
 
-   },
 
-   tryToPullLiLevel: function(liNodes, composer) {
 
-     var listNode, outerListNode, outerLiNode, list, prevLi, liNode, afterList,
 
-         found = false,
 
-         that = this;
 
-     composer.selection.executeAndRestoreRangy(function() {
 
-       for (var i = liNodes.length; i--;) {
 
-         liNode = liNodes[i];
 
-         if (liNode.parentNode) {
 
-           listNode = liNode.parentNode;
 
-           if (listNode.tagName === 'OL' || listNode.tagName === 'UL') {
 
-             found = true;
 
-             outerListNode = wysihtml5.dom.getParentElement(listNode.parentNode, { nodeName: ['OL', 'UL']}, false, composer.element);
 
-             outerLiNode = wysihtml5.dom.getParentElement(listNode.parentNode, { nodeName: ['LI']}, false, composer.element);
 
-             if (outerListNode && outerLiNode) {
 
-               if (liNode.nextSibling) {
 
-                 afterList = that.getAfterList(listNode, liNode);
 
-                 liNode.appendChild(afterList);
 
-               }
 
-               outerListNode.insertBefore(liNode, outerLiNode.nextSibling);
 
-             } else {
 
-               if (liNode.nextSibling) {
 
-                 afterList = that.getAfterList(listNode, liNode);
 
-                 liNode.appendChild(afterList);
 
-               }
 
-               for (var j = liNode.childNodes.length; j--;) {
 
-                 listNode.parentNode.insertBefore(liNode.childNodes[j], listNode.nextSibling);
 
-               }
 
-               listNode.parentNode.insertBefore(document.createElement('br'), listNode.nextSibling);
 
-               liNode.parentNode.removeChild(liNode);
 
-             }
 
-             // cleanup
 
-             if (listNode.childNodes.length === 0) {
 
-                 listNode.parentNode.removeChild(listNode);
 
-             }
 
-           }
 
-         }
 
-       }
 
-     });
 
-     return found;
 
-   },
 
-   getAfterList: function(listNode, liNode) {
 
-     var nodeName = listNode.nodeName,
 
-         newList = document.createElement(nodeName);
 
-     while (liNode.nextSibling) {
 
-       newList.appendChild(liNode.nextSibling);
 
-     }
 
-     return newList;
 
-   }
 
- };;/**
 
-  * Undo Manager for wysihtml5
 
-  * slightly inspired by http://rniwa.com/editing/undomanager.html#the-undomanager-interface
 
-  */
 
- (function(wysihtml5) {
 
-   var Z_KEY               = 90,
 
-       Y_KEY               = 89,
 
-       BACKSPACE_KEY       = 8,
 
-       DELETE_KEY          = 46,
 
-       MAX_HISTORY_ENTRIES = 25,
 
-       DATA_ATTR_NODE      = "data-wysihtml5-selection-node",
 
-       DATA_ATTR_OFFSET    = "data-wysihtml5-selection-offset",
 
-       UNDO_HTML           = '<span id="_wysihtml5-undo" class="_wysihtml5-temp">' + wysihtml5.INVISIBLE_SPACE + '</span>',
 
-       REDO_HTML           = '<span id="_wysihtml5-redo" class="_wysihtml5-temp">' + wysihtml5.INVISIBLE_SPACE + '</span>',
 
-       dom                 = wysihtml5.dom;
 
-   function cleanTempElements(doc) {
 
-     var tempElement;
 
-     while (tempElement = doc.querySelector("._wysihtml5-temp")) {
 
-       tempElement.parentNode.removeChild(tempElement);
 
-     }
 
-   }
 
-   wysihtml5.UndoManager = wysihtml5.lang.Dispatcher.extend(
 
-     /** @scope wysihtml5.UndoManager.prototype */ {
 
-     constructor: function(editor) {
 
-       this.editor = editor;
 
-       this.composer = editor.composer;
 
-       this.element = this.composer.element;
 
-       this.position = 0;
 
-       this.historyStr = [];
 
-       this.historyDom = [];
 
-       this.transact();
 
-       this._observe();
 
-     },
 
-     _observe: function() {
 
-       var that      = this,
 
-           doc       = this.composer.sandbox.getDocument(),
 
-           lastKey;
 
-       // Catch CTRL+Z and CTRL+Y
 
-       dom.observe(this.element, "keydown", function(event) {
 
-         if (event.altKey || (!event.ctrlKey && !event.metaKey)) {
 
-           return;
 
-         }
 
-         var keyCode = event.keyCode,
 
-             isUndo = keyCode === Z_KEY && !event.shiftKey,
 
-             isRedo = (keyCode === Z_KEY && event.shiftKey) || (keyCode === Y_KEY);
 
-         if (isUndo) {
 
-           that.undo();
 
-           event.preventDefault();
 
-         } else if (isRedo) {
 
-           that.redo();
 
-           event.preventDefault();
 
-         }
 
-       });
 
-       // Catch delete and backspace
 
-       dom.observe(this.element, "keydown", function(event) {
 
-         var keyCode = event.keyCode;
 
-         if (keyCode === lastKey) {
 
-           return;
 
-         }
 
-         lastKey = keyCode;
 
-         if (keyCode === BACKSPACE_KEY || keyCode === DELETE_KEY) {
 
-           that.transact();
 
-         }
 
-       });
 
-       this.editor
 
-         .on("newword:composer", function() {
 
-           that.transact();
 
-         })
 
-         .on("beforecommand:composer", function() {
 
-           that.transact();
 
-         });
 
-     },
 
-     transact: function() {
 
-       var previousHtml      = this.historyStr[this.position - 1],
 
-           currentHtml       = this.composer.getValue(false, false),
 
-           composerIsVisible   = this.element.offsetWidth > 0 && this.element.offsetHeight > 0,
 
-           range, node, offset, element, position;
 
-       if (currentHtml === previousHtml) {
 
-         return;
 
-       }
 
-       var length = this.historyStr.length = this.historyDom.length = this.position;
 
-       if (length > MAX_HISTORY_ENTRIES) {
 
-         this.historyStr.shift();
 
-         this.historyDom.shift();
 
-         this.position--;
 
-       }
 
-       this.position++;
 
-       if (composerIsVisible) {
 
-         // Do not start saving selection if composer is not visible
 
-         range   = this.composer.selection.getRange();
 
-         node    = (range && range.startContainer) ? range.startContainer : this.element;
 
-         offset  = (range && range.startOffset) ? range.startOffset : 0;
 
-         if (node.nodeType === wysihtml5.ELEMENT_NODE) {
 
-           element = node;
 
-         } else {
 
-           element  = node.parentNode;
 
-           position = this.getChildNodeIndex(element, node);
 
-         }
 
-         element.setAttribute(DATA_ATTR_OFFSET, offset);
 
-         if (typeof(position) !== "undefined") {
 
-           element.setAttribute(DATA_ATTR_NODE, position);
 
-         }
 
-       }
 
-       var clone = this.element.cloneNode(!!currentHtml);
 
-       this.historyDom.push(clone);
 
-       this.historyStr.push(currentHtml);
 
-       if (element) {
 
-         element.removeAttribute(DATA_ATTR_OFFSET);
 
-         element.removeAttribute(DATA_ATTR_NODE);
 
-       }
 
-     },
 
-     undo: function() {
 
-       this.transact();
 
-       if (!this.undoPossible()) {
 
-         return;
 
-       }
 
-       this.set(this.historyDom[--this.position - 1]);
 
-       this.editor.fire("undo:composer");
 
-     },
 
-     redo: function() {
 
-       if (!this.redoPossible()) {
 
-         return;
 
-       }
 
-       this.set(this.historyDom[++this.position - 1]);
 
-       this.editor.fire("redo:composer");
 
-     },
 
-     undoPossible: function() {
 
-       return this.position > 1;
 
-     },
 
-     redoPossible: function() {
 
-       return this.position < this.historyStr.length;
 
-     },
 
-     set: function(historyEntry) {
 
-       this.element.innerHTML = "";
 
-       var i = 0,
 
-           childNodes = historyEntry.childNodes,
 
-           length = historyEntry.childNodes.length;
 
-       for (; i<length; i++) {
 
-         this.element.appendChild(childNodes[i].cloneNode(true));
 
-       }
 
-       // Restore selection
 
-       var offset,
 
-           node,
 
-           position;
 
-       if (historyEntry.hasAttribute(DATA_ATTR_OFFSET)) {
 
-         offset    = historyEntry.getAttribute(DATA_ATTR_OFFSET);
 
-         position  = historyEntry.getAttribute(DATA_ATTR_NODE);
 
-         node      = this.element;
 
-       } else {
 
-         node      = this.element.querySelector("[" + DATA_ATTR_OFFSET + "]") || this.element;
 
-         offset    = node.getAttribute(DATA_ATTR_OFFSET);
 
-         position  = node.getAttribute(DATA_ATTR_NODE);
 
-         node.removeAttribute(DATA_ATTR_OFFSET);
 
-         node.removeAttribute(DATA_ATTR_NODE);
 
-       }
 
-       if (position !== null) {
 
-         node = this.getChildNodeByIndex(node, +position);
 
-       }
 
-       this.composer.selection.set(node, offset);
 
-     },
 
-     getChildNodeIndex: function(parent, child) {
 
-       var i           = 0,
 
-           childNodes  = parent.childNodes,
 
-           length      = childNodes.length;
 
-       for (; i<length; i++) {
 
-         if (childNodes[i] === child) {
 
-           return i;
 
-         }
 
-       }
 
-     },
 
-     getChildNodeByIndex: function(parent, index) {
 
-       return parent.childNodes[index];
 
-     }
 
-   });
 
- })(wysihtml5);
 
- ;/**
 
-  * TODO: the following methods still need unit test coverage
 
-  */
 
- wysihtml5.views.View = Base.extend(
 
-   /** @scope wysihtml5.views.View.prototype */ {
 
-   constructor: function(parent, textareaElement, config) {
 
-     this.parent   = parent;
 
-     this.element  = textareaElement;
 
-     this.config   = config;
 
-     if (!this.config.noTextarea) {
 
-         this._observeViewChange();
 
-     }
 
-   },
 
-   _observeViewChange: function() {
 
-     var that = this;
 
-     this.parent.on("beforeload", function() {
 
-       that.parent.on("change_view", function(view) {
 
-         if (view === that.name) {
 
-           that.parent.currentView = that;
 
-           that.show();
 
-           // Using tiny delay here to make sure that the placeholder is set before focusing
 
-           setTimeout(function() { that.focus(); }, 0);
 
-         } else {
 
-           that.hide();
 
-         }
 
-       });
 
-     });
 
-   },
 
-   focus: function() {
 
-     if (this.element.ownerDocument.querySelector(":focus") === this.element) {
 
-       return;
 
-     }
 
-     try { this.element.focus(); } catch(e) {}
 
-   },
 
-   hide: function() {
 
-     this.element.style.display = "none";
 
-   },
 
-   show: function() {
 
-     this.element.style.display = "";
 
-   },
 
-   disable: function() {
 
-     this.element.setAttribute("disabled", "disabled");
 
-   },
 
-   enable: function() {
 
-     this.element.removeAttribute("disabled");
 
-   }
 
- });
 
- ;(function(wysihtml5) {
 
-   var dom       = wysihtml5.dom,
 
-       browser   = wysihtml5.browser;
 
-   wysihtml5.views.Composer = wysihtml5.views.View.extend(
 
-     /** @scope wysihtml5.views.Composer.prototype */ {
 
-     name: "composer",
 
-     // Needed for firefox in order to display a proper caret in an empty contentEditable
 
-     CARET_HACK: "<br>",
 
-     constructor: function(parent, editableElement, config) {
 
-       this.base(parent, editableElement, config);
 
-       if (!this.config.noTextarea) {
 
-           this.textarea = this.parent.textarea;
 
-       } else {
 
-           this.editableArea = editableElement;
 
-       }
 
-       if (this.config.contentEditableMode) {
 
-           this._initContentEditableArea();
 
-       } else {
 
-           this._initSandbox();
 
-       }
 
-     },
 
-     clear: function() {
 
-       this.element.innerHTML = browser.displaysCaretInEmptyContentEditableCorrectly() ? "" : this.CARET_HACK;
 
-     },
 
-     getValue: function(parse, clearInternals) {
 
-       var value = this.isEmpty() ? "" : wysihtml5.quirks.getCorrectInnerHTML(this.element);
 
-       if (parse !== false) {
 
-         value = this.parent.parse(value, (clearInternals === false) ? false : true);
 
-       }
 
-       return value;
 
-     },
 
-     setValue: function(html, parse) {
 
-       if (parse) {
 
-         html = this.parent.parse(html);
 
-       }
 
-       try {
 
-         this.element.innerHTML = html;
 
-       } catch (e) {
 
-         this.element.innerText = html;
 
-       }
 
-     },
 
-     cleanUp: function() {
 
-         this.parent.parse(this.element);
 
-     },
 
-     show: function() {
 
-       this.editableArea.style.display = this._displayStyle || "";
 
-       if (!this.config.noTextarea && !this.textarea.element.disabled) {
 
-         // Firefox needs this, otherwise contentEditable becomes uneditable
 
-         this.disable();
 
-         this.enable();
 
-       }
 
-     },
 
-     hide: function() {
 
-       this._displayStyle = dom.getStyle("display").from(this.editableArea);
 
-       if (this._displayStyle === "none") {
 
-         this._displayStyle = null;
 
-       }
 
-       this.editableArea.style.display = "none";
 
-     },
 
-     disable: function() {
 
-       this.parent.fire("disable:composer");
 
-       this.element.removeAttribute("contentEditable");
 
-     },
 
-     enable: function() {
 
-       this.parent.fire("enable:composer");
 
-       this.element.setAttribute("contentEditable", "true");
 
-     },
 
-     focus: function(setToEnd) {
 
-       // IE 8 fires the focus event after .focus()
 
-       // This is needed by our simulate_placeholder.js to work
 
-       // therefore we clear it ourselves this time
 
-       if (wysihtml5.browser.doesAsyncFocus() && this.hasPlaceholderSet()) {
 
-         this.clear();
 
-       }
 
-       this.base();
 
-       var lastChild = this.element.lastChild;
 
-       if (setToEnd && lastChild && this.selection) {
 
-         if (lastChild.nodeName === "BR") {
 
-           this.selection.setBefore(this.element.lastChild);
 
-         } else {
 
-           this.selection.setAfter(this.element.lastChild);
 
-         }
 
-       }
 
-     },
 
-     getTextContent: function() {
 
-       return dom.getTextContent(this.element);
 
-     },
 
-     hasPlaceholderSet: function() {
 
-       return this.getTextContent() == ((this.config.noTextarea) ? this.editableArea.getAttribute("data-placeholder") : this.textarea.element.getAttribute("placeholder")) && this.placeholderSet;
 
-     },
 
-     isEmpty: function() {
 
-       var innerHTML = this.element.innerHTML.toLowerCase();
 
-       return (/^(\s|<br>|<\/br>|<p>|<\/p>)*$/i).test(innerHTML)  ||
 
-              innerHTML === ""            ||
 
-              innerHTML === "<br>"        ||
 
-              innerHTML === "<p></p>"     ||
 
-              innerHTML === "<p><br></p>" ||
 
-              this.hasPlaceholderSet();
 
-     },
 
-     _initContentEditableArea: function() {
 
-         var that = this;
 
-         if (this.config.noTextarea) {
 
-             this.sandbox = new dom.ContentEditableArea(function() {
 
-                 that._create();
 
-             }, {}, this.editableArea);
 
-         } else {
 
-             this.sandbox = new dom.ContentEditableArea(function() {
 
-                 that._create();
 
-             });
 
-             this.editableArea = this.sandbox.getContentEditable();
 
-             dom.insert(this.editableArea).after(this.textarea.element);
 
-             this._createWysiwygFormField();
 
-         }
 
-     },
 
-     _initSandbox: function() {
 
-       var that = this;
 
-       this.sandbox = new dom.Sandbox(function() {
 
-         that._create();
 
-       }, {
 
-         stylesheets:  this.config.stylesheets
 
-       });
 
-       this.editableArea  = this.sandbox.getIframe();
 
-       var textareaElement = this.textarea.element;
 
-       dom.insert(this.editableArea).after(textareaElement);
 
-       this._createWysiwygFormField();
 
-     },
 
-     // Creates hidden field which tells the server after submit, that the user used an wysiwyg editor
 
-     _createWysiwygFormField: function() {
 
-         if (this.textarea.element.form) {
 
-           var hiddenField = document.createElement("input");
 
-           hiddenField.type   = "hidden";
 
-           hiddenField.name   = "_wysihtml5_mode";
 
-           hiddenField.value  = 1;
 
-           dom.insert(hiddenField).after(this.textarea.element);
 
-         }
 
-     },
 
-     _create: function() {
 
-       var that = this;
 
-       this.doc                = this.sandbox.getDocument();
 
-       this.element            = (this.config.contentEditableMode) ? this.sandbox.getContentEditable() : this.doc.body;
 
-       if (!this.config.noTextarea) {
 
-           this.textarea           = this.parent.textarea;
 
-           this.element.innerHTML  = this.textarea.getValue(true, false);
 
-       } else {
 
-           this.cleanUp(); // cleans contenteditable on initiation as it may contain html
 
-       }
 
-       // Make sure our selection handler is ready
 
-       this.selection = new wysihtml5.Selection(this.parent, this.element, this.config.uneditableContainerClassname);
 
-       // Make sure commands dispatcher is ready
 
-       this.commands  = new wysihtml5.Commands(this.parent);
 
-       if (!this.config.noTextarea) {
 
-           dom.copyAttributes([
 
-               "className", "spellcheck", "title", "lang", "dir", "accessKey"
 
-           ]).from(this.textarea.element).to(this.element);
 
-       }
 
-       dom.addClass(this.element, this.config.composerClassName);
 
-       //
 
-       // Make the editor look like the original textarea, by syncing styles
 
-       if (this.config.style && !this.config.contentEditableMode) {
 
-         this.style();
 
-       }
 
-       this.observe();
 
-       var name = this.config.name;
 
-       if (name) {
 
-         dom.addClass(this.element, name);
 
-         if (!this.config.contentEditableMode) { dom.addClass(this.editableArea, name); }
 
-       }
 
-       this.enable();
 
-       if (!this.config.noTextarea && this.textarea.element.disabled) {
 
-         this.disable();
 
-       }
 
-       // Simulate html5 placeholder attribute on contentEditable element
 
-       var placeholderText = typeof(this.config.placeholder) === "string"
 
-         ? this.config.placeholder
 
-         : ((this.config.noTextarea) ? this.editableArea.getAttribute("data-placeholder") : this.textarea.element.getAttribute("placeholder"));
 
-       if (placeholderText) {
 
-         dom.simulatePlaceholder(this.parent, this, placeholderText);
 
-       }
 
-       // Make sure that the browser avoids using inline styles whenever possible
 
-       this.commands.exec("styleWithCSS", false);
 
-       this._initAutoLinking();
 
-       this._initObjectResizing();
 
-       this._initUndoManager();
 
-       this._initLineBreaking();
 
-       // Simulate html5 autofocus on contentEditable element
 
-       // This doesn't work on IOS (5.1.1)
 
-       if (!this.config.noTextarea && (this.textarea.element.hasAttribute("autofocus") || document.querySelector(":focus") == this.textarea.element) && !browser.isIos()) {
 
-         setTimeout(function() { that.focus(true); }, 100);
 
-       }
 
-       // IE sometimes leaves a single paragraph, which can't be removed by the user
 
-       if (!browser.clearsContentEditableCorrectly()) {
 
-         wysihtml5.quirks.ensureProperClearing(this);
 
-       }
 
-       // Set up a sync that makes sure that textarea and editor have the same content
 
-       if (this.initSync && this.config.sync) {
 
-         this.initSync();
 
-       }
 
-       // Okay hide the textarea, we are ready to go
 
-       if (!this.config.noTextarea) { this.textarea.hide(); }
 
-       // Fire global (before-)load event
 
-       this.parent.fire("beforeload").fire("load");
 
-     },
 
-     _initAutoLinking: function() {
 
-       var that                           = this,
 
-           supportsDisablingOfAutoLinking = browser.canDisableAutoLinking(),
 
-           supportsAutoLinking            = browser.doesAutoLinkingInContentEditable();
 
-       if (supportsDisablingOfAutoLinking) {
 
-         this.commands.exec("autoUrlDetect", false);
 
-       }
 
-       if (!this.config.autoLink) {
 
-         return;
 
-       }
 
-       // Only do the auto linking by ourselves when the browser doesn't support auto linking
 
-       // OR when he supports auto linking but we were able to turn it off (IE9+)
 
-       if (!supportsAutoLinking || (supportsAutoLinking && supportsDisablingOfAutoLinking)) {
 
-         this.parent.on("newword:composer", function() {
 
-           if (dom.getTextContent(that.element).match(dom.autoLink.URL_REG_EXP)) {
 
-             that.selection.executeAndRestore(function(startContainer, endContainer) {
 
-               var uneditables = that.element.querySelectorAll("." + that.config.uneditableContainerClassname),
 
-                   isInUneditable = false;
 
-               for (var i = uneditables.length; i--;) {
 
-                 if (wysihtml5.dom.contains(uneditables[i], endContainer)) {
 
-                   isInUneditable = true;
 
-                 }
 
-               }
 
-               if (!isInUneditable) dom.autoLink(endContainer.parentNode, [that.config.uneditableContainerClassname]);
 
-             });
 
-           }
 
-         });
 
-         dom.observe(this.element, "blur", function() {
 
-           dom.autoLink(that.element, [that.config.uneditableContainerClassname]);
 
-         });
 
-       }
 
-       // Assuming we have the following:
 
-       //  <a href="http://www.google.de">http://www.google.de</a>
 
-       // If a user now changes the url in the innerHTML we want to make sure that
 
-       // it's synchronized with the href attribute (as long as the innerHTML is still a url)
 
-       var // Use a live NodeList to check whether there are any links in the document
 
-           links           = this.sandbox.getDocument().getElementsByTagName("a"),
 
-           // The autoLink helper method reveals a reg exp to detect correct urls
 
-           urlRegExp       = dom.autoLink.URL_REG_EXP,
 
-           getTextContent  = function(element) {
 
-             var textContent = wysihtml5.lang.string(dom.getTextContent(element)).trim();
 
-             if (textContent.substr(0, 4) === "www.") {
 
-               textContent = "http://" + textContent;
 
-             }
 
-             return textContent;
 
-           };
 
-       dom.observe(this.element, "keydown", function(event) {
 
-         if (!links.length) {
 
-           return;
 
-         }
 
-         var selectedNode = that.selection.getSelectedNode(event.target.ownerDocument),
 
-             link         = dom.getParentElement(selectedNode, { nodeName: "A" }, 4),
 
-             textContent;
 
-         if (!link) {
 
-           return;
 
-         }
 
-         textContent = getTextContent(link);
 
-         // keydown is fired before the actual content is changed
 
-         // therefore we set a timeout to change the href
 
-         setTimeout(function() {
 
-           var newTextContent = getTextContent(link);
 
-           if (newTextContent === textContent) {
 
-             return;
 
-           }
 
-           // Only set href when new href looks like a valid url
 
-           if (newTextContent.match(urlRegExp)) {
 
-             link.setAttribute("href", newTextContent);
 
-           }
 
-         }, 0);
 
-       });
 
-     },
 
-     _initObjectResizing: function() {
 
-       this.commands.exec("enableObjectResizing", true);
 
-       // IE sets inline styles after resizing objects
 
-       // The following lines make sure that the width/height css properties
 
-       // are copied over to the width/height attributes
 
-       if (browser.supportsEvent("resizeend")) {
 
-         var properties        = ["width", "height"],
 
-             propertiesLength  = properties.length,
 
-             element           = this.element;
 
-         dom.observe(element, "resizeend", function(event) {
 
-           var target = event.target || event.srcElement,
 
-               style  = target.style,
 
-               i      = 0,
 
-               property;
 
-           if (target.nodeName !== "IMG") {
 
-             return;
 
-           }
 
-           for (; i<propertiesLength; i++) {
 
-             property = properties[i];
 
-             if (style[property]) {
 
-               target.setAttribute(property, parseInt(style[property], 10));
 
-               style[property] = "";
 
-             }
 
-           }
 
-           // After resizing IE sometimes forgets to remove the old resize handles
 
-           wysihtml5.quirks.redraw(element);
 
-         });
 
-       }
 
-     },
 
-     _initUndoManager: function() {
 
-       this.undoManager = new wysihtml5.UndoManager(this.parent);
 
-     },
 
-     _initLineBreaking: function() {
 
-       var that                              = this,
 
-           USE_NATIVE_LINE_BREAK_INSIDE_TAGS = ["LI", "P", "H1", "H2", "H3", "H4", "H5", "H6"],
 
-           LIST_TAGS                         = ["UL", "OL", "MENU"];
 
-       function adjust(selectedNode) {
 
-         var parentElement = dom.getParentElement(selectedNode, { nodeName: ["P", "DIV"] }, 2);
 
-         if (parentElement && dom.contains(that.element, parentElement)) {
 
-           that.selection.executeAndRestore(function() {
 
-             if (that.config.useLineBreaks) {
 
-               dom.replaceWithChildNodes(parentElement);
 
-             } else if (parentElement.nodeName !== "P") {
 
-               dom.renameElement(parentElement, "p");
 
-             }
 
-           });
 
-         }
 
-       }
 
-       if (!this.config.useLineBreaks) {
 
-         dom.observe(this.element, ["focus", "keydown"], function() {
 
-           if (that.isEmpty()) {
 
-             var paragraph = that.doc.createElement("P");
 
-             that.element.innerHTML = "";
 
-             that.element.appendChild(paragraph);
 
-             if (!browser.displaysCaretInEmptyContentEditableCorrectly()) {
 
-               paragraph.innerHTML = "<br>";
 
-               that.selection.setBefore(paragraph.firstChild);
 
-             } else {
 
-               that.selection.selectNode(paragraph, true);
 
-             }
 
-           }
 
-         });
 
-       }
 
-       // Under certain circumstances Chrome + Safari create nested <p> or <hX> tags after paste
 
-       // Inserting an invisible white space in front of it fixes the issue
 
-       // This is too hacky and causes selection not to replace content on paste in chrome
 
-      /* if (browser.createsNestedInvalidMarkupAfterPaste()) {
 
-         dom.observe(this.element, "paste", function(event) {
 
-           var invisibleSpace = that.doc.createTextNode(wysihtml5.INVISIBLE_SPACE);
 
-           that.selection.insertNode(invisibleSpace);
 
-         });
 
-       }*/
 
-       dom.observe(this.element, "keydown", function(event) {
 
-         var keyCode = event.keyCode;
 
-         if (event.shiftKey) {
 
-           return;
 
-         }
 
-         if (keyCode !== wysihtml5.ENTER_KEY && keyCode !== wysihtml5.BACKSPACE_KEY) {
 
-           return;
 
-         }
 
-         var blockElement = dom.getParentElement(that.selection.getSelectedNode(), { nodeName: USE_NATIVE_LINE_BREAK_INSIDE_TAGS }, 4);
 
-         if (blockElement) {
 
-           setTimeout(function() {
 
-             // Unwrap paragraph after leaving a list or a H1-6
 
-             var selectedNode = that.selection.getSelectedNode(),
 
-                 list;
 
-             if (blockElement.nodeName === "LI") {
 
-               if (!selectedNode) {
 
-                 return;
 
-               }
 
-               list = dom.getParentElement(selectedNode, { nodeName: LIST_TAGS }, 2);
 
-               if (!list) {
 
-                 adjust(selectedNode);
 
-               }
 
-             }
 
-             if (keyCode === wysihtml5.ENTER_KEY && blockElement.nodeName.match(/^H[1-6]$/)) {
 
-               adjust(selectedNode);
 
-             }
 
-           }, 0);
 
-           return;
 
-         }
 
-         if (that.config.useLineBreaks && keyCode === wysihtml5.ENTER_KEY && !wysihtml5.browser.insertsLineBreaksOnReturn()) {
 
-           event.preventDefault();
 
-           that.commands.exec("insertLineBreak");
 
-         }
 
-       });
 
-     }
 
-   });
 
- })(wysihtml5);
 
- ;(function(wysihtml5) {
 
-   var dom             = wysihtml5.dom,
 
-       doc             = document,
 
-       win             = window,
 
-       HOST_TEMPLATE   = doc.createElement("div"),
 
-       /**
 
-        * Styles to copy from textarea to the composer element
 
-        */
 
-       TEXT_FORMATTING = [
 
-         "background-color",
 
-         "color", "cursor",
 
-         "font-family", "font-size", "font-style", "font-variant", "font-weight",
 
-         "line-height", "letter-spacing",
 
-         "text-align", "text-decoration", "text-indent", "text-rendering",
 
-         "word-break", "word-wrap", "word-spacing"
 
-       ],
 
-       /**
 
-        * Styles to copy from textarea to the iframe
 
-        */
 
-       BOX_FORMATTING = [
 
-         "background-color",
 
-         "border-collapse",
 
-         "border-bottom-color", "border-bottom-style", "border-bottom-width",
 
-         "border-left-color", "border-left-style", "border-left-width",
 
-         "border-right-color", "border-right-style", "border-right-width",
 
-         "border-top-color", "border-top-style", "border-top-width",
 
-         "clear", "display", "float",
 
-         "margin-bottom", "margin-left", "margin-right", "margin-top",
 
-         "outline-color", "outline-offset", "outline-width", "outline-style",
 
-         "padding-left", "padding-right", "padding-top", "padding-bottom",
 
-         "position", "top", "left", "right", "bottom", "z-index",
 
-         "vertical-align", "text-align",
 
-         "-webkit-box-sizing", "-moz-box-sizing", "-ms-box-sizing", "box-sizing",
 
-         "-webkit-box-shadow", "-moz-box-shadow", "-ms-box-shadow","box-shadow",
 
-         "-webkit-border-top-right-radius", "-moz-border-radius-topright", "border-top-right-radius",
 
-         "-webkit-border-bottom-right-radius", "-moz-border-radius-bottomright", "border-bottom-right-radius",
 
-         "-webkit-border-bottom-left-radius", "-moz-border-radius-bottomleft", "border-bottom-left-radius",
 
-         "-webkit-border-top-left-radius", "-moz-border-radius-topleft", "border-top-left-radius",
 
-         "width", "height"
 
-       ],
 
-       ADDITIONAL_CSS_RULES = [
 
-         "html                 { height: 100%; }",
 
-         "body                 { height: 100%; padding: 1px 0 0 0; margin: -1px 0 0 0; }",
 
-         "body > p:first-child { margin-top: 0; }",
 
-         "._wysihtml5-temp     { display: none; }",
 
-         wysihtml5.browser.isGecko ?
 
-           "body.placeholder { color: graytext !important; }" :
 
-           "body.placeholder { color: #a9a9a9 !important; }",
 
-         // Ensure that user see's broken images and can delete them
 
-         "img:-moz-broken      { -moz-force-broken-image-icon: 1; height: 24px; width: 24px; }"
 
-       ];
 
-   /**
 
-    * With "setActive" IE offers a smart way of focusing elements without scrolling them into view:
 
-    * http://msdn.microsoft.com/en-us/library/ms536738(v=vs.85).aspx
 
-    *
 
-    * Other browsers need a more hacky way: (pssst don't tell my mama)
 
-    * In order to prevent the element being scrolled into view when focusing it, we simply
 
-    * move it out of the scrollable area, focus it, and reset it's position
 
-    */
 
-   var focusWithoutScrolling = function(element) {
 
-     if (element.setActive) {
 
-       // Following line could cause a js error when the textarea is invisible
 
-       // See https://github.com/xing/wysihtml5/issues/9
 
-       try { element.setActive(); } catch(e) {}
 
-     } else {
 
-       var elementStyle = element.style,
 
-           originalScrollTop = doc.documentElement.scrollTop || doc.body.scrollTop,
 
-           originalScrollLeft = doc.documentElement.scrollLeft || doc.body.scrollLeft,
 
-           originalStyles = {
 
-             position:         elementStyle.position,
 
-             top:              elementStyle.top,
 
-             left:             elementStyle.left,
 
-             WebkitUserSelect: elementStyle.WebkitUserSelect
 
-           };
 
-       dom.setStyles({
 
-         position:         "absolute",
 
-         top:              "-99999px",
 
-         left:             "-99999px",
 
-         // Don't ask why but temporarily setting -webkit-user-select to none makes the whole thing performing smoother
 
-         WebkitUserSelect: "none"
 
-       }).on(element);
 
-       element.focus();
 
-       dom.setStyles(originalStyles).on(element);
 
-       if (win.scrollTo) {
 
-         // Some browser extensions unset this method to prevent annoyances
 
-         // "Better PopUp Blocker" for Chrome http://code.google.com/p/betterpopupblocker/source/browse/trunk/blockStart.js#100
 
-         // Issue: http://code.google.com/p/betterpopupblocker/issues/detail?id=1
 
-         win.scrollTo(originalScrollLeft, originalScrollTop);
 
-       }
 
-     }
 
-   };
 
-   wysihtml5.views.Composer.prototype.style = function() {
 
-     var that                  = this,
 
-         originalActiveElement = doc.querySelector(":focus"),
 
-         textareaElement       = this.textarea.element,
 
-         hasPlaceholder        = textareaElement.hasAttribute("placeholder"),
 
-         originalPlaceholder   = hasPlaceholder && textareaElement.getAttribute("placeholder"),
 
-         originalDisplayValue  = textareaElement.style.display,
 
-         originalDisabled      = textareaElement.disabled,
 
-         displayValueForCopying;
 
-     this.focusStylesHost      = HOST_TEMPLATE.cloneNode(false);
 
-     this.blurStylesHost       = HOST_TEMPLATE.cloneNode(false);
 
-     this.disabledStylesHost   = HOST_TEMPLATE.cloneNode(false);
 
-     // Remove placeholder before copying (as the placeholder has an affect on the computed style)
 
-     if (hasPlaceholder) {
 
-       textareaElement.removeAttribute("placeholder");
 
-     }
 
-     if (textareaElement === originalActiveElement) {
 
-       textareaElement.blur();
 
-     }
 
-     // enable for copying styles
 
-     textareaElement.disabled = false;
 
-     // set textarea to display="none" to get cascaded styles via getComputedStyle
 
-     textareaElement.style.display = displayValueForCopying = "none";
 
-     if ((textareaElement.getAttribute("rows") && dom.getStyle("height").from(textareaElement) === "auto") ||
 
-         (textareaElement.getAttribute("cols") && dom.getStyle("width").from(textareaElement) === "auto")) {
 
-       textareaElement.style.display = displayValueForCopying = originalDisplayValue;
 
-     }
 
-     // --------- iframe styles (has to be set before editor styles, otherwise IE9 sets wrong fontFamily on blurStylesHost) ---------
 
-     dom.copyStyles(BOX_FORMATTING).from(textareaElement).to(this.editableArea).andTo(this.blurStylesHost);
 
-     // --------- editor styles ---------
 
-     dom.copyStyles(TEXT_FORMATTING).from(textareaElement).to(this.element).andTo(this.blurStylesHost);
 
-     // --------- apply standard rules ---------
 
-     dom.insertCSS(ADDITIONAL_CSS_RULES).into(this.element.ownerDocument);
 
-     // --------- :disabled styles ---------
 
-     textareaElement.disabled = true;
 
-     dom.copyStyles(BOX_FORMATTING).from(textareaElement).to(this.disabledStylesHost);
 
-     dom.copyStyles(TEXT_FORMATTING).from(textareaElement).to(this.disabledStylesHost);
 
-     textareaElement.disabled = originalDisabled;
 
-     // --------- :focus styles ---------
 
-     textareaElement.style.display = originalDisplayValue;
 
-     focusWithoutScrolling(textareaElement);
 
-     textareaElement.style.display = displayValueForCopying;
 
-     dom.copyStyles(BOX_FORMATTING).from(textareaElement).to(this.focusStylesHost);
 
-     dom.copyStyles(TEXT_FORMATTING).from(textareaElement).to(this.focusStylesHost);
 
-     // reset textarea
 
-     textareaElement.style.display = originalDisplayValue;
 
-     dom.copyStyles(["display"]).from(textareaElement).to(this.editableArea);
 
-     // Make sure that we don't change the display style of the iframe when copying styles oblur/onfocus
 
-     // this is needed for when the change_view event is fired where the iframe is hidden and then
 
-     // the blur event fires and re-displays it
 
-     var boxFormattingStyles = wysihtml5.lang.array(BOX_FORMATTING).without(["display"]);
 
-     // --------- restore focus ---------
 
-     if (originalActiveElement) {
 
-       originalActiveElement.focus();
 
-     } else {
 
-       textareaElement.blur();
 
-     }
 
-     // --------- restore placeholder ---------
 
-     if (hasPlaceholder) {
 
-       textareaElement.setAttribute("placeholder", originalPlaceholder);
 
-     }
 
-     // --------- Sync focus/blur styles ---------
 
-     this.parent.on("focus:composer", function() {
 
-       dom.copyStyles(boxFormattingStyles) .from(that.focusStylesHost).to(that.editableArea);
 
-       dom.copyStyles(TEXT_FORMATTING)     .from(that.focusStylesHost).to(that.element);
 
-     });
 
-     this.parent.on("blur:composer", function() {
 
-       dom.copyStyles(boxFormattingStyles) .from(that.blurStylesHost).to(that.editableArea);
 
-       dom.copyStyles(TEXT_FORMATTING)     .from(that.blurStylesHost).to(that.element);
 
-     });
 
-     this.parent.observe("disable:composer", function() {
 
-       dom.copyStyles(boxFormattingStyles) .from(that.disabledStylesHost).to(that.editableArea);
 
-       dom.copyStyles(TEXT_FORMATTING)     .from(that.disabledStylesHost).to(that.element);
 
-     });
 
-     this.parent.observe("enable:composer", function() {
 
-       dom.copyStyles(boxFormattingStyles) .from(that.blurStylesHost).to(that.editableArea);
 
-       dom.copyStyles(TEXT_FORMATTING)     .from(that.blurStylesHost).to(that.element);
 
-     });
 
-     return this;
 
-   };
 
- })(wysihtml5);
 
- ;/**
 
-  * Taking care of events
 
-  *  - Simulating 'change' event on contentEditable element
 
-  *  - Handling drag & drop logic
 
-  *  - Catch paste events
 
-  *  - Dispatch proprietary newword:composer event
 
-  *  - Keyboard shortcuts
 
-  */
 
- (function(wysihtml5) {
 
-   var dom       = wysihtml5.dom,
 
-       browser   = wysihtml5.browser,
 
-       /**
 
-        * Map keyCodes to query commands
 
-        */
 
-       shortcuts = {
 
-         "66": "bold",     // B
 
-         "73": "italic",   // I
 
-         "85": "underline" // U
 
-       };
 
-   var deleteAroundEditable = function(selection, uneditable, element) {
 
-     // merge node with previous node from uneditable
 
-     var prevNode = selection.getPreviousNode(uneditable, true),
 
-         curNode = selection.getSelectedNode();
 
-     if (curNode.nodeType !== 1 && curNode.parentNode !== element) { curNode = curNode.parentNode; }
 
-     if (prevNode) {
 
-       if (curNode.nodeType == 1) {
 
-         var first = curNode.firstChild;
 
-         if (prevNode.nodeType == 1) {
 
-           while (curNode.firstChild) {
 
-             prevNode.appendChild(curNode.firstChild);
 
-           }
 
-         } else {
 
-           while (curNode.firstChild) {
 
-             uneditable.parentNode.insertBefore(curNode.firstChild, uneditable);
 
-           }
 
-         }
 
-         if (curNode.parentNode) {
 
-           curNode.parentNode.removeChild(curNode);
 
-         }
 
-         selection.setBefore(first);
 
-       } else {
 
-         if (prevNode.nodeType == 1) {
 
-           prevNode.appendChild(curNode);
 
-         } else {
 
-           uneditable.parentNode.insertBefore(curNode, uneditable);
 
-         }
 
-         selection.setBefore(curNode);
 
-       }
 
-     }
 
-   };
 
-   var handleDeleteKeyPress = function(event, selection, element, composer) {
 
-     if (selection.isCollapsed()) {
 
-       if (selection.caretIsInTheBeginnig('LI')) {
 
-         event.preventDefault();
 
-         composer.commands.exec('outdentList');
 
-       } else if (selection.caretIsInTheBeginnig()) {
 
-         event.preventDefault();
 
-       } else {
 
-         if (selection.caretIsFirstInSelection() &&
 
-             selection.getPreviousNode() &&
 
-             selection.getPreviousNode().nodeName &&
 
-             (/^H\d$/gi).test(selection.getPreviousNode().nodeName)
 
-         ) {
 
-           var prevNode = selection.getPreviousNode();
 
-           event.preventDefault();
 
-           if ((/^\s*$/).test(prevNode.textContent || prevNode.innerText)) {
 
-             // heading is empty
 
-             prevNode.parentNode.removeChild(prevNode);
 
-           } else {
 
-             var range = prevNode.ownerDocument.createRange();
 
-             range.selectNodeContents(prevNode);
 
-             range.collapse(false);
 
-             selection.setSelection(range);
 
-           }
 
-         }
 
-         var beforeUneditable = selection.caretIsBeforeUneditable();
 
-         // Do a special delete if caret would delete uneditable
 
-         if (beforeUneditable) {
 
-           event.preventDefault();
 
-           deleteAroundEditable(selection, beforeUneditable, element);
 
-         }
 
-       }
 
-     } else {
 
-       if (selection.containsUneditable()) {
 
-         event.preventDefault();
 
-         selection.deleteContents();
 
-       }
 
-     }
 
-   };
 
-   var handleTabKeyDown = function(composer, element) {
 
-     if (!composer.selection.isCollapsed()) {
 
-       composer.selection.deleteContents();
 
-     } else if (composer.selection.caretIsInTheBeginnig('LI')) {
 
-       if (composer.commands.exec('indentList')) return;
 
-     }
 
-     // Is   close enough to tab. Could not find enough counter arguments for now.
 
-     composer.commands.exec("insertHTML", " ");
 
-   };
 
-   wysihtml5.views.Composer.prototype.observe = function() {
 
-     var that                = this,
 
-         state               = this.getValue(false, false),
 
-         container           = (this.sandbox.getIframe) ? this.sandbox.getIframe() : this.sandbox.getContentEditable(),
 
-         element             = this.element,
 
-         focusBlurElement    = (browser.supportsEventsInIframeCorrectly() || this.sandbox.getContentEditable) ? element : this.sandbox.getWindow(),
 
-         pasteEvents         = ["drop", "paste", "beforepaste"],
 
-         interactionEvents   = ["drop", "paste", "mouseup", "focus", "keyup"];
 
-     // --------- destroy:composer event ---------
 
-     dom.observe(container, "DOMNodeRemoved", function() {
 
-       clearInterval(domNodeRemovedInterval);
 
-       that.parent.fire("destroy:composer");
 
-     });
 
-     // DOMNodeRemoved event is not supported in IE 8
 
-     if (!browser.supportsMutationEvents()) {
 
-         var domNodeRemovedInterval = setInterval(function() {
 
-           if (!dom.contains(document.documentElement, container)) {
 
-             clearInterval(domNodeRemovedInterval);
 
-             that.parent.fire("destroy:composer");
 
-           }
 
-         }, 250);
 
-     }
 
-     // --------- User interaction tracking --
 
-     dom.observe(focusBlurElement, interactionEvents, function() {
 
-       setTimeout(function() {
 
-         that.parent.fire("interaction").fire("interaction:composer");
 
-       }, 0);
 
-     });
 
-     if (this.config.handleTables) {
 
-       if(!this.tableClickHandle && this.doc.execCommand && wysihtml5.browser.supportsCommand(this.doc, "enableObjectResizing") && wysihtml5.browser.supportsCommand(this.doc, "enableInlineTableEditing")) {
 
-         if (this.sandbox.getIframe) {
 
-           this.tableClickHandle = dom.observe(container , ["focus", "mouseup", "mouseover"], function() {
 
-             that.doc.execCommand("enableObjectResizing", false, "false");
 
-             that.doc.execCommand("enableInlineTableEditing", false, "false");
 
-             that.tableClickHandle.stop();
 
-           });
 
-         } else {
 
-           setTimeout(function() {
 
-             that.doc.execCommand("enableObjectResizing", false, "false");
 
-             that.doc.execCommand("enableInlineTableEditing", false, "false");
 
-           }, 0);
 
-         }
 
-       }
 
-       this.tableSelection = wysihtml5.quirks.tableCellsSelection(element, that.parent);
 
-     }
 
-     // --------- Focus & blur logic ---------
 
-     dom.observe(focusBlurElement, "focus", function(event) {
 
-       that.parent.fire("focus", event).fire("focus:composer", event);
 
-       // Delay storing of state until all focus handler are fired
 
-       // especially the one which resets the placeholder
 
-       setTimeout(function() { state = that.getValue(false, false); }, 0);
 
-     });
 
-     dom.observe(focusBlurElement, "blur", function(event) {
 
-       if (state !== that.getValue(false, false)) {
 
-         //create change event if supported (all except IE8)
 
-         var changeevent = event;
 
-         if(typeof Object.create == 'function') {
 
-           changeevent = Object.create(event, { type: { value: 'change' } });
 
-         }
 
-         that.parent.fire("change", changeevent).fire("change:composer", changeevent);
 
-       }
 
-       that.parent.fire("blur", event).fire("blur:composer", event);
 
-     });
 
-     // --------- Drag & Drop logic ---------
 
-     dom.observe(element, "dragenter", function() {
 
-       that.parent.fire("unset_placeholder");
 
-     });
 
-     dom.observe(element, pasteEvents, function(event) {
 
-       that.parent.fire(event.type, event).fire(event.type + ":composer", event);
 
-     });
 
-     if (this.config.copyedFromMarking) {
 
-       // If supported the copied source is based directly on selection
 
-       // Very useful for webkit based browsers where copy will otherwise contain a lot of code and styles based on whatever and not actually in selection.
 
-       dom.observe(element, "copy", function(event) {
 
-         if (event.clipboardData) {
 
-           event.clipboardData.setData("text/html", that.config.copyedFromMarking + that.selection.getHtml());
 
-           event.preventDefault();
 
-         }
 
-         that.parent.fire(event.type, event).fire(event.type + ":composer", event);
 
-       });
 
-     }
 
-     // --------- neword event ---------
 
-     dom.observe(element, "keyup", function(event) {
 
-       var keyCode = event.keyCode;
 
-       if (keyCode === wysihtml5.SPACE_KEY || keyCode === wysihtml5.ENTER_KEY) {
 
-         that.parent.fire("newword:composer");
 
-       }
 
-     });
 
-     this.parent.on("paste:composer", function() {
 
-       setTimeout(function() { that.parent.fire("newword:composer"); }, 0);
 
-     });
 
-     // --------- Make sure that images are selected when clicking on them ---------
 
-     if (!browser.canSelectImagesInContentEditable()) {
 
-       dom.observe(element, "mousedown", function(event) {
 
-         var target = event.target;
 
-         var allImages = element.querySelectorAll('img'),
 
-             notMyImages = element.querySelectorAll('.' + that.config.uneditableContainerClassname + ' img'),
 
-             myImages = wysihtml5.lang.array(allImages).without(notMyImages);
 
-         if (target.nodeName === "IMG" && wysihtml5.lang.array(myImages).contains(target)) {
 
-           that.selection.selectNode(target);
 
-         }
 
-       });
 
-     }
 
-     if (!browser.canSelectImagesInContentEditable()) {
 
-         dom.observe(element, "drop", function(event) {
 
-             // TODO: if I knew how to get dropped elements list from event I could limit it to only IMG element case
 
-             setTimeout(function() {
 
-                 that.selection.getSelection().removeAllRanges();
 
-             }, 0);
 
-         });
 
-     }
 
-     if (browser.hasHistoryIssue() && browser.supportsSelectionModify()) {
 
-       dom.observe(element, "keydown", function(event) {
 
-         if (!event.metaKey && !event.ctrlKey) {
 
-           return;
 
-         }
 
-         var keyCode   = event.keyCode,
 
-             win       = element.ownerDocument.defaultView,
 
-             selection = win.getSelection();
 
-         if (keyCode === 37 || keyCode === 39) {
 
-           if (keyCode === 37) {
 
-             selection.modify("extend", "left", "lineboundary");
 
-             if (!event.shiftKey) {
 
-               selection.collapseToStart();
 
-             }
 
-           }
 
-           if (keyCode === 39) {
 
-             selection.modify("extend", "right", "lineboundary");
 
-             if (!event.shiftKey) {
 
-               selection.collapseToEnd();
 
-             }
 
-           }
 
-           event.preventDefault();
 
-         }
 
-       });
 
-     }
 
-     // --------- Shortcut logic ---------
 
-     dom.observe(element, "keydown", function(event) {
 
-       var keyCode  = event.keyCode,
 
-           command  = shortcuts[keyCode];
 
-       if ((event.ctrlKey || event.metaKey) && !event.altKey && command) {
 
-         that.commands.exec(command);
 
-         event.preventDefault();
 
-       }
 
-       if (keyCode === 8) {
 
-         // delete key
 
-         handleDeleteKeyPress(event, that.selection, element, that);
 
-       } else if (that.config.handleTabKey && keyCode === 9) {
 
-         event.preventDefault();
 
-         handleTabKeyDown(that, element);
 
-       }
 
-     });
 
-     // --------- Make sure that when pressing backspace/delete on selected images deletes the image and it's anchor ---------
 
-     dom.observe(element, "keydown", function(event) {
 
-       var target  = that.selection.getSelectedNode(true),
 
-           keyCode = event.keyCode,
 
-           parent;
 
-       if (target && target.nodeName === "IMG" && (keyCode === wysihtml5.BACKSPACE_KEY || keyCode === wysihtml5.DELETE_KEY)) { // 8 => backspace, 46 => delete
 
-         parent = target.parentNode;
 
-         // delete the <img>
 
-         parent.removeChild(target);
 
-         // and it's parent <a> too if it hasn't got any other child nodes
 
-         if (parent.nodeName === "A" && !parent.firstChild) {
 
-           parent.parentNode.removeChild(parent);
 
-         }
 
-         setTimeout(function() { wysihtml5.quirks.redraw(element); }, 0);
 
-         event.preventDefault();
 
-       }
 
-     });
 
-     // --------- IE 8+9 focus the editor when the iframe is clicked (without actually firing the 'focus' event on the <body>) ---------
 
-     if (!this.config.contentEditableMode && browser.hasIframeFocusIssue()) {
 
-       dom.observe(container, "focus", function() {
 
-         setTimeout(function() {
 
-           if (that.doc.querySelector(":focus") !== that.element) {
 
-             that.focus();
 
-           }
 
-         }, 0);
 
-       });
 
-       dom.observe(this.element, "blur", function() {
 
-         setTimeout(function() {
 
-           that.selection.getSelection().removeAllRanges();
 
-         }, 0);
 
-       });
 
-     }
 
-     // --------- Show url in tooltip when hovering links or images ---------
 
-     var titlePrefixes = {
 
-       IMG: "Image: ",
 
-       A:   "Link: "
 
-     };
 
-     dom.observe(element, "mouseover", function(event) {
 
-       var target   = event.target,
 
-           nodeName = target.nodeName,
 
-           title;
 
-       if (nodeName !== "A" && nodeName !== "IMG") {
 
-         return;
 
-       }
 
-       var hasTitle = target.hasAttribute("title");
 
-       if(!hasTitle){
 
-         title = titlePrefixes[nodeName] + (target.getAttribute("href") || target.getAttribute("src"));
 
-         target.setAttribute("title", title);
 
-       }
 
-     });
 
-   };
 
- })(wysihtml5);
 
- ;/**
 
-  * Class that takes care that the value of the composer and the textarea is always in sync
 
-  */
 
- (function(wysihtml5) {
 
-   var INTERVAL = 400;
 
-   wysihtml5.views.Synchronizer = Base.extend(
 
-     /** @scope wysihtml5.views.Synchronizer.prototype */ {
 
-     constructor: function(editor, textarea, composer) {
 
-       this.editor   = editor;
 
-       this.textarea = textarea;
 
-       this.composer = composer;
 
-       this._observe();
 
-     },
 
-     /**
 
-      * Sync html from composer to textarea
 
-      * Takes care of placeholders
 
-      * @param {Boolean} shouldParseHtml Whether the html should be sanitized before inserting it into the textarea
 
-      */
 
-     fromComposerToTextarea: function(shouldParseHtml) {
 
-       this.textarea.setValue(wysihtml5.lang.string(this.composer.getValue(false, false)).trim(), shouldParseHtml);
 
-     },
 
-     /**
 
-      * Sync value of textarea to composer
 
-      * Takes care of placeholders
 
-      * @param {Boolean} shouldParseHtml Whether the html should be sanitized before inserting it into the composer
 
-      */
 
-     fromTextareaToComposer: function(shouldParseHtml) {
 
-       var textareaValue = this.textarea.getValue(false, false);
 
-       if (textareaValue) {
 
-         this.composer.setValue(textareaValue, shouldParseHtml);
 
-       } else {
 
-         this.composer.clear();
 
-         this.editor.fire("set_placeholder");
 
-       }
 
-     },
 
-     /**
 
-      * Invoke syncing based on view state
 
-      * @param {Boolean} shouldParseHtml Whether the html should be sanitized before inserting it into the composer/textarea
 
-      */
 
-     sync: function(shouldParseHtml) {
 
-       if (this.editor.currentView.name === "textarea") {
 
-         this.fromTextareaToComposer(shouldParseHtml);
 
-       } else {
 
-         this.fromComposerToTextarea(shouldParseHtml);
 
-       }
 
-     },
 
-     /**
 
-      * Initializes interval-based syncing
 
-      * also makes sure that on-submit the composer's content is synced with the textarea
 
-      * immediately when the form gets submitted
 
-      */
 
-     _observe: function() {
 
-       var interval,
 
-           that          = this,
 
-           form          = this.textarea.element.form,
 
-           startInterval = function() {
 
-             interval = setInterval(function() { that.fromComposerToTextarea(); }, INTERVAL);
 
-           },
 
-           stopInterval  = function() {
 
-             clearInterval(interval);
 
-             interval = null;
 
-           };
 
-       startInterval();
 
-       if (form) {
 
-         // If the textarea is in a form make sure that after onreset and onsubmit the composer
 
-         // has the correct state
 
-         wysihtml5.dom.observe(form, "submit", function() {
 
-           that.sync(true);
 
-         });
 
-         wysihtml5.dom.observe(form, "reset", function() {
 
-           setTimeout(function() { that.fromTextareaToComposer(); }, 0);
 
-         });
 
-       }
 
-       this.editor.on("change_view", function(view) {
 
-         if (view === "composer" && !interval) {
 
-           that.fromTextareaToComposer(true);
 
-           startInterval();
 
-         } else if (view === "textarea") {
 
-           that.fromComposerToTextarea(true);
 
-           stopInterval();
 
-         }
 
-       });
 
-       this.editor.on("destroy:composer", stopInterval);
 
-     }
 
-   });
 
- })(wysihtml5);
 
- ;wysihtml5.views.Textarea = wysihtml5.views.View.extend(
 
-   /** @scope wysihtml5.views.Textarea.prototype */ {
 
-   name: "textarea",
 
-   constructor: function(parent, textareaElement, config) {
 
-     this.base(parent, textareaElement, config);
 
-     this._observe();
 
-   },
 
-   clear: function() {
 
-     this.element.value = "";
 
-   },
 
-   getValue: function(parse) {
 
-     var value = this.isEmpty() ? "" : this.element.value;
 
-     if (parse !== false) {
 
-       value = this.parent.parse(value);
 
-     }
 
-     return value;
 
-   },
 
-   setValue: function(html, parse) {
 
-     if (parse) {
 
-       html = this.parent.parse(html);
 
-     }
 
-     this.element.value = html;
 
-   },
 
-   cleanUp: function() {
 
-       var html = this.parent.parse(this.element.value);
 
-       this.element.value = html;
 
-   },
 
-   hasPlaceholderSet: function() {
 
-     var supportsPlaceholder = wysihtml5.browser.supportsPlaceholderAttributeOn(this.element),
 
-         placeholderText     = this.element.getAttribute("placeholder") || null,
 
-         value               = this.element.value,
 
-         isEmpty             = !value;
 
-     return (supportsPlaceholder && isEmpty) || (value === placeholderText);
 
-   },
 
-   isEmpty: function() {
 
-     return !wysihtml5.lang.string(this.element.value).trim() || this.hasPlaceholderSet();
 
-   },
 
-   _observe: function() {
 
-     var element = this.element,
 
-         parent  = this.parent,
 
-         eventMapping = {
 
-           focusin:  "focus",
 
-           focusout: "blur"
 
-         },
 
-         /**
 
-          * Calling focus() or blur() on an element doesn't synchronously trigger the attached focus/blur events
 
-          * This is the case for focusin and focusout, so let's use them whenever possible, kkthxbai
 
-          */
 
-         events = wysihtml5.browser.supportsEvent("focusin") ? ["focusin", "focusout", "change"] : ["focus", "blur", "change"];
 
-     parent.on("beforeload", function() {
 
-       wysihtml5.dom.observe(element, events, function(event) {
 
-         var eventName = eventMapping[event.type] || event.type;
 
-         parent.fire(eventName).fire(eventName + ":textarea");
 
-       });
 
-       wysihtml5.dom.observe(element, ["paste", "drop"], function() {
 
-         setTimeout(function() { parent.fire("paste").fire("paste:textarea"); }, 0);
 
-       });
 
-     });
 
-   }
 
- });
 
- ;/**
 
-  * WYSIHTML5 Editor
 
-  *
 
-  * @param {Element} editableElement Reference to the textarea which should be turned into a rich text interface
 
-  * @param {Object} [config] See defaultConfig object below for explanation of each individual config option
 
-  *
 
-  * @events
 
-  *    load
 
-  *    beforeload (for internal use only)
 
-  *    focus
 
-  *    focus:composer
 
-  *    focus:textarea
 
-  *    blur
 
-  *    blur:composer
 
-  *    blur:textarea
 
-  *    change
 
-  *    change:composer
 
-  *    change:textarea
 
-  *    paste
 
-  *    paste:composer
 
-  *    paste:textarea
 
-  *    newword:composer
 
-  *    destroy:composer
 
-  *    undo:composer
 
-  *    redo:composer
 
-  *    beforecommand:composer
 
-  *    aftercommand:composer
 
-  *    enable:composer
 
-  *    disable:composer
 
-  *    change_view
 
-  */
 
- (function(wysihtml5) {
 
-   var undef;
 
-   var defaultConfig = {
 
-     // Give the editor a name, the name will also be set as class name on the iframe and on the iframe's body
 
-     name:                 undef,
 
-     // Whether the editor should look like the textarea (by adopting styles)
 
-     style:                true,
 
-     // Id of the toolbar element, pass falsey value if you don't want any toolbar logic
 
-     toolbar:              undef,
 
-     // Whether toolbar is displayed after init by script automatically.
 
-     // Can be set to false if toolobar is set to display only on editable area focus
 
-     showToolbarAfterInit: true,
 
-     // Whether urls, entered by the user should automatically become clickable-links
 
-     autoLink:             true,
 
-     // Includes table editing events and cell selection tracking
 
-     handleTables:         true,
 
-     // Tab key inserts tab into text as default behaviour. It can be disabled to regain keyboard navigation
 
-     handleTabKey:         true,
 
-     // Object which includes parser rules to apply when html gets cleaned
 
-     // See parser_rules/*.js for examples
 
-     parserRules:          { tags: { br: {}, span: {}, div: {}, p: {} }, classes: {} },
 
-     // Object which includes parser when the user inserts content via copy & paste. If null parserRules will be used instead
 
-     pasteParserRulesets: null,
 
-     // Parser method to use when the user inserts content
 
-     parser:               wysihtml5.dom.parse,
 
-     // Class name which should be set on the contentEditable element in the created sandbox iframe, can be styled via the 'stylesheets' option
 
-     composerClassName:    "wysihtml5-editor",
 
-     // Class name to add to the body when the wysihtml5 editor is supported
 
-     bodyClassName:        "wysihtml5-supported",
 
-     // By default wysihtml5 will insert a <br> for line breaks, set this to false to use <p>
 
-     useLineBreaks:        true,
 
-     // Array (or single string) of stylesheet urls to be loaded in the editor's iframe
 
-     stylesheets:          [],
 
-     // Placeholder text to use, defaults to the placeholder attribute on the textarea element
 
-     placeholderText:      undef,
 
-     // Whether the rich text editor should be rendered on touch devices (wysihtml5 >= 0.3.0 comes with basic support for iOS 5)
 
-     supportTouchDevices:  true,
 
-     // Whether senseless <span> elements (empty or without attributes) should be removed/replaced with their content
 
-     cleanUp:              true,
 
-     // Whether to use div instead of secure iframe
 
-     contentEditableMode: false,
 
-     // Classname of container that editor should not touch and pass through
 
-     // Pass false to disable
 
-     uneditableContainerClassname: "wysihtml5-uneditable-container",
 
-     // Browsers that support copied source handling will get a marking of the origin of the copied source (for determinig code cleanup rules on paste)
 
-     // Also copied source is based directly on selection - 
 
-     // (very useful for webkit based browsers where copy will otherwise contain a lot of code and styles based on whatever and not actually in selection).
 
-     // If falsy value is passed source override is also disabled
 
-     copyedFromMarking: '<meta name="copied-from" content="wysihtml5">'
 
-   };
 
-   wysihtml5.Editor = wysihtml5.lang.Dispatcher.extend(
 
-     /** @scope wysihtml5.Editor.prototype */ {
 
-     constructor: function(editableElement, config) {
 
-       this.editableElement  = typeof(editableElement) === "string" ? document.getElementById(editableElement) : editableElement;
 
-       this.config           = wysihtml5.lang.object({}).merge(defaultConfig).merge(config).get();
 
-       this._isCompatible    = wysihtml5.browser.supported();
 
-       if (this.editableElement.nodeName.toLowerCase() != "textarea") {
 
-           this.config.contentEditableMode = true;
 
-           this.config.noTextarea = true;
 
-       }
 
-       if (!this.config.noTextarea) {
 
-           this.textarea         = new wysihtml5.views.Textarea(this, this.editableElement, this.config);
 
-           this.currentView      = this.textarea;
 
-       }
 
-       // Sort out unsupported/unwanted browsers here
 
-       if (!this._isCompatible || (!this.config.supportTouchDevices && wysihtml5.browser.isTouchDevice())) {
 
-         var that = this;
 
-         setTimeout(function() { that.fire("beforeload").fire("load"); }, 0);
 
-         return;
 
-       }
 
-       // Add class name to body, to indicate that the editor is supported
 
-       wysihtml5.dom.addClass(document.body, this.config.bodyClassName);
 
-       this.composer = new wysihtml5.views.Composer(this, this.editableElement, this.config);
 
-       this.currentView = this.composer;
 
-       if (typeof(this.config.parser) === "function") {
 
-         this._initParser();
 
-       }
 
-       this.on("beforeload", this.handleBeforeLoad);
 
-     },
 
-     handleBeforeLoad: function() {
 
-         if (!this.config.noTextarea) {
 
-             this.synchronizer = new wysihtml5.views.Synchronizer(this, this.textarea, this.composer);
 
-         }
 
-         if (this.config.toolbar) {
 
-           this.toolbar = new wysihtml5.toolbar.Toolbar(this, this.config.toolbar, this.config.showToolbarAfterInit);
 
-         }
 
-     },
 
-     isCompatible: function() {
 
-       return this._isCompatible;
 
-     },
 
-     clear: function() {
 
-       this.currentView.clear();
 
-       return this;
 
-     },
 
-     getValue: function(parse, clearInternals) {
 
-       return this.currentView.getValue(parse, clearInternals);
 
-     },
 
-     setValue: function(html, parse) {
 
-       this.fire("unset_placeholder");
 
-       if (!html) {
 
-         return this.clear();
 
-       }
 
-       this.currentView.setValue(html, parse);
 
-       return this;
 
-     },
 
-     cleanUp: function() {
 
-         this.currentView.cleanUp();
 
-     },
 
-     focus: function(setToEnd) {
 
-       this.currentView.focus(setToEnd);
 
-       return this;
 
-     },
 
-     /**
 
-      * Deactivate editor (make it readonly)
 
-      */
 
-     disable: function() {
 
-       this.currentView.disable();
 
-       return this;
 
-     },
 
-     /**
 
-      * Activate editor
 
-      */
 
-     enable: function() {
 
-       this.currentView.enable();
 
-       return this;
 
-     },
 
-     isEmpty: function() {
 
-       return this.currentView.isEmpty();
 
-     },
 
-     hasPlaceholderSet: function() {
 
-       return this.currentView.hasPlaceholderSet();
 
-     },
 
-     parse: function(htmlOrElement, clearInternals) {
 
-       var parseContext = (this.config.contentEditableMode) ? document : ((this.composer) ? this.composer.sandbox.getDocument() : null);
 
-       var returnValue = this.config.parser(htmlOrElement, {
 
-         "rules": this.config.parserRules,
 
-         "cleanUp": this.config.cleanUp,
 
-         "context": parseContext,
 
-         "uneditableClass": this.config.uneditableContainerClassname,
 
-         "clearInternals" : clearInternals
 
-       });
 
-       if (typeof(htmlOrElement) === "object") {
 
-         wysihtml5.quirks.redraw(htmlOrElement);
 
-       }
 
-       return returnValue;
 
-     },
 
-     /**
 
-      * Prepare html parser logic
 
-      *  - Observes for paste and drop
 
-      */
 
-     _initParser: function() {
 
-       var that = this,
 
-           oldHtml,
 
-           cleanHtml;
 
-       if (wysihtml5.browser.supportsModenPaste()) {
 
-         this.on("paste:composer", function(event) {
 
-           event.preventDefault();
 
-           oldHtml = wysihtml5.dom.getPastedHtml(event);
 
-           if (oldHtml) {
 
-             that._cleanAndPaste(oldHtml);
 
-           }
 
-         });
 
-       } else {
 
-         this.on("beforepaste:composer", function(event) {
 
-           event.preventDefault();
 
-           wysihtml5.dom.getPastedHtmlWithDiv(that.composer, function(pastedHTML) {
 
-             if (pastedHTML) {
 
-               that._cleanAndPaste(pastedHTML);
 
-             }
 
-           });
 
-         });
 
-       }
 
-     },
 
-     _cleanAndPaste: function (oldHtml) {
 
-       var cleanHtml = wysihtml5.quirks.cleanPastedHTML(oldHtml, {
 
-         "referenceNode": this.composer.element,
 
-         "rules": this.config.pasteParserRulesets || [{"set": this.config.parserRules}],
 
-         "uneditableClass": this.config.uneditableContainerClassname
 
-       });
 
-       this.composer.selection.deleteContents();
 
-       this.composer.selection.insertHTML(cleanHtml);
 
-     }
 
-   });
 
- })(wysihtml5);
 
- ;/**
 
-  * Toolbar Dialog
 
-  *
 
-  * @param {Element} link The toolbar link which causes the dialog to show up
 
-  * @param {Element} container The dialog container
 
-  *
 
-  * @example
 
-  *    <!-- Toolbar link -->
 
-  *    <a data-wysihtml5-command="insertImage">insert an image</a>
 
-  *
 
-  *    <!-- Dialog -->
 
-  *    <div data-wysihtml5-dialog="insertImage" style="display: none;">
 
-  *      <label>
 
-  *        URL: <input data-wysihtml5-dialog-field="src" value="http://">
 
-  *      </label>
 
-  *      <label>
 
-  *        Alternative text: <input data-wysihtml5-dialog-field="alt" value="">
 
-  *      </label>
 
-  *    </div>
 
-  *
 
-  *    <script>
 
-  *      var dialog = new wysihtml5.toolbar.Dialog(
 
-  *        document.querySelector("[data-wysihtml5-command='insertImage']"),
 
-  *        document.querySelector("[data-wysihtml5-dialog='insertImage']")
 
-  *      );
 
-  *      dialog.observe("save", function(attributes) {
 
-  *        // do something
 
-  *      });
 
-  *    </script>
 
-  */
 
- (function(wysihtml5) {
 
-   var dom                     = wysihtml5.dom,
 
-       CLASS_NAME_OPENED       = "wysihtml5-command-dialog-opened",
 
-       SELECTOR_FORM_ELEMENTS  = "input, select, textarea",
 
-       SELECTOR_FIELDS         = "[data-wysihtml5-dialog-field]",
 
-       ATTRIBUTE_FIELDS        = "data-wysihtml5-dialog-field";
 
-   wysihtml5.toolbar.Dialog = wysihtml5.lang.Dispatcher.extend(
 
-     /** @scope wysihtml5.toolbar.Dialog.prototype */ {
 
-     constructor: function(link, container) {
 
-       this.link       = link;
 
-       this.container  = container;
 
-     },
 
-     _observe: function() {
 
-       if (this._observed) {
 
-         return;
 
-       }
 
-       var that = this,
 
-           callbackWrapper = function(event) {
 
-             var attributes = that._serialize();
 
-             if (attributes == that.elementToChange) {
 
-               that.fire("edit", attributes);
 
-             } else {
 
-               that.fire("save", attributes);
 
-             }
 
-             that.hide();
 
-             event.preventDefault();
 
-             event.stopPropagation();
 
-           };
 
-       dom.observe(that.link, "click", function() {
 
-         if (dom.hasClass(that.link, CLASS_NAME_OPENED)) {
 
-           setTimeout(function() { that.hide(); }, 0);
 
-         }
 
-       });
 
-       dom.observe(this.container, "keydown", function(event) {
 
-         var keyCode = event.keyCode;
 
-         if (keyCode === wysihtml5.ENTER_KEY) {
 
-           callbackWrapper(event);
 
-         }
 
-         if (keyCode === wysihtml5.ESCAPE_KEY) {
 
-           that.fire("cancel");
 
-           that.hide();
 
-         }
 
-       });
 
-       dom.delegate(this.container, "[data-wysihtml5-dialog-action=save]", "click", callbackWrapper);
 
-       dom.delegate(this.container, "[data-wysihtml5-dialog-action=cancel]", "click", function(event) {
 
-         that.fire("cancel");
 
-         that.hide();
 
-         event.preventDefault();
 
-         event.stopPropagation();
 
-       });
 
-       var formElements  = this.container.querySelectorAll(SELECTOR_FORM_ELEMENTS),
 
-           i             = 0,
 
-           length        = formElements.length,
 
-           _clearInterval = function() { clearInterval(that.interval); };
 
-       for (; i<length; i++) {
 
-         dom.observe(formElements[i], "change", _clearInterval);
 
-       }
 
-       this._observed = true;
 
-     },
 
-     /**
 
-      * Grabs all fields in the dialog and puts them in key=>value style in an object which
 
-      * then gets returned
 
-      */
 
-     _serialize: function() {
 
-       var data    = this.elementToChange || {},
 
-           fields  = this.container.querySelectorAll(SELECTOR_FIELDS),
 
-           length  = fields.length,
 
-           i       = 0;
 
-       for (; i<length; i++) {
 
-         data[fields[i].getAttribute(ATTRIBUTE_FIELDS)] = fields[i].value;
 
-       }
 
-       return data;
 
-     },
 
-     /**
 
-      * Takes the attributes of the "elementToChange"
 
-      * and inserts them in their corresponding dialog input fields
 
-      *
 
-      * Assume the "elementToChange" looks like this:
 
-      *    <a href="http://www.google.com" target="_blank">foo</a>
 
-      *
 
-      * and we have the following dialog:
 
-      *    <input type="text" data-wysihtml5-dialog-field="href" value="">
 
-      *    <input type="text" data-wysihtml5-dialog-field="target" value="">
 
-      *
 
-      * after calling _interpolate() the dialog will look like this
 
-      *    <input type="text" data-wysihtml5-dialog-field="href" value="http://www.google.com">
 
-      *    <input type="text" data-wysihtml5-dialog-field="target" value="_blank">
 
-      *
 
-      * Basically it adopted the attribute values into the corresponding input fields
 
-      *
 
-      */
 
-     _interpolate: function(avoidHiddenFields) {
 
-       var field,
 
-           fieldName,
 
-           newValue,
 
-           focusedElement = document.querySelector(":focus"),
 
-           fields         = this.container.querySelectorAll(SELECTOR_FIELDS),
 
-           length         = fields.length,
 
-           i              = 0;
 
-       for (; i<length; i++) {
 
-         field = fields[i];
 
-         // Never change elements where the user is currently typing in
 
-         if (field === focusedElement) {
 
-           continue;
 
-         }
 
-         // Don't update hidden fields
 
-         // See https://github.com/xing/wysihtml5/pull/14
 
-         if (avoidHiddenFields && field.type === "hidden") {
 
-           continue;
 
-         }
 
-         fieldName = field.getAttribute(ATTRIBUTE_FIELDS);
 
-         newValue  = (this.elementToChange && typeof(this.elementToChange) !== 'boolean') ? (this.elementToChange.getAttribute(fieldName) || "") : field.defaultValue;
 
-         field.value = newValue;
 
-       }
 
-     },
 
-     /**
 
-      * Show the dialog element
 
-      */
 
-     show: function(elementToChange) {
 
-       if (dom.hasClass(this.link, CLASS_NAME_OPENED)) {
 
-         return;
 
-       }
 
-       var that        = this,
 
-           firstField  = this.container.querySelector(SELECTOR_FORM_ELEMENTS);
 
-       this.elementToChange = elementToChange;
 
-       this._observe();
 
-       this._interpolate();
 
-       if (elementToChange) {
 
-         this.interval = setInterval(function() { that._interpolate(true); }, 500);
 
-       }
 
-       dom.addClass(this.link, CLASS_NAME_OPENED);
 
-       this.container.style.display = "";
 
-       this.fire("show");
 
-       if (firstField && !elementToChange) {
 
-         try {
 
-           firstField.focus();
 
-         } catch(e) {}
 
-       }
 
-     },
 
-     /**
 
-      * Hide the dialog element
 
-      */
 
-     hide: function() {
 
-       clearInterval(this.interval);
 
-       this.elementToChange = null;
 
-       dom.removeClass(this.link, CLASS_NAME_OPENED);
 
-       this.container.style.display = "none";
 
-       this.fire("hide");
 
-     }
 
-   });
 
- })(wysihtml5);
 
- ;/**
 
-  * Converts speech-to-text and inserts this into the editor
 
-  * As of now (2011/03/25) this only is supported in Chrome >= 11
 
-  *
 
-  * Note that it sends the recorded audio to the google speech recognition api:
 
-  * http://stackoverflow.com/questions/4361826/does-chrome-have-buil-in-speech-recognition-for-input-type-text-x-webkit-speec
 
-  *
 
-  * Current HTML5 draft can be found here
 
-  * http://lists.w3.org/Archives/Public/public-xg-htmlspeech/2011Feb/att-0020/api-draft.html
 
-  *
 
-  * "Accessing Google Speech API Chrome 11"
 
-  * http://mikepultz.com/2011/03/accessing-google-speech-api-chrome-11/
 
-  */
 
- (function(wysihtml5) {
 
-   var dom = wysihtml5.dom;
 
-   var linkStyles = {
 
-     position: "relative"
 
-   };
 
-   var wrapperStyles = {
 
-     left:     0,
 
-     margin:   0,
 
-     opacity:  0,
 
-     overflow: "hidden",
 
-     padding:  0,
 
-     position: "absolute",
 
-     top:      0,
 
-     zIndex:   1
 
-   };
 
-   var inputStyles = {
 
-     cursor:     "inherit",
 
-     fontSize:   "50px",
 
-     height:     "50px",
 
-     marginTop:  "-25px",
 
-     outline:    0,
 
-     padding:    0,
 
-     position:   "absolute",
 
-     right:      "-4px",
 
-     top:        "50%"
 
-   };
 
-   var inputAttributes = {
 
-     "x-webkit-speech": "",
 
-     "speech":          ""
 
-   };
 
-   wysihtml5.toolbar.Speech = function(parent, link) {
 
-     var input = document.createElement("input");
 
-     if (!wysihtml5.browser.supportsSpeechApiOn(input)) {
 
-       link.style.display = "none";
 
-       return;
 
-     }
 
-     var lang = parent.editor.textarea.element.getAttribute("lang");
 
-     if (lang) {
 
-       inputAttributes.lang = lang;
 
-     }
 
-     var wrapper = document.createElement("div");
 
-     wysihtml5.lang.object(wrapperStyles).merge({
 
-       width:  link.offsetWidth  + "px",
 
-       height: link.offsetHeight + "px"
 
-     });
 
-     dom.insert(input).into(wrapper);
 
-     dom.insert(wrapper).into(link);
 
-     dom.setStyles(inputStyles).on(input);
 
-     dom.setAttributes(inputAttributes).on(input);
 
-     dom.setStyles(wrapperStyles).on(wrapper);
 
-     dom.setStyles(linkStyles).on(link);
 
-     var eventName = "onwebkitspeechchange" in input ? "webkitspeechchange" : "speechchange";
 
-     dom.observe(input, eventName, function() {
 
-       parent.execCommand("insertText", input.value);
 
-       input.value = "";
 
-     });
 
-     dom.observe(input, "click", function(event) {
 
-       if (dom.hasClass(link, "wysihtml5-command-disabled")) {
 
-         event.preventDefault();
 
-       }
 
-       event.stopPropagation();
 
-     });
 
-   };
 
- })(wysihtml5);
 
- ;/**
 
-  * Toolbar
 
-  *
 
-  * @param {Object} parent Reference to instance of Editor instance
 
-  * @param {Element} container Reference to the toolbar container element
 
-  *
 
-  * @example
 
-  *    <div id="toolbar">
 
-  *      <a data-wysihtml5-command="createLink">insert link</a>
 
-  *      <a data-wysihtml5-command="formatBlock" data-wysihtml5-command-value="h1">insert h1</a>
 
-  *    </div>
 
-  *
 
-  *    <script>
 
-  *      var toolbar = new wysihtml5.toolbar.Toolbar(editor, document.getElementById("toolbar"));
 
-  *    </script>
 
-  */
 
- (function(wysihtml5) {
 
-   var CLASS_NAME_COMMAND_DISABLED   = "wysihtml5-command-disabled",
 
-       CLASS_NAME_COMMANDS_DISABLED  = "wysihtml5-commands-disabled",
 
-       CLASS_NAME_COMMAND_ACTIVE     = "wysihtml5-command-active",
 
-       CLASS_NAME_ACTION_ACTIVE      = "wysihtml5-action-active",
 
-       dom                           = wysihtml5.dom;
 
-   wysihtml5.toolbar.Toolbar = Base.extend(
 
-     /** @scope wysihtml5.toolbar.Toolbar.prototype */ {
 
-     constructor: function(editor, container, showOnInit) {
 
-       this.editor     = editor;
 
-       this.container  = typeof(container) === "string" ? document.getElementById(container) : container;
 
-       this.composer   = editor.composer;
 
-       this._getLinks("command");
 
-       this._getLinks("action");
 
-       this._observe();
 
-       if (showOnInit) { this.show(); }
 
-       if (editor.config.classNameCommandDisabled != null) {
 
-         CLASS_NAME_COMMAND_DISABLED = editor.config.classNameCommandDisabled;
 
-       }
 
-       if (editor.config.classNameCommandsDisabled != null) {
 
-         CLASS_NAME_COMMANDS_DISABLED = editor.config.classNameCommandsDisabled;
 
-       }
 
-       if (editor.config.classNameCommandActive != null) {
 
-         CLASS_NAME_COMMAND_ACTIVE = editor.config.classNameCommandActive;
 
-       }
 
-       if (editor.config.classNameActionActive != null) {
 
-         CLASS_NAME_ACTION_ACTIVE = editor.config.classNameActionActive;
 
-       }
 
-       var speechInputLinks  = this.container.querySelectorAll("[data-wysihtml5-command=insertSpeech]"),
 
-           length            = speechInputLinks.length,
 
-           i                 = 0;
 
-       for (; i<length; i++) {
 
-         new wysihtml5.toolbar.Speech(this, speechInputLinks[i]);
 
-       }
 
-     },
 
-     _getLinks: function(type) {
 
-       var links   = this[type + "Links"] = wysihtml5.lang.array(this.container.querySelectorAll("[data-wysihtml5-" + type + "]")).get(),
 
-           length  = links.length,
 
-           i       = 0,
 
-           mapping = this[type + "Mapping"] = {},
 
-           link,
 
-           group,
 
-           name,
 
-           value,
 
-           dialog;
 
-       for (; i<length; i++) {
 
-         link    = links[i];
 
-         name    = link.getAttribute("data-wysihtml5-" + type);
 
-         value   = link.getAttribute("data-wysihtml5-" + type + "-value");
 
-         group   = this.container.querySelector("[data-wysihtml5-" + type + "-group='" + name + "']");
 
-         dialog  = this._getDialog(link, name);
 
-         mapping[name + ":" + value] = {
 
-           link:   link,
 
-           group:  group,
 
-           name:   name,
 
-           value:  value,
 
-           dialog: dialog,
 
-           state:  false
 
-         };
 
-       }
 
-     },
 
-     _getDialog: function(link, command) {
 
-       var that          = this,
 
-           dialogElement = this.container.querySelector("[data-wysihtml5-dialog='" + command + "']"),
 
-           dialog,
 
-           caretBookmark;
 
-       if (dialogElement) {
 
-         if (wysihtml5.toolbar["Dialog_" + command]) {
 
-             dialog = new wysihtml5.toolbar["Dialog_" + command](link, dialogElement);
 
-         } else {
 
-             dialog = new wysihtml5.toolbar.Dialog(link, dialogElement);
 
-         }
 
-         dialog.on("show", function() {
 
-           caretBookmark = that.composer.selection.getBookmark();
 
-           that.editor.fire("show:dialog", { command: command, dialogContainer: dialogElement, commandLink: link });
 
-         });
 
-         dialog.on("save", function(attributes) {
 
-           if (caretBookmark) {
 
-             that.composer.selection.setBookmark(caretBookmark);
 
-           }
 
-           that._execCommand(command, attributes);
 
-           that.editor.fire("save:dialog", { command: command, dialogContainer: dialogElement, commandLink: link });
 
-         });
 
-         dialog.on("cancel", function() {
 
-           that.editor.focus(false);
 
-           that.editor.fire("cancel:dialog", { command: command, dialogContainer: dialogElement, commandLink: link });
 
-         });
 
-       }
 
-       return dialog;
 
-     },
 
-     /**
 
-      * @example
 
-      *    var toolbar = new wysihtml5.Toolbar();
 
-      *    // Insert a <blockquote> element or wrap current selection in <blockquote>
 
-      *    toolbar.execCommand("formatBlock", "blockquote");
 
-      */
 
-     execCommand: function(command, commandValue) {
 
-       if (this.commandsDisabled) {
 
-         return;
 
-       }
 
-       var commandObj = this.commandMapping[command + ":" + commandValue];
 
-       // Show dialog when available
 
-       if (commandObj && commandObj.dialog && !commandObj.state) {
 
-         commandObj.dialog.show();
 
-       } else {
 
-         this._execCommand(command, commandValue);
 
-       }
 
-     },
 
-     _execCommand: function(command, commandValue) {
 
-       // Make sure that composer is focussed (false => don't move caret to the end)
 
-       this.editor.focus(false);
 
-       this.composer.commands.exec(command, commandValue);
 
-       this._updateLinkStates();
 
-     },
 
-     execAction: function(action) {
 
-       var editor = this.editor;
 
-       if (action === "change_view") {
 
-         if (editor.textarea) {
 
-             if (editor.currentView === editor.textarea) {
 
-               editor.fire("change_view", "composer");
 
-             } else {
 
-               editor.fire("change_view", "textarea");
 
-             }
 
-         }
 
-       }
 
-       if (action == "showSource") {
 
-           editor.fire("showSource");
 
-       }
 
-     },
 
-     _observe: function() {
 
-       var that      = this,
 
-           editor    = this.editor,
 
-           container = this.container,
 
-           links     = this.commandLinks.concat(this.actionLinks),
 
-           length    = links.length,
 
-           i         = 0;
 
-       for (; i<length; i++) {
 
-         // 'javascript:;' and unselectable=on Needed for IE, but done in all browsers to make sure that all get the same css applied
 
-         // (you know, a:link { ... } doesn't match anchors with missing href attribute)
 
-         if (links[i].nodeName === "A") {
 
-           dom.setAttributes({
 
-             href:         "javascript:;",
 
-             unselectable: "on"
 
-           }).on(links[i]);
 
-         } else {
 
-           dom.setAttributes({ unselectable: "on" }).on(links[i]);
 
-         }
 
-       }
 
-       // Needed for opera and chrome
 
-       dom.delegate(container, "[data-wysihtml5-command], [data-wysihtml5-action]", "mousedown", function(event) { event.preventDefault(); });
 
-       dom.delegate(container, "[data-wysihtml5-command]", "click", function(event) {
 
-         var link          = this,
 
-             command       = link.getAttribute("data-wysihtml5-command"),
 
-             commandValue  = link.getAttribute("data-wysihtml5-command-value");
 
-         that.execCommand(command, commandValue);
 
-         event.preventDefault();
 
-       });
 
-       dom.delegate(container, "[data-wysihtml5-action]", "click", function(event) {
 
-         var action = this.getAttribute("data-wysihtml5-action");
 
-         that.execAction(action);
 
-         event.preventDefault();
 
-       });
 
-       editor.on("interaction:composer", function() {
 
-           that._updateLinkStates();
 
-       });
 
-       editor.on("focus:composer", function() {
 
-         that.bookmark = null;
 
-       });
 
-       if (this.editor.config.handleTables) {
 
-           editor.on("tableselect:composer", function() {
 
-               that.container.querySelectorAll('[data-wysihtml5-hiddentools="table"]')[0].style.display = "";
 
-           });
 
-           editor.on("tableunselect:composer", function() {
 
-               that.container.querySelectorAll('[data-wysihtml5-hiddentools="table"]')[0].style.display = "none";
 
-           });
 
-       }
 
-       editor.on("change_view", function(currentView) {
 
-         // Set timeout needed in order to let the blur event fire first
 
-         if (editor.textarea) {
 
-             setTimeout(function() {
 
-               that.commandsDisabled = (currentView !== "composer");
 
-               that._updateLinkStates();
 
-               if (that.commandsDisabled) {
 
-                 dom.addClass(container, CLASS_NAME_COMMANDS_DISABLED);
 
-               } else {
 
-                 dom.removeClass(container, CLASS_NAME_COMMANDS_DISABLED);
 
-               }
 
-             }, 0);
 
-         }
 
-       });
 
-     },
 
-     _updateLinkStates: function() {
 
-       var commandMapping    = this.commandMapping,
 
-           actionMapping     = this.actionMapping,
 
-           i,
 
-           state,
 
-           action,
 
-           command;
 
-       // every millisecond counts... this is executed quite often
 
-       for (i in commandMapping) {
 
-         command = commandMapping[i];
 
-         if (this.commandsDisabled) {
 
-           state = false;
 
-           dom.removeClass(command.link, CLASS_NAME_COMMAND_ACTIVE);
 
-           if (command.group) {
 
-             dom.removeClass(command.group, CLASS_NAME_COMMAND_ACTIVE);
 
-           }
 
-           if (command.dialog) {
 
-             command.dialog.hide();
 
-           }
 
-         } else {
 
-           state = this.composer.commands.state(command.name, command.value);
 
-           dom.removeClass(command.link, CLASS_NAME_COMMAND_DISABLED);
 
-           if (command.group) {
 
-             dom.removeClass(command.group, CLASS_NAME_COMMAND_DISABLED);
 
-           }
 
-         }
 
-         if (command.state === state) {
 
-           continue;
 
-         }
 
-         command.state = state;
 
-         if (state) {
 
-           dom.addClass(command.link, CLASS_NAME_COMMAND_ACTIVE);
 
-           if (command.group) {
 
-             dom.addClass(command.group, CLASS_NAME_COMMAND_ACTIVE);
 
-           }
 
-           if (command.dialog) {
 
-             if (typeof(state) === "object" || wysihtml5.lang.object(state).isArray()) {
 
-               if (!command.dialog.multiselect && wysihtml5.lang.object(state).isArray()) {
 
-                 // Grab first and only object/element in state array, otherwise convert state into boolean
 
-                 // to avoid showing a dialog for multiple selected elements which may have different attributes
 
-                 // eg. when two links with different href are selected, the state will be an array consisting of both link elements
 
-                 // but the dialog interface can only update one
 
-                 state = state.length === 1 ? state[0] : true;
 
-                 command.state = state;
 
-               }
 
-               command.dialog.show(state);
 
-             } else {
 
-               command.dialog.hide();
 
-             }
 
-           }
 
-         } else {
 
-           dom.removeClass(command.link, CLASS_NAME_COMMAND_ACTIVE);
 
-           if (command.group) {
 
-             dom.removeClass(command.group, CLASS_NAME_COMMAND_ACTIVE);
 
-           }
 
-           if (command.dialog) {
 
-             command.dialog.hide();
 
-           }
 
-         }
 
-       }
 
-       for (i in actionMapping) {
 
-         action = actionMapping[i];
 
-         if (action.name === "change_view") {
 
-           action.state = this.editor.currentView === this.editor.textarea;
 
-           if (action.state) {
 
-             dom.addClass(action.link, CLASS_NAME_ACTION_ACTIVE);
 
-           } else {
 
-             dom.removeClass(action.link, CLASS_NAME_ACTION_ACTIVE);
 
-           }
 
-         }
 
-       }
 
-     },
 
-     show: function() {
 
-       this.container.style.display = "";
 
-     },
 
-     hide: function() {
 
-       this.container.style.display = "none";
 
-     }
 
-   });
 
- })(wysihtml5);
 
- ;(function(wysihtml5) {
 
-     wysihtml5.toolbar.Dialog_createTable = wysihtml5.toolbar.Dialog.extend({
 
-         show: function(elementToChange) {
 
-             this.base(elementToChange);
 
-         }
 
-     });
 
- })(wysihtml5);
 
- ;(function(wysihtml5) {
 
-   var dom                     = wysihtml5.dom,
 
-       SELECTOR_FIELDS         = "[data-wysihtml5-dialog-field]",
 
-       ATTRIBUTE_FIELDS        = "data-wysihtml5-dialog-field";
 
-   wysihtml5.toolbar.Dialog_foreColorStyle = wysihtml5.toolbar.Dialog.extend({
 
-     multiselect: true,
 
-     _serialize: function() {
 
-       var data    = {},
 
-           fields  = this.container.querySelectorAll(SELECTOR_FIELDS),
 
-           length  = fields.length,
 
-           i       = 0;
 
-       for (; i<length; i++) {
 
-         data[fields[i].getAttribute(ATTRIBUTE_FIELDS)] = fields[i].value;
 
-       }
 
-       return data;
 
-     },
 
-     _interpolate: function(avoidHiddenFields) {
 
-       var field,
 
-           fieldName,
 
-           newValue,
 
-           focusedElement = document.querySelector(":focus"),
 
-           fields         = this.container.querySelectorAll(SELECTOR_FIELDS),
 
-           length         = fields.length,
 
-           i              = 0,
 
-           firstElement   = (this.elementToChange) ? ((wysihtml5.lang.object(this.elementToChange).isArray()) ? this.elementToChange[0] : this.elementToChange) : null,
 
-           colorStr       = (firstElement) ? firstElement.getAttribute('style') : null,
 
-           color          = (colorStr) ? wysihtml5.quirks.styleParser.parseColor(colorStr, "color") : null;
 
-       for (; i<length; i++) {
 
-         field = fields[i];
 
-         // Never change elements where the user is currently typing in
 
-         if (field === focusedElement) {
 
-           continue;
 
-         }
 
-         // Don't update hidden fields3
 
-         if (avoidHiddenFields && field.type === "hidden") {
 
-           continue;
 
-         }
 
-         if (field.getAttribute(ATTRIBUTE_FIELDS) === "color") {
 
-           if (color) {
 
-             if (color[3] && color[3] != 1) {
 
-               field.value = "rgba(" + color[0] + "," + color[1] + "," + color[2] + "," + color[3] + ");";
 
-             } else {
 
-               field.value = "rgb(" + color[0] + "," + color[1] + "," + color[2] + ");";
 
-             }
 
-           } else {
 
-             field.value = "rgb(0,0,0);";
 
-           }
 
-         }
 
-       }
 
-     }
 
-   });
 
- })(wysihtml5);
 
- ;(function(wysihtml5) {
 
-   var dom                     = wysihtml5.dom,
 
-       SELECTOR_FIELDS         = "[data-wysihtml5-dialog-field]",
 
-       ATTRIBUTE_FIELDS        = "data-wysihtml5-dialog-field";
 
-   wysihtml5.toolbar.Dialog_fontSizeStyle = wysihtml5.toolbar.Dialog.extend({
 
-     multiselect: true,
 
-     _serialize: function() {
 
-       return {"size" : this.container.querySelector('[data-wysihtml5-dialog-field="size"]').value};
 
-     },
 
-     _interpolate: function(avoidHiddenFields) {
 
-       var focusedElement = document.querySelector(":focus"),
 
-           field          = this.container.querySelector("[data-wysihtml5-dialog-field='size']"),
 
-           firstElement   = (this.elementToChange) ? ((wysihtml5.lang.object(this.elementToChange).isArray()) ? this.elementToChange[0] : this.elementToChange) : null,
 
-           styleStr       = (firstElement) ? firstElement.getAttribute('style') : null,
 
-           size           = (styleStr) ? wysihtml5.quirks.styleParser.parseFontSize(styleStr) : null;
 
-       if (field && field !== focusedElement && size && !(/^\s*$/).test(size)) {
 
-         field.value = size;
 
-       }
 
-     }
 
-   });
 
- })(wysihtml5);
 
- /*!
 
-  handlebars v1.3.0
 
- Copyright (C) 2011 by Yehuda Katz
 
- Permission is hereby granted, free of charge, to any person obtaining a copy
 
- of this software and associated documentation files (the "Software"), to deal
 
- in the Software without restriction, including without limitation the rights
 
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
- copies of the Software, and to permit persons to whom the Software is
 
- furnished to do so, subject to the following conditions:
 
- The above copyright notice and this permission notice shall be included in
 
- all copies or substantial portions of the Software.
 
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 
- THE SOFTWARE.
 
- @license
 
- */
 
- var Handlebars=function(){var a=function(){"use strict";function a(a){this.string=a}var b;return a.prototype.toString=function(){return""+this.string},b=a}(),b=function(a){"use strict";function b(a){return h[a]||"&"}function c(a,b){for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&(a[c]=b[c])}function d(a){return a instanceof g?a.toString():a||0===a?(a=""+a,j.test(a)?a.replace(i,b):a):""}function e(a){return a||0===a?m(a)&&0===a.length?!0:!1:!0}var f={},g=a,h={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},i=/[&<>"'`]/g,j=/[&<>"'`]/;f.extend=c;var k=Object.prototype.toString;f.toString=k;var l=function(a){return"function"==typeof a};l(/x/)&&(l=function(a){return"function"==typeof a&&"[object Function]"===k.call(a)});var l;f.isFunction=l;var m=Array.isArray||function(a){return a&&"object"==typeof a?"[object Array]"===k.call(a):!1};return f.isArray=m,f.escapeExpression=d,f.isEmpty=e,f}(a),c=function(){"use strict";function a(a,b){var d;b&&b.firstLine&&(d=b.firstLine,a+=" - "+d+":"+b.firstColumn);for(var e=Error.prototype.constructor.call(this,a),f=0;f<c.length;f++)this[c[f]]=e[c[f]];d&&(this.lineNumber=d,this.column=b.firstColumn)}var b,c=["description","fileName","lineNumber","message","name","number","stack"];return a.prototype=new Error,b=a}(),d=function(a,b){"use strict";function c(a,b){this.helpers=a||{},this.partials=b||{},d(this)}function d(a){a.registerHelper("helperMissing",function(a){if(2===arguments.length)return void 0;throw new h("Missing helper: '"+a+"'")}),a.registerHelper("blockHelperMissing",function(b,c){var d=c.inverse||function(){},e=c.fn;return m(b)&&(b=b.call(this)),b===!0?e(this):b===!1||null==b?d(this):l(b)?b.length>0?a.helpers.each(b,c):d(this):e(b)}),a.registerHelper("each",function(a,b){var c,d=b.fn,e=b.inverse,f=0,g="";if(m(a)&&(a=a.call(this)),b.data&&(c=q(b.data)),a&&"object"==typeof a)if(l(a))for(var h=a.length;h>f;f++)c&&(c.index=f,c.first=0===f,c.last=f===a.length-1),g+=d(a[f],{data:c});else for(var i in a)a.hasOwnProperty(i)&&(c&&(c.key=i,c.index=f,c.first=0===f),g+=d(a[i],{data:c}),f++);return 0===f&&(g=e(this)),g}),a.registerHelper("if",function(a,b){return m(a)&&(a=a.call(this)),!b.hash.includeZero&&!a||g.isEmpty(a)?b.inverse(this):b.fn(this)}),a.registerHelper("unless",function(b,c){return a.helpers["if"].call(this,b,{fn:c.inverse,inverse:c.fn,hash:c.hash})}),a.registerHelper("with",function(a,b){return m(a)&&(a=a.call(this)),g.isEmpty(a)?void 0:b.fn(a)}),a.registerHelper("log",function(b,c){var d=c.data&&null!=c.data.level?parseInt(c.data.level,10):1;a.log(d,b)})}function e(a,b){p.log(a,b)}var f={},g=a,h=b,i="1.3.0";f.VERSION=i;var j=4;f.COMPILER_REVISION=j;var k={1:"<= 1.0.rc.2",2:"== 1.0.0-rc.3",3:"== 1.0.0-rc.4",4:">= 1.0.0"};f.REVISION_CHANGES=k;var l=g.isArray,m=g.isFunction,n=g.toString,o="[object Object]";f.HandlebarsEnvironment=c,c.prototype={constructor:c,logger:p,log:e,registerHelper:function(a,b,c){if(n.call(a)===o){if(c||b)throw new h("Arg not supported with multiple helpers");g.extend(this.helpers,a)}else c&&(b.not=c),this.helpers[a]=b},registerPartial:function(a,b){n.call(a)===o?g.extend(this.partials,a):this.partials[a]=b}};var p={methodMap:{0:"debug",1:"info",2:"warn",3:"error"},DEBUG:0,INFO:1,WARN:2,ERROR:3,level:3,log:function(a,b){if(p.level<=a){var c=p.methodMap[a];"undefined"!=typeof console&&console[c]&&console[c].call(console,b)}}};f.logger=p,f.log=e;var q=function(a){var b={};return g.extend(b,a),b};return f.createFrame=q,f}(b,c),e=function(a,b,c){"use strict";function d(a){var b=a&&a[0]||1,c=m;if(b!==c){if(c>b){var d=n[c],e=n[b];throw new l("Template was precompiled with an older version of Handlebars than the current runtime. Please update your precompiler to a newer version ("+d+") or downgrade your runtime to an older version ("+e+").")}throw new l("Template was precompiled with a newer version of Handlebars than the current runtime. Please update your runtime to a newer version ("+a[1]+").")}}function e(a,b){if(!b)throw new l("No environment passed to template");var c=function(a,c,d,e,f,g){var h=b.VM.invokePartial.apply(this,arguments);if(null!=h)return h;if(b.compile){var i={helpers:e,partials:f,data:g};return f[c]=b.compile(a,{data:void 0!==g},b),f[c](d,i)}throw new l("The partial "+c+" could not be compiled when running in runtime-only mode")},d={escapeExpression:k.escapeExpression,invokePartial:c,programs:[],program:function(a,b,c){var d=this.programs[a];return c?d=g(a,b,c):d||(d=this.programs[a]=g(a,b)),d},merge:function(a,b){var c=a||b;return a&&b&&a!==b&&(c={},k.extend(c,b),k.extend(c,a)),c},programWithDepth:b.VM.programWithDepth,noop:b.VM.noop,compilerInfo:null};return function(c,e){e=e||{};var f,g,h=e.partial?e:b;e.partial||(f=e.helpers,g=e.partials);var i=a.call(d,h,c,f,g,e.data);return e.partial||b.VM.checkRevision(d.compilerInfo),i}}function f(a,b,c){var d=Array.prototype.slice.call(arguments,3),e=function(a,e){return e=e||{},b.apply(this,[a,e.data||c].concat(d))};return e.program=a,e.depth=d.length,e}function g(a,b,c){var d=function(a,d){return d=d||{},b(a,d.data||c)};return d.program=a,d.depth=0,d}function h(a,b,c,d,e,f){var g={partial:!0,helpers:d,partials:e,data:f};if(void 0===a)throw new l("The partial "+b+" could not be found");return a instanceof Function?a(c,g):void 0}function i(){return""}var j={},k=a,l=b,m=c.COMPILER_REVISION,n=c.REVISION_CHANGES;return j.checkRevision=d,j.template=e,j.programWithDepth=f,j.program=g,j.invokePartial=h,j.noop=i,j}(b,c,d),f=function(a,b,c,d,e){"use strict";var f,g=a,h=b,i=c,j=d,k=e,l=function(){var a=new g.HandlebarsEnvironment;return j.extend(a,g),a.SafeString=h,a.Exception=i,a.Utils=j,a.VM=k,a.template=function(b){return k.template(b,a)},a},m=l();return m.create=l,f=m}(d,a,c,b,e);return f}();this["wysihtml5"] = this["wysihtml5"] || {};
 
- this["wysihtml5"]["tpl"] = this["wysihtml5"]["tpl"] || {};
 
- this["wysihtml5"]["tpl"]["blockquote"] = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
 
-   this.compilerInfo = [4,'>= 1.0.0'];
 
- helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
 
-   var buffer = "", stack1, functionType="function", escapeExpression=this.escapeExpression, self=this;
 
- function program1(depth0,data) {
 
-   
 
-   var buffer = "", stack1;
 
-   buffer += "btn-"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.size)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1));
 
-   return buffer;
 
-   }
 
- function program3(depth0,data) {
 
-   
 
-   
 
-   return " \n      <span class=\"fa fa-quote-left\"></span>\n    ";
 
-   }
 
- function program5(depth0,data) {
 
-   
 
-   
 
-   return "\n      <span class=\"glyphicon glyphicon-quote\"></span>\n    ";
 
-   }
 
-   buffer += "<li>\n  <a class=\"btn ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.size), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += " btn-default\" data-wysihtml5-command=\"formatBlock\" data-wysihtml5-command-value=\"blockquote\" data-wysihtml5-display-format-name=\"false\" tabindex=\"-1\">\n    ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.fa), {hash:{},inverse:self.program(5, program5, data),fn:self.program(3, program3, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += "\n  </a>\n</li>\n";
 
-   return buffer;
 
-   });
 
- this["wysihtml5"]["tpl"]["color"] = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
 
-   this.compilerInfo = [4,'>= 1.0.0'];
 
- helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
 
-   var buffer = "", stack1, functionType="function", escapeExpression=this.escapeExpression, self=this;
 
- function program1(depth0,data) {
 
-   
 
-   var buffer = "", stack1;
 
-   buffer += "btn-"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.size)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1));
 
-   return buffer;
 
-   }
 
-   buffer += "<li class=\"dropdown\">\n  <a class=\"btn btn-default dropdown-toggle ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.size), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += "\" data-toggle=\"dropdown\" tabindex=\"-1\">\n    <span class=\"current-color\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.colours)),stack1 == null || stack1 === false ? stack1 : stack1.black)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</span>\n    <b class=\"caret\"></b>\n  </a>\n  <ul class=\"dropdown-menu\">\n    <li><div class=\"wysihtml5-colors\" data-wysihtml5-command-value=\"black\"></div><a class=\"wysihtml5-colors-title\" data-wysihtml5-command=\"foreColor\" data-wysihtml5-command-value=\"black\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.colours)),stack1 == null || stack1 === false ? stack1 : stack1.black)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a></li>\n    <li><div class=\"wysihtml5-colors\" data-wysihtml5-command-value=\"silver\"></div><a class=\"wysihtml5-colors-title\" data-wysihtml5-command=\"foreColor\" data-wysihtml5-command-value=\"silver\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.colours)),stack1 == null || stack1 === false ? stack1 : stack1.silver)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a></li>\n    <li><div class=\"wysihtml5-colors\" data-wysihtml5-command-value=\"gray\"></div><a class=\"wysihtml5-colors-title\" data-wysihtml5-command=\"foreColor\" data-wysihtml5-command-value=\"gray\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.colours)),stack1 == null || stack1 === false ? stack1 : stack1.gray)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a></li>\n    <li><div class=\"wysihtml5-colors\" data-wysihtml5-command-value=\"maroon\"></div><a class=\"wysihtml5-colors-title\" data-wysihtml5-command=\"foreColor\" data-wysihtml5-command-value=\"maroon\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.colours)),stack1 == null || stack1 === false ? stack1 : stack1.maroon)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a></li>\n    <li><div class=\"wysihtml5-colors\" data-wysihtml5-command-value=\"red\"></div><a class=\"wysihtml5-colors-title\" data-wysihtml5-command=\"foreColor\" data-wysihtml5-command-value=\"red\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.colours)),stack1 == null || stack1 === false ? stack1 : stack1.red)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a></li>\n    <li><div class=\"wysihtml5-colors\" data-wysihtml5-command-value=\"purple\"></div><a class=\"wysihtml5-colors-title\" data-wysihtml5-command=\"foreColor\" data-wysihtml5-command-value=\"purple\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.colours)),stack1 == null || stack1 === false ? stack1 : stack1.purple)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a></li>\n    <li><div class=\"wysihtml5-colors\" data-wysihtml5-command-value=\"green\"></div><a class=\"wysihtml5-colors-title\" data-wysihtml5-command=\"foreColor\" data-wysihtml5-command-value=\"green\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.colours)),stack1 == null || stack1 === false ? stack1 : stack1.green)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a></li>\n    <li><div class=\"wysihtml5-colors\" data-wysihtml5-command-value=\"olive\"></div><a class=\"wysihtml5-colors-title\" data-wysihtml5-command=\"foreColor\" data-wysihtml5-command-value=\"olive\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.colours)),stack1 == null || stack1 === false ? stack1 : stack1.olive)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a></li>\n    <li><div class=\"wysihtml5-colors\" data-wysihtml5-command-value=\"navy\"></div><a class=\"wysihtml5-colors-title\" data-wysihtml5-command=\"foreColor\" data-wysihtml5-command-value=\"navy\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.colours)),stack1 == null || stack1 === false ? stack1 : stack1.navy)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a></li>\n    <li><div class=\"wysihtml5-colors\" data-wysihtml5-command-value=\"blue\"></div><a class=\"wysihtml5-colors-title\" data-wysihtml5-command=\"foreColor\" data-wysihtml5-command-value=\"blue\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.colours)),stack1 == null || stack1 === false ? stack1 : stack1.blue)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a></li>\n    <li><div class=\"wysihtml5-colors\" data-wysihtml5-command-value=\"orange\"></div><a class=\"wysihtml5-colors-title\" data-wysihtml5-command=\"foreColor\" data-wysihtml5-command-value=\"orange\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.colours)),stack1 == null || stack1 === false ? stack1 : stack1.orange)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a></li>\n  </ul>\n</li>\n";
 
-   return buffer;
 
-   });
 
- this["wysihtml5"]["tpl"]["emphasis"] = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
 
-   this.compilerInfo = [4,'>= 1.0.0'];
 
- helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
 
-   var buffer = "", stack1, functionType="function", escapeExpression=this.escapeExpression, self=this;
 
- function program1(depth0,data) {
 
-   
 
-   var buffer = "", stack1;
 
-   buffer += "btn-"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.size)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1));
 
-   return buffer;
 
-   }
 
- function program3(depth0,data) {
 
-   
 
-   var buffer = "", stack1;
 
-   buffer += "\n    <a class=\"btn ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.size), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += " btn-default\" data-wysihtml5-command=\"small\" title=\"CTRL+S\" tabindex=\"-1\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.emphasis)),stack1 == null || stack1 === false ? stack1 : stack1.small)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a>\n    ";
 
-   return buffer;
 
-   }
 
-   buffer += "<li>\n  <div class=\"btn-group\">\n    <a class=\"btn ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.size), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += " btn-default\" data-wysihtml5-command=\"bold\" title=\"CTRL+B\" tabindex=\"-1\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.emphasis)),stack1 == null || stack1 === false ? stack1 : stack1.bold)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a>\n    <a class=\"btn ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.size), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += " btn-default\" data-wysihtml5-command=\"italic\" title=\"CTRL+I\" tabindex=\"-1\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.emphasis)),stack1 == null || stack1 === false ? stack1 : stack1.italic)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a>\n    <a class=\"btn ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.size), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += " btn-default\" data-wysihtml5-command=\"underline\" title=\"CTRL+U\" tabindex=\"-1\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.emphasis)),stack1 == null || stack1 === false ? stack1 : stack1.underline)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a>\n    ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.emphasis)),stack1 == null || stack1 === false ? stack1 : stack1.small), {hash:{},inverse:self.noop,fn:self.program(3, program3, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += "\n  </div>\n</li>\n";
 
-   return buffer;
 
-   });
 
- this["wysihtml5"]["tpl"]["font-styles"] = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
 
-   this.compilerInfo = [4,'>= 1.0.0'];
 
- helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
 
-   var buffer = "", stack1, functionType="function", escapeExpression=this.escapeExpression, self=this;
 
- function program1(depth0,data) {
 
-   
 
-   var buffer = "", stack1;
 
-   buffer += "btn-"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.size)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1));
 
-   return buffer;
 
-   }
 
- function program3(depth0,data) {
 
-   
 
-   
 
-   return "\n      <span class=\"fa fa-font\"></span>\n    ";
 
-   }
 
- function program5(depth0,data) {
 
-   
 
-   
 
-   return "\n      <span class=\"glyphicon glyphicon-font\"></span>\n    ";
 
-   }
 
-   buffer += "<li class=\"dropdown\">\n  <a class=\"btn btn-default dropdown-toggle ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.size), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += "\" data-toggle=\"dropdown\">\n    ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.fa), {hash:{},inverse:self.program(5, program5, data),fn:self.program(3, program3, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += "\n    <span class=\"current-font\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.font_styles)),stack1 == null || stack1 === false ? stack1 : stack1.normal)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</span>\n    <b class=\"caret\"></b>\n  </a>\n  <ul class=\"dropdown-menu\">\n    <li><a data-wysihtml5-command=\"formatBlock\" data-wysihtml5-command-value=\"p\" tabindex=\"-1\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.font_styles)),stack1 == null || stack1 === false ? stack1 : stack1.normal)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a></li>\n    <li><a data-wysihtml5-command=\"formatBlock\" data-wysihtml5-command-value=\"h1\" tabindex=\"-1\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.font_styles)),stack1 == null || stack1 === false ? stack1 : stack1.h1)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a></li>\n    <li><a data-wysihtml5-command=\"formatBlock\" data-wysihtml5-command-value=\"h2\" tabindex=\"-1\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.font_styles)),stack1 == null || stack1 === false ? stack1 : stack1.h2)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a></li>\n    <li><a data-wysihtml5-command=\"formatBlock\" data-wysihtml5-command-value=\"h3\" tabindex=\"-1\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.font_styles)),stack1 == null || stack1 === false ? stack1 : stack1.h3)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a></li>\n    <li><a data-wysihtml5-command=\"formatBlock\" data-wysihtml5-command-value=\"h4\" tabindex=\"-1\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.font_styles)),stack1 == null || stack1 === false ? stack1 : stack1.h4)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a></li>\n    <li><a data-wysihtml5-command=\"formatBlock\" data-wysihtml5-command-value=\"h5\" tabindex=\"-1\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.font_styles)),stack1 == null || stack1 === false ? stack1 : stack1.h5)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a></li>\n    <li><a data-wysihtml5-command=\"formatBlock\" data-wysihtml5-command-value=\"h6\" tabindex=\"-1\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.font_styles)),stack1 == null || stack1 === false ? stack1 : stack1.h6)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a></li>\n  </ul>\n</li>\n";
 
-   return buffer;
 
-   });
 
- this["wysihtml5"]["tpl"]["html"] = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
 
-   this.compilerInfo = [4,'>= 1.0.0'];
 
- helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
 
-   var buffer = "", stack1, functionType="function", escapeExpression=this.escapeExpression, self=this;
 
- function program1(depth0,data) {
 
-   
 
-   var buffer = "", stack1;
 
-   buffer += "btn-"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.size)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1));
 
-   return buffer;
 
-   }
 
- function program3(depth0,data) {
 
-   
 
-   
 
-   return "\n        <span class=\"fa fa-pencil\"></span>\n      ";
 
-   }
 
- function program5(depth0,data) {
 
-   
 
-   
 
-   return "\n        <span class=\"glyphicon glyphicon-pencil\"></span>\n      ";
 
-   }
 
-   buffer += "<li>\n  <div class=\"btn-group\">\n    <a class=\"btn ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.size), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += " btn-default\" data-wysihtml5-action=\"change_view\" title=\""
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.html)),stack1 == null || stack1 === false ? stack1 : stack1.edit)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "\" tabindex=\"-1\">\n      ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.fa), {hash:{},inverse:self.program(5, program5, data),fn:self.program(3, program3, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += "\n    </a>\n  </div>\n</li>\n";
 
-   return buffer;
 
-   });
 
- this["wysihtml5"]["tpl"]["image"] = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
 
-   this.compilerInfo = [4,'>= 1.0.0'];
 
- helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
 
-   var buffer = "", stack1, functionType="function", escapeExpression=this.escapeExpression, self=this;
 
- function program1(depth0,data) {
 
-   
 
-   
 
-   return "modal-sm";
 
-   }
 
- function program3(depth0,data) {
 
-   
 
-   var buffer = "", stack1;
 
-   buffer += "btn-"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.size)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1));
 
-   return buffer;
 
-   }
 
- function program5(depth0,data) {
 
-   
 
-   
 
-   return "\n      <span class=\"fa fa-file-image-o\"></span>\n    ";
 
-   }
 
- function program7(depth0,data) {
 
-   
 
-   
 
-   return "\n      <span class=\"glyphicon glyphicon-picture\"></span>\n    ";
 
-   }
 
-   buffer += "<li>\n  <div class=\"bootstrap-wysihtml5-insert-image-modal modal fade\" data-wysihtml5-dialog=\"insertImage\">\n    <div class=\"modal-dialog ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.smallmodals), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += "\">\n      <div class=\"modal-content\">\n        <div class=\"modal-header\">\n          <a class=\"close\" data-dismiss=\"modal\">×</a>\n          <h3>"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.image)),stack1 == null || stack1 === false ? stack1 : stack1.insert)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</h3>\n        </div>\n        <div class=\"modal-body\">\n          <div class=\"form-group\">\n            <input value=\"http://\" class=\"bootstrap-wysihtml5-insert-image-url form-control\" data-wysihtml5-dialog-field=\"src\">\n          </div> \n        </div>\n        <div class=\"modal-footer\">\n          <a class=\"btn btn-default\" data-dismiss=\"modal\" data-wysihtml5-dialog-action=\"cancel\" href=\"#\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.image)),stack1 == null || stack1 === false ? stack1 : stack1.cancel)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a>\n          <a class=\"btn btn-primary\" data-dismiss=\"modal\"  data-wysihtml5-dialog-action=\"save\" href=\"#\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.image)),stack1 == null || stack1 === false ? stack1 : stack1.insert)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a>\n        </div>\n      </div>\n    </div>\n  </div>\n  <a class=\"btn ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.size), {hash:{},inverse:self.noop,fn:self.program(3, program3, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += " btn-default\" data-wysihtml5-command=\"insertImage\" title=\""
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.image)),stack1 == null || stack1 === false ? stack1 : stack1.insert)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "\" tabindex=\"-1\">\n    ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.fa), {hash:{},inverse:self.program(7, program7, data),fn:self.program(5, program5, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += "\n  </a>\n</li>\n";
 
-   return buffer;
 
-   });
 
- this["wysihtml5"]["tpl"]["link"] = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
 
-   this.compilerInfo = [4,'>= 1.0.0'];
 
- helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
 
-   var buffer = "", stack1, functionType="function", escapeExpression=this.escapeExpression, self=this;
 
- function program1(depth0,data) {
 
-   
 
-   
 
-   return "modal-sm";
 
-   }
 
- function program3(depth0,data) {
 
-   
 
-   var buffer = "", stack1;
 
-   buffer += "btn-"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.size)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1));
 
-   return buffer;
 
-   }
 
- function program5(depth0,data) {
 
-   
 
-   
 
-   return "\n      <span class=\"fa fa-share-square-o\"></span>\n    ";
 
-   }
 
- function program7(depth0,data) {
 
-   
 
-   
 
-   return "\n      <span class=\"glyphicon glyphicon-share\"></span>\n    ";
 
-   }
 
-   buffer += "<li>\n  <div class=\"bootstrap-wysihtml5-insert-link-modal modal fade\" data-wysihtml5-dialog=\"createLink\">\n    <div class=\"modal-dialog ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.smallmodals), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += "\">\n      <div class=\"modal-content\">\n        <div class=\"modal-header\">\n          <a class=\"close\" data-dismiss=\"modal\">×</a>\n          <h3>"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.link)),stack1 == null || stack1 === false ? stack1 : stack1.insert)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</h3>\n        </div>\n        <div class=\"modal-body\">\n          <div class=\"form-group\">\n            <input value=\"http://\" class=\"bootstrap-wysihtml5-insert-link-url form-control\" data-wysihtml5-dialog-field=\"href\">\n          </div> \n          <div class=\"checkbox\">\n            <label> \n              <input type=\"checkbox\" class=\"bootstrap-wysihtml5-insert-link-target\" checked>"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.link)),stack1 == null || stack1 === false ? stack1 : stack1.target)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "\n            </label>\n          </div>\n        </div>\n        <div class=\"modal-footer\">\n          <a class=\"btn btn-default\" data-dismiss=\"modal\" data-wysihtml5-dialog-action=\"cancel\" href=\"#\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.link)),stack1 == null || stack1 === false ? stack1 : stack1.cancel)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a>\n          <a href=\"#\" class=\"btn btn-primary\" data-dismiss=\"modal\" data-wysihtml5-dialog-action=\"save\">"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.link)),stack1 == null || stack1 === false ? stack1 : stack1.insert)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "</a>\n        </div>\n      </div>\n    </div>\n  </div>\n  <a class=\"btn ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.size), {hash:{},inverse:self.noop,fn:self.program(3, program3, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += " btn-default\" data-wysihtml5-command=\"createLink\" title=\""
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.link)),stack1 == null || stack1 === false ? stack1 : stack1.insert)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "\" tabindex=\"-1\">\n    ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.fa), {hash:{},inverse:self.program(7, program7, data),fn:self.program(5, program5, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += "\n  </a>\n</li>\n";
 
-   return buffer;
 
-   });
 
- this["wysihtml5"]["tpl"]["lists"] = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
 
-   this.compilerInfo = [4,'>= 1.0.0'];
 
- helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
 
-   var buffer = "", stack1, functionType="function", escapeExpression=this.escapeExpression, self=this;
 
- function program1(depth0,data) {
 
-   
 
-   var buffer = "", stack1;
 
-   buffer += "btn-"
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.size)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1));
 
-   return buffer;
 
-   }
 
- function program3(depth0,data) {
 
-   
 
-   
 
-   return "\n      <span class=\"fa fa-list-ul\"></span>\n    ";
 
-   }
 
- function program5(depth0,data) {
 
-   
 
-   
 
-   return "\n      <span class=\"glyphicon glyphicon-list\"></span>\n    ";
 
-   }
 
- function program7(depth0,data) {
 
-   
 
-   
 
-   return "\n      <span class=\"fa fa-list-ol\"></span>\n    ";
 
-   }
 
- function program9(depth0,data) {
 
-   
 
-   
 
-   return "\n      <span class=\"glyphicon glyphicon-th-list\"></span>\n    ";
 
-   }
 
- function program11(depth0,data) {
 
-   
 
-   
 
-   return "\n      <span class=\"fa fa-outdent\"></span>\n    ";
 
-   }
 
- function program13(depth0,data) {
 
-   
 
-   
 
-   return "\n      <span class=\"glyphicon glyphicon-indent-right\"></span>\n    ";
 
-   }
 
- function program15(depth0,data) {
 
-   
 
-   
 
-   return "\n      <span class=\"fa fa-indent\"></span>\n    ";
 
-   }
 
- function program17(depth0,data) {
 
-   
 
-   
 
-   return "\n      <span class=\"glyphicon glyphicon-indent-left\"></span>\n    ";
 
-   }
 
-   buffer += "<li>\n  <div class=\"btn-group\">\n    <a class=\"btn ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.size), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += " btn-default\" data-wysihtml5-command=\"insertUnorderedList\" title=\""
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.lists)),stack1 == null || stack1 === false ? stack1 : stack1.unordered)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "\" tabindex=\"-1\">\n    ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.fa), {hash:{},inverse:self.program(5, program5, data),fn:self.program(3, program3, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += "\n    </a>\n    <a class=\"btn ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.size), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += " btn-default\" data-wysihtml5-command=\"insertOrderedList\" title=\""
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.lists)),stack1 == null || stack1 === false ? stack1 : stack1.ordered)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "\" tabindex=\"-1\">\n    ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.fa), {hash:{},inverse:self.program(9, program9, data),fn:self.program(7, program7, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += "\n    </a>\n    <a class=\"btn ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.size), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += " btn-default\" data-wysihtml5-command=\"Outdent\" title=\""
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.lists)),stack1 == null || stack1 === false ? stack1 : stack1.outdent)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "\" tabindex=\"-1\">\n    ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.fa), {hash:{},inverse:self.program(13, program13, data),fn:self.program(11, program11, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += "\n    </a>\n    <a class=\"btn ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.size), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += " btn-default\" data-wysihtml5-command=\"Indent\" title=\""
 
-     + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.locale)),stack1 == null || stack1 === false ? stack1 : stack1.lists)),stack1 == null || stack1 === false ? stack1 : stack1.indent)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
 
-     + "\" tabindex=\"-1\">\n    ";
 
-   stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 && depth0.options)),stack1 == null || stack1 === false ? stack1 : stack1.toolbar)),stack1 == null || stack1 === false ? stack1 : stack1.fa), {hash:{},inverse:self.program(17, program17, data),fn:self.program(15, program15, data),data:data});
 
-   if(stack1 || stack1 === 0) { buffer += stack1; }
 
-   buffer += "\n    </a>\n  </div>\n</li>\n";
 
-   return buffer;
 
-   });(function (factory) {
 
-   'use strict';
 
-   if (typeof define === 'function' && define.amd) {
 
-     // AMD. Register as an anonymous module.
 
-     define('bootstrap.wysihtml5', ['jquery', 'wysihtml5', 'bootstrap', 'bootstrap.wysihtml5.templates', 'bootstrap.wysihtml5.commands'], factory);
 
-   } else {
 
-     // Browser globals
 
-     factory(jQuery, wysihtml5); // jshint ignore:line
 
-   }
 
- }(function ($, wysihtml5) {
 
-   'use strict';
 
-   var bsWysihtml5 = function($, wysihtml5) {
 
-     var templates = function(key, locale, options) {
 
-       if(wysihtml5.tpl[key]) {
 
-         return wysihtml5.tpl[key]({locale: locale, options: options});
 
-       }
 
-     };
 
-     var Wysihtml5 = function(el, options) {
 
-       this.el = el;
 
-       var toolbarOpts = $.extend(true, {}, defaultOptions, options);
 
-       for(var t in toolbarOpts.customTemplates) {
 
-         if (toolbarOpts.customTemplates.hasOwnProperty(t)) {
 
-           wysihtml5.tpl[t] = toolbarOpts.customTemplates[t];
 
-         }
 
-       }
 
-       this.toolbar = this.createToolbar(el, toolbarOpts);
 
-       this.editor =  this.createEditor(toolbarOpts);
 
-     };
 
-     Wysihtml5.prototype = {
 
-       constructor: Wysihtml5,
 
-       createEditor: function(options) {
 
-         options = options || {};
 
-         // Add the toolbar to a clone of the options object so multiple instances
 
-         // of the WYISYWG don't break because 'toolbar' is already defined
 
-         options = $.extend(true, {}, options);
 
-         options.toolbar = this.toolbar[0];
 
-         
 
-         this.initializeEditor(this.el[0], options);
 
-       },
 
-       initializeEditor: function(el, options) {
 
-         var editor = new wysihtml5.Editor(this.el[0], options);
 
-         editor.on('beforeload', this.syncBootstrapDialogEvents);
 
-         editor.on('beforeload', this.loadParserRules);
 
-         // #30 - body is in IE 10 not created by default, which leads to nullpointer
 
-         // 2014/02/13 - adapted to wysihtml5-0.4, does not work in IE
 
-         if(editor.composer.editableArea.contentDocument) {
 
-           this.addMoreShortcuts(editor, 
 
-                                 editor.composer.editableArea.contentDocument.body || editor.composer.editableArea.contentDocument, 
 
-                                 options.shortcuts);
 
-         } else {
 
-           this.addMoreShortcuts(editor, editor.composer.editableArea, options.shortcuts);    
 
-         }
 
-         if(options && options.events) {
 
-           for(var eventName in options.events) {
 
-             if (options.events.hasOwnProperty(eventName)) {
 
-               editor.on(eventName, options.events[eventName]);
 
-             }
 
-           }
 
-         }
 
-         return editor;
 
-       },
 
-       loadParserRules: function() {
 
-         if($.type(this.config.parserRules) === 'string') {
 
-           $.ajax({
 
-             dataType: 'json',
 
-             url: this.config.parserRules,
 
-             context: this,
 
-             error: function (jqXHR, textStatus, errorThrown) {
 
-               console.log(errorThrown);
 
-             },
 
-             success: function (parserRules) {
 
-               this.config.parserRules = parserRules;
 
-               console.log('parserrules loaded');
 
-             }
 
-           });
 
-         }
 
-         if(this.config.pasteParserRulesets && $.type(this.config.pasteParserRulesets) === 'string') {
 
-           $.ajax({
 
-             dataType: 'json',
 
-             url: this.config.pasteParserRulesets,
 
-             context: this,
 
-             error: function (jqXHR, textStatus, errorThrown) {
 
-               console.log(errorThrown);
 
-             },
 
-             success: function (pasteParserRulesets) {
 
-               this.config.pasteParserRulesets = pasteParserRulesets;
 
-             }
 
-           });
 
-         }
 
-       },
 
-       //sync wysihtml5 events for dialogs with bootstrap events
 
-       syncBootstrapDialogEvents: function() {
 
-         var editor = this;
 
-         $.map(this.toolbar.commandMapping, function(value) {
 
-           return [value];
 
-         }).filter(function(commandObj) {
 
-           return commandObj.dialog;
 
-         }).map(function(commandObj) {
 
-           return commandObj.dialog;
 
-         }).forEach(function(dialog) {
 
-           dialog.on('show', function() {
 
-             $(this.container).modal('show');
 
-           });
 
-           dialog.on('hide', function() {
 
-             $(this.container).modal('hide');
 
-             setTimeout(editor.composer.focus, 0);
 
-           });
 
-           $(dialog.container).on('shown.bs.modal', function () {
 
-             $(this).find('input, select, textarea').first().focus();
 
-           });
 
-         });
 
-         this.on('change_view', function() {
 
-           $(this.toolbar.container.children).find('a.btn').not('[data-wysihtml5-action="change_view"]').toggleClass('disabled');
 
-         });
 
-       },
 
-       createToolbar: function(el, options) {
 
-         var self = this;
 
-         var toolbar = $('<ul/>', {
 
-           'class' : 'wysihtml5-toolbar',
 
-           'style': 'display:none'
 
-         });
 
-         var culture = options.locale || defaultOptions.locale || 'en';
 
-         if(!locale.hasOwnProperty(culture)) {
 
-           console.debug('Locale \'' + culture + '\' not found. Available locales are: ' + Object.keys(locale) + '. Falling back to \'en\'.');
 
-           culture = 'en';
 
-         }
 
-         var localeObject = $.extend(true, {}, locale.en, locale[culture]);
 
-         for(var key in options.toolbar) {
 
-           if(options.toolbar[key]) {
 
-             toolbar.append(templates(key, localeObject, options));
 
-           }
 
-         }
 
-         toolbar.find('a[data-wysihtml5-command="formatBlock"]').click(function(e) {
 
-           var target = e.delegateTarget || e.target || e.srcElement,
 
-           el = $(target),
 
-           showformat = el.data('wysihtml5-display-format-name'),
 
-           formatname = el.data('wysihtml5-format-name') || el.html();
 
-           if(showformat === undefined || showformat === 'true') {
 
-             self.toolbar.find('.current-font').text(formatname);
 
-           }
 
-         });
 
-         toolbar.find('a[data-wysihtml5-command="foreColor"]').click(function(e) {
 
-           var target = e.target || e.srcElement;
 
-           var el = $(target);
 
-           self.toolbar.find('.current-color').text(el.html());
 
-         });
 
-         this.el.before(toolbar);
 
-         return toolbar;
 
-       },
 
-       addMoreShortcuts: function(editor, el, shortcuts) {
 
-         /* some additional shortcuts */
 
-         wysihtml5.dom.observe(el, 'keydown', function(event) {
 
-           var keyCode  = event.keyCode,
 
-           command  = shortcuts[keyCode];
 
-           if ((event.ctrlKey || event.metaKey || event.altKey) && command && wysihtml5.commands[command]) {
 
-             var commandObj = editor.toolbar.commandMapping[command + ':null'];
 
-             if (commandObj && commandObj.dialog && !commandObj.state) {
 
-               commandObj.dialog.show();
 
-             } else {
 
-               wysihtml5.commands[command].exec(editor.composer, command);
 
-             }
 
-             event.preventDefault();
 
-           }
 
-         });
 
-       }
 
-     };
 
-     // these define our public api
 
-     var methods = {
 
-       resetDefaults: function() {
 
-         $.fn.wysihtml5.defaultOptions = $.extend(true, {}, $.fn.wysihtml5.defaultOptionsCache);
 
-       },
 
-       bypassDefaults: function(options) {
 
-         return this.each(function () {
 
-           var $this = $(this);
 
-           $this.data('wysihtml5', new Wysihtml5($this, options));
 
-         });
 
-       },
 
-       shallowExtend: function (options) {
 
-         var settings = $.extend({}, $.fn.wysihtml5.defaultOptions, options || {}, $(this).data());
 
-         var that = this;
 
-         return methods.bypassDefaults.apply(that, [settings]);
 
-       },
 
-       deepExtend: function(options) {
 
-         var settings = $.extend(true, {}, $.fn.wysihtml5.defaultOptions, options || {});
 
-         var that = this;
 
-         return methods.bypassDefaults.apply(that, [settings]);
 
-       },
 
-       init: function(options) {
 
-         var that = this;
 
-         return methods.shallowExtend.apply(that, [options]);
 
-       }
 
-     };
 
-     $.fn.wysihtml5 = function ( method ) {
 
-       if ( methods[method] ) {
 
-         return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
 
-       } else if ( typeof method === 'object' || ! method ) {
 
-         return methods.init.apply( this, arguments );
 
-       } else {
 
-         $.error( 'Method ' +  method + ' does not exist on jQuery.wysihtml5' );
 
-       }    
 
-     };
 
-     $.fn.wysihtml5.Constructor = Wysihtml5;
 
-     var defaultOptions = $.fn.wysihtml5.defaultOptions = {
 
-       toolbar: {
 
-         'font-styles': true,
 
-         'color': false,
 
-         'emphasis': {
 
-           'small': true
 
-         },
 
-         'blockquote': true,
 
-         'lists': true,
 
-         'html': false,
 
-         'link': true,
 
-         'image': true,
 
-         'smallmodals': false
 
-       },
 
-       useLineBreaks: false,
 
-       parserRules: {
 
-         classes: {
 
-           'wysiwyg-color-silver' : 1,
 
-           'wysiwyg-color-gray' : 1,
 
-           'wysiwyg-color-white' : 1,
 
-           'wysiwyg-color-maroon' : 1,
 
-           'wysiwyg-color-red' : 1,
 
-           'wysiwyg-color-purple' : 1,
 
-           'wysiwyg-color-fuchsia' : 1,
 
-           'wysiwyg-color-green' : 1,
 
-           'wysiwyg-color-lime' : 1,
 
-           'wysiwyg-color-olive' : 1,
 
-           'wysiwyg-color-yellow' : 1,
 
-           'wysiwyg-color-navy' : 1,
 
-           'wysiwyg-color-blue' : 1,
 
-           'wysiwyg-color-teal' : 1,
 
-           'wysiwyg-color-aqua' : 1,
 
-           'wysiwyg-color-orange' : 1
 
-         },
 
-         tags: {
 
-           'b':  {},
 
-           'i':  {},
 
-           'strong': {},
 
-           'em': {},
 
-           'p': {},
 
-           'br': {},
 
-           'ol': {},
 
-           'ul': {},
 
-           'li': {},
 
-           'h1': {},
 
-           'h2': {},
 
-           'h3': {},
 
-           'h4': {},
 
-           'h5': {},
 
-           'h6': {},
 
-           'blockquote': {},
 
-           'u': 1,
 
-           'img': {
 
-             'check_attributes': {
 
-               'width': 'numbers',
 
-               'alt': 'alt',
 
-               'src': 'url',
 
-               'height': 'numbers'
 
-             }
 
-           },
 
-           'a':  {
 
-             'check_attributes': {
 
-               'href': 'url'
 
-             },
 
-             'set_attributes': {
 
-               'target': '_blank',
 
-               'rel': 'nofollow'
 
-             }
 
-           },
 
-           'span': 1,
 
-           'div': 1,
 
-           'small': 1,
 
-           'code': 1,
 
-           'pre': 1
 
-         }
 
-       },
 
-       locale: 'en',
 
-       shortcuts: {
 
-         '83': 'small',// S
 
-         '75': 'createLink'// K
 
-       }
 
-     };
 
-     if (typeof $.fn.wysihtml5.defaultOptionsCache === 'undefined') {
 
-       $.fn.wysihtml5.defaultOptionsCache = $.extend(true, {}, $.fn.wysihtml5.defaultOptions);
 
-     }
 
-     var locale = $.fn.wysihtml5.locale = {};
 
-   };
 
-   bsWysihtml5($, wysihtml5);
 
- }));
 
- (function(wysihtml5) {
 
-   wysihtml5.commands.small = {
 
-     exec: function(composer, command) {
 
-       return wysihtml5.commands.formatInline.exec(composer, command, "small");
 
-     },
 
-     state: function(composer, command) {
 
-       return wysihtml5.commands.formatInline.state(composer, command, "small");
 
-     }
 
-   };
 
- })(wysihtml5);
 
- /**
 
-  * English translation for bootstrap-wysihtml5
 
-  */
 
- (function (factory) {
 
-     if (typeof define === 'function' && define.amd) {
 
-         // AMD. Register as an anonymous module.
 
-         define('bootstrap.wysihtml5.en-US', ['jquery', 'bootstrap.wysihtml5'], factory);
 
-     } else {
 
-         // Browser globals
 
-         factory(jQuery);
 
-     }
 
- }(function ($) {
 
-   $.fn.wysihtml5.locale.en = $.fn.wysihtml5.locale['en-US'] = {
 
-     font_styles: {
 
-       normal: 'Normal text',
 
-       h1: 'Heading 1',
 
-       h2: 'Heading 2',
 
-       h3: 'Heading 3',
 
-       h4: 'Heading 4',
 
-       h5: 'Heading 5',
 
-       h6: 'Heading 6'
 
-     },
 
-     emphasis: {
 
-       bold: 'Bold',
 
-       italic: 'Italic',
 
-       underline: 'Underline',
 
-       small: 'Small'
 
-     },
 
-     lists: {
 
-       unordered: 'Unordered list',
 
-       ordered: 'Ordered list',
 
-       outdent: 'Outdent',
 
-       indent: 'Indent'
 
-     },
 
-     link: {
 
-       insert: 'Insert link',
 
-       cancel: 'Cancel',
 
-       target: 'Open link in new window'
 
-     },
 
-     image: {
 
-       insert: 'Insert image',
 
-       cancel: 'Cancel'
 
-     },
 
-     html: {
 
-       edit: 'Edit HTML'
 
-     },
 
-     colours: {
 
-       black: 'Black',
 
-       silver: 'Silver',
 
-       gray: 'Grey',
 
-       maroon: 'Maroon',
 
-       red: 'Red',
 
-       purple: 'Purple',
 
-       green: 'Green',
 
-       olive: 'Olive',
 
-       navy: 'Navy',
 
-       blue: 'Blue',
 
-       orange: 'Orange'
 
-     }
 
-   };
 
- }));
 
 
  |