123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741 |
- /**
- * SCEditor
- * http://www.samclarke.com/2011/07/sceditor/
- *
- * Copyright (C) 2011-2012, Sam Clarke (samclarke.com)
- *
- * SCEditor is licensed under the MIT license:
- * http://www.opensource.org/licenses/mit-license.php
- *
- * @fileoverview SCEditor - A lightweight WYSIWYG BBCode and HTML editor
- * @author Sam Clarke
- * @version 1.3.7
- * @requires jQuery
- */
- // ==ClosureCompiler==
- // @output_file_name jquery.sceditor.min.js
- // @compilation_level SIMPLE_OPTIMIZATIONS
- // ==/ClosureCompiler==
- /*jshint smarttabs: true, scripturl: true, jquery: true, devel:true, eqnull:true, curly: false */
- /*global XMLSerializer: true*/
- ;(function ($, window, document) {
- 'use strict';
- var _templates = {
- html: '<!DOCTYPE html>' +
- '<html>' +
- '<head>' +
- '<!--[if IE]><style>* {min-height: auto !important}</style><![endif]-->' +
- '<meta http-equiv="Content-Type" content="text/html;charset={charset}" />' +
- '<link rel="stylesheet" type="text/css" href="{style}" />' +
- '</head>' +
- '<body contenteditable="true"></body>' +
- '</html>',
- toolbarButton: '<a class="sceditor-button sceditor-button-{name}" data-sceditor-command="{name}" unselectable="on"><div unselectable="on">{dispName}</div></a>',
- emoticon: '<img src="{url}" data-sceditor-emoticon="{key}" alt="{key}" />',
- fontOpt: '<a class="sceditor-font-option" href="#" data-font="{font}"><font face="{font}">{font}</font></a>',
- sizeOpt: '<a class="sceditor-fontsize-option" data-size="{size}" style="line-height:{points}pt" href="#"><font size="{size}">{size}</font></a>',
- pastetext: '<div><label for="txt">{label}</label> ' +
- '<textarea cols="20" rows="7" id="txt"></textarea></div>' +
- '<div><input type="button" class="button" value="{insert}" /></div>',
- table: '<div><label for="rows">{rows}</label><input type="text" id="rows" value="2" /></div>' +
- '<div><label for="cols">{cols}</label><input type="text" id="cols" value="2" /></div>' +
- '<div><input type="button" class="button" value="{insert}" /></div>',
- image: '<div><label for="link">{url}</label> <input type="text" id="image" value="http://" /></div>' +
- '<div><label for="width">{width}</label> <input type="text" id="width" size="2" /></div>' +
- '<div><label for="height">{height}</label> <input type="text" id="height" size="2" /></div>' +
- '<div><input type="button" class="button" value="{insert}" /></div>',
- email: '<div><label for="email">{label}</label> <input type="text" id="email" /></div>' +
- '<div><input type="button" class="button" value="{insert}" /></div>',
- link: '<div><label for="link">{url}</label> <input type="text" id="link" value="http://" /></div>' +
- '<div><label for="des">{desc}</label> <input type="text" id="des" /></div>' +
- '<div><input type="button" class="button" value="{ins}" /></div>',
- youtubeMenu: '<div><label for="link">{label}</label> <input type="text" id="link" value="http://" /></div><div><input type="button" class="button" value="{insert}" /></div>',
- youtube: '<iframe width="560" height="315" src="http://www.youtube.com/embed/{id}?wmode=opaque" data-youtube-id="{id}" frameborder="0" allowfullscreen></iframe>'
- };
- /**
- * <p>Replaces any params in a template with the passed params.</p>
- *
- * <p>If createHTML is passed it will use jQuery to create the HTML. The
- * same as doing: $(editor.tmpl("html", {params...}));</p>
- *
- * @param {string} templateName
- * @param {Object} params
- * @param {Boolean} createHTML
- * @private
- */
- var _tmpl = function(name, params, createHTML) {
- var template = _templates[name];
- $.each(params, function(name, val) {
- template = template.replace(new RegExp('\\{' + name + '\\}', 'g'), val);
- });
- if(createHTML)
- template = $(template);
- return template;
- };
- /**
- * SCEditor - A lightweight WYSIWYG editor
- *
- * @param {Element} el The textarea to be converted
- * @return {Object} options
- * @class sceditor
- * @name jQuery.sceditor
- */
- $.sceditor = function (el, options) {
- /**
- * Alias of this
- * @private
- */
- var base = this;
- /**
- * The textarea element being replaced
- * @private
- */
- var $textarea = $(el);
- var textarea = el;
- /**
- * The div which contains the editor and toolbar
- * @private
- */
- var $editorContainer;
- /**
- * The editors toolbar
- * @private
- */
- var $toolbar;
- /**
- * The editors iframe which should be in design mode
- * @private
- */
- var $wysiwygEditor;
- var wysiwygEditor;
- /**
- * The editors textarea for viewing source
- * @private
- */
- var $textEditor;
- var textEditor;
- /**
- * The current dropdown
- * @private
- */
- var $dropdown;
- /**
- * Array of all the commands key press functions
- * @private
- */
- var keyPressFuncs = [];
- /**
- * Store the last cursor position. Needed for IE because it forgets
- * @private
- */
- var lastRange;
- /**
- * The editors locale
- * @private
- */
- var locale;
- /**
- * Stores a cache of preloaded images
- * @private
- */
- var preLoadCache = [];
- var rangeHelper;
- var $blurElm;
- var init,
- replaceEmoticons,
- handleCommand,
- saveRange,
- handlePasteEvt,
- handlePasteData,
- handleKeyPress,
- handleFormReset,
- handleMouseDown,
- initEditor,
- initToolBar,
- initKeyPressFuncs,
- initResize,
- documentClickHandler,
- formSubmitHandler,
- initEmoticons,
- getWysiwygDoc,
- handleWindowResize,
- initLocale,
- updateToolBar,
- textEditorSelectedText,
- autofocus;
- /**
- * All the commands supported by the editor
- */
- base.commands = $.extend({}, (options.commands || $.sceditor.commands));
- /**
- * Initializer. Creates the editor iframe and textarea
- * @private
- * @name sceditor.init
- */
- init = function () {
- $textarea.data("sceditor", base);
- base.options = $.extend({}, $.sceditor.defaultOptions, options);
- // Load locale
- if(base.options.locale && base.options.locale !== "en")
- initLocale();
- // if either width or height are % based, add the resize handler to update the editor
- // when the window is resized
- var h = base.options.height, w = base.options.width;
- if((h && (h + "").indexOf("%") > -1) || (w && (w + "").indexOf("%") > -1))
- $(window).resize(handleWindowResize);
- $editorContainer = $('<div class="sceditor-container" />').insertAfter($textarea);
- // create the editor
- initToolBar();
- initEditor();
- initKeyPressFuncs();
- if(base.options.resizeEnabled)
- initResize();
- if(base.options.id)
- $editorContainer.attr('id', base.options.id);
- $(document).click(documentClickHandler);
- $(textarea.form)
- .attr('novalidate','novalidate')
- .bind("reset", handleFormReset)
- .submit(formSubmitHandler);
- // load any textarea value into the editor
- base.val($textarea.hide().val());
- /*
- // Pass the value though the getTextHandler if it is set so that
- // BBCode, ect. can be converted
- if(base.options.getTextHandler && base.options.supportedWysiwyg)
- {
- val = base.options.getTextHandler(val);
- base.setWysiwygEditorValue(val);
- }
- else
- {
- base.toggleTextMode();
- base.setTextareaValue(val);
- }
- */
- if(base.options.autofocus)
- autofocus();
- // force into source mode if is a browser that can't handle
- // full editing
- if(!$.sceditor.isWysiwygSupported())
- base.toggleTextMode();
- if(base.options.toolbar.indexOf('emoticon') !== -1)
- initEmoticons();
- // Can't use load event as it gets fired before CSS is loaded
- // in some browsers
- if(base.options.autoExpand)
- var interval = setInterval(function() {
- if (!document.readyState || document.readyState === "complete") {
- base.expandToContent();
- clearInterval(interval);
- }
- }, 10);
- };
- /**
- * Creates the editor iframe and textarea
- * @private
- */
- initEditor = function () {
- var $doc, $body;
- $textEditor = $('<textarea></textarea>').hide();
- $wysiwygEditor = $('<iframe frameborder="0"></iframe>');
- if(window.location.protocol === "https:")
- $wysiwygEditor.attr("src", "javascript:false");
- // add the editor to the HTML and store the editors element
- $editorContainer.append($wysiwygEditor).append($textEditor);
- wysiwygEditor = $wysiwygEditor[0];
- textEditor = $textEditor[0];
- base.width(base.options.width || $textarea.width());
- base.height(base.options.height || $textarea.height());
- getWysiwygDoc().open();
- getWysiwygDoc().write(_tmpl("html", {
- charset: base.options.charset,
- style: base.options.style
- }));
- getWysiwygDoc().close();
- base.readOnly(!!base.options.readOnly);
- $doc = $(getWysiwygDoc());
- $body = $doc.find("body");
- // Add IE version class to the HTML element so can apply
- // conditional styling without CSS hacks
- if($.sceditor.ie)
- $doc.find("html").addClass('ie' + $.sceditor.ie);
- // iframe overflow fix
- if(/iPhone|iPod|iPad| wosbrowser\//i.test(navigator.userAgent))
- $body.height('100%');
- // set the key press event
- $body.keypress(handleKeyPress);
- $doc.keypress(handleKeyPress)
- .mousedown(handleMouseDown)
- .bind("beforedeactivate keyup", saveRange)
- .focus(function() {
- lastRange = null;
- });
- if(base.options.rtl)
- {
- $body.attr('dir', 'rtl');
- $textEditor.attr('dir', 'rtl');
- }
- if(base.options.enablePasteFiltering)
- $body.bind("paste", handlePasteEvt);
- if(base.options.autoExpand)
- $doc.bind("keyup", base.expandToContent);
- rangeHelper = new $.sceditor.rangeHelper(wysiwygEditor.contentWindow);
- };
- /**
- * Creates the toolbar and appends it to the container
- * @private
- */
- initToolBar = function () {
- var $group, $button, buttons,
- i, x, buttonClick,
- groups = base.options.toolbar.split("|");
- buttonClick = function () {
- var self = $(this);
- if(!self.hasClass('disabled'))
- handleCommand(self, base.commands[self.data("sceditor-command")]);
- return false;
- };
- $toolbar = $('<div class="sceditor-toolbar" />');
- var rows = base.options.toolbar.split("||");
- for (var r=0; r < rows.length; r++) {
- var row = $('<div class="sceditor-row" />');
- var groups = rows[r].split("|"),
- buttons, accessibilityName, button, i;
- for (i=0; i < groups.length; i++) {
- $group = $('<div class="sceditor-group" />');
- buttons = groups[i].split(",");
- for (x=0; x < buttons.length; x++) {
- // the button must be a valid command otherwise ignore it
- if(!base.commands[buttons[x]])
- continue;
- $button = _tmpl("toolbarButton", {
- name: buttons[x],
- dispName: base.commands[buttons[x]].tooltip || buttons[x]
- }, true).click(buttonClick);
- if(base.commands[buttons[x]].hasOwnProperty("tooltip"))
- $button.attr('title', base._(base.commands[buttons[x]].tooltip));
- if(base.commands[buttons[x]].exec)
- $button.data('sceditor-wysiwygmode', true);
- else
- $button.addClass('disabled');
- if(base.commands[buttons[x]].txtExec)
- $button.data('sceditor-txtmode', true);
- $group.append($button);
- }
- row.append($group);
- }
- $toolbar.append(row);
- }
- // append the toolbar to the toolbarContainer option if given
- if(base.options.toolbarContainer)
- $(base.options.toolbarContainer).append($toolbar);
- else
- $editorContainer.append($toolbar);
- };
- /**
- * Autofocus the editor
- * @private
- */
- autofocus = function() {
- var doc = wysiwygEditor.contentWindow.document,
- body = doc.body, rng;
- if(!doc.createRange)
- return base.focus();
- if(!body.firstChild)
- return;
- rng = doc.createRange();
- rng.setStart(body.firstChild, 0);
- rng.setEnd(body.firstChild, 0);
- rangeHelper.selectRange(rng);
- body.focus();
- };
- /**
- * Gets the readOnly property of the editor
- *
- * @since 1.3.5
- * @function
- * @memberOf jQuery.sceditor.prototype
- * @name readOnly
- * @return {boolean}
- */
- /**
- * Sets the readOnly property of the editor
- *
- * @param {boolean} readOnly
- * @since 1.3.5
- * @function
- * @memberOf jQuery.sceditor.prototype
- * @name readOnly^2
- * @return {this}
- */
- base.readOnly = function(readOnly) {
- if(typeof readOnly !== 'boolean')
- return $textEditor.attr('readonly') === 'readonly';
- getWysiwygDoc().body.contentEditable = !readOnly;
- if(!readOnly)
- $textEditor.removeAttr('readonly');
- else
- $textEditor.attr('readonly', 'readonly');
- updateToolBar(readOnly);
- return this;
- };
- /**
- * Updates the toolbar to disable/enable the appropriate buttons
- * @private
- */
- updateToolBar = function(disable) {
- $toolbar.find('.sceditor-button').removeClass('disabled');
- $toolbar.find('.sceditor-button').each(function () {
- var button = $(this);
- if(disable === true)
- button.addClass('disabled');
- else if(base.inSourceMode() && !button.data('sceditor-txtmode'))
- button.addClass('disabled');
- else if (!base.inSourceMode() && !button.data('sceditor-wysiwygmode'))
- button.addClass('disabled');
- });
- };
- /**
- * Creates an array of all the key press functions
- * like emoticons, ect.
- * @private
- */
- initKeyPressFuncs = function () {
- $.each(base.commands, function (command, values) {
- if(values.keyPress)
- keyPressFuncs.push(values.keyPress);
- });
- };
- /**
- * Gets the width of the editor in px
- *
- * @since 1.3.5
- * @function
- * @memberOf jQuery.sceditor.prototype
- * @name width
- * @return {int}
- */
- /**
- * Sets the width of the editor
- *
- * @param {int} width Width in px
- * @since 1.3.5
- * @function
- * @memberOf jQuery.sceditor.prototype
- * @name width^2
- * @return {this}
- */
- base.width = function (width) {
- if(!width)
- return $editorContainer.width();
- $editorContainer.width(width);
- // fix the height and width of the textarea/iframe
- $wysiwygEditor.width(width);
- $wysiwygEditor.width(width + (width - $wysiwygEditor.outerWidth(true)));
- $textEditor.width(width);
- $textEditor.width(width + (width - $textEditor.outerWidth(true)));
- return this;
- };
- /**
- * Gets the height of the editor in px
- *
- * @since 1.3.5
- * @function
- * @memberOf jQuery.sceditor.prototype
- * @name height
- * @return {int}
- */
- /**
- * Sets the height of the editor
- *
- * @param {int} height Height in px
- * @since 1.3.5
- * @function
- * @memberOf jQuery.sceditor.prototype
- * @name height^2
- * @return {this}
- */
- base.height = function (height) {
- if(!height)
- return $editorContainer.height();
- $editorContainer.height(height);
- height -= !base.options.toolbarContainer ? $toolbar.outerHeight(true) : 0;
- // fix the height and width of the textarea/iframe
- $wysiwygEditor.height(height);
- $wysiwygEditor.height(height + (height - $wysiwygEditor.outerHeight(true)));
- $textEditor.height(height);
- $textEditor.height(height + (height - $textEditor.outerHeight(true)));
- return this;
- };
- /**
- * Expands the editor to the size of it's content
- *
- * @since 1.3.5
- * @param {Boolean} [ignoreMaxHeight=false]
- * @function
- * @name expandToContent
- * @memberOf jQuery.sceditor.prototype
- * @see #resizeToContent
- */
- base.expandToContent = function(ignoreMaxHeight) {
- var doc = getWysiwygDoc(),
- currentHeight = $editorContainer.height(),
- height = doc.body.scrollHeight || doc.documentElement.scrollHeight,
- padding = (currentHeight - $wysiwygEditor.height()),
- maxHeight = base.options.resizeMaxHeight || ((base.options.height || $textarea.height()) * 2);
- height += padding;
- if(ignoreMaxHeight !== true && height > maxHeight)
- height = maxHeight;
- if(height > currentHeight)
- base.height(height);
- };
- /**
- * Creates the resizer.
- * @private
- */
- initResize = function () {
- var $grip = $('<div class="sceditor-grip" />'),
- // cover is used to cover the editor iframe so document still gets mouse move events
- $cover = $('<div class="sceditor-resize-cover" />'),
- startX = 0,
- startY = 0,
- startWidth = 0,
- startHeight = 0,
- origWidth = $editorContainer.width(),
- origHeight = $editorContainer.height(),
- dragging = false,
- minHeight, maxHeight, minWidth, maxWidth, mouseMoveFunc;
- minHeight = base.options.resizeMinHeight || origHeight / 1.5;
- maxHeight = base.options.resizeMaxHeight || origHeight * 2.5;
- minWidth = base.options.resizeMinWidth || origWidth / 1.25;
- maxWidth = base.options.resizeMaxWidth || origWidth * 1.25;
- mouseMoveFunc = function (e) {
- var newHeight = startHeight + (e.pageY - startY),
- newWidth = startWidth + (e.pageX - startX);
- if (newWidth >= minWidth && (maxWidth < 0 || newWidth <= maxWidth))
- base.width(newWidth);
- if (newHeight >= minHeight && (maxHeight < 0 || newHeight <= maxHeight))
- base.height(newHeight);
- e.preventDefault();
- };
- $editorContainer.append($grip);
- $editorContainer.append($cover.hide());
- $grip.mousedown(function (e) {
- startX = e.pageX;
- startY = e.pageY;
- startWidth = $editorContainer.width();
- startHeight = $editorContainer.height();
- dragging = true;
- $editorContainer.addClass('resizing');
- $cover.show();
- $(document).bind('mousemove', mouseMoveFunc);
- e.preventDefault();
- });
- $(document).mouseup(function (e) {
- if(!dragging)
- return;
- dragging = false;
- $cover.hide();
- $editorContainer.removeClass('resizing');
- $(document).unbind('mousemove', mouseMoveFunc);
- e.preventDefault();
- });
- };
- /**
- * Handles the forms submit event
- * @private
- */
- formSubmitHandler = function(e) {
- base.updateTextareaValue();
- $(this).removeAttr('novalidate');
- if(this.checkValidity && !this.checkValidity())
- e.preventDefault();
- $(this).attr('novalidate','novalidate');
- base.blur();
- };
- /**
- * Destroys the editor, removing all elements and
- * event handlers.
- *
- * @function
- * @name destory
- * @memberOf jQuery.sceditor.prototype
- */
- base.destory = function () {
- $(document).unbind('click', documentClickHandler);
- $(window).unbind('resize', handleWindowResize);
- $(textarea.form).removeAttr('novalidate')
- .unbind('submit', formSubmitHandler)
- .unbind("reset", handleFormReset);
- $(getWysiwygDoc()).find('*').remove();
- $(getWysiwygDoc()).unbind("keypress mousedown beforedeactivate keyup focus paste keypress");
- $editorContainer.find('*').remove();
- $editorContainer.remove();
- $textarea.removeData("sceditor").removeData("sceditorbbcode").show();
- };
- /**
- * Preloads the emoticon images
- * Idea from: http://engineeredweb.com/blog/09/12/preloading-images-jquery-and-javascript
- * @private
- */
- initEmoticons = function () {
- // prefix emoticon root to emoticon urls
- if(base.options.emoticonsRoot && base.options.emoticons)
- {
- $.each(base.options.emoticons, function (idx, emoticons) {
- $.each(emoticons, function (key, url) {
- base.options.emoticons[idx][key] = base.options.emoticonsRoot + url;
- });
- });
- }
- var emoticons = $.extend({}, base.options.emoticons.more, base.options.emoticons.dropdown, base.options.emoticons.hidden),
- emoticon;
- $.each(emoticons, function (key, url) {
- // In SMF an empty entry means a new line
- if (url == '')
- emoticon = document.createElement('br');
- else
- {
- emoticon = document.createElement('img');
- emoticon.src = url;
- }
- preLoadCache.push(emoticon);
- });
- };
- /**
- * Creates a menu item drop down
- *
- * @param HTMLElement menuItem The button to align the drop down with
- * @param string dropDownName Used for styling the dropown, will be a class sceditor-dropDownName
- * @param string content The HTML content of the dropdown
- * @param bool ieUnselectable If to add the unselectable attribute to all the contents elements. Stops IE from deselecting the text in the editor
- * @function
- * @name createDropDown
- * @memberOf jQuery.sceditor.prototype
- */
- base.createDropDown = function (menuItem, dropDownName, content, ieUnselectable) {
- base.closeDropDown();
- // IE needs unselectable attr to stop it from unselecting the text in the editor.
- // The editor can cope if IE does unselect the text it's just not nice.
- if(ieUnselectable !== false) {
- $(content).find(':not(input,textarea)')
- .filter(function() {
- return this.nodeType===1;
- })
- .attr('unselectable', 'on');
- }
- var css = {
- top: menuItem.offset().top,
- left: menuItem.offset().left
- };
- $.extend(css, base.options.dropDownCss);
- $dropdown = $('<div class="sceditor-dropdown sceditor-' + dropDownName + '" />')
- .css(css)
- .append(content)
- .appendTo($('body'))
- .click(function (e) {
- // stop clicks within the dropdown from being handled
- e.stopPropagation();
- });
- };
- /**
- * Handles any document click and closes the dropdown if open
- * @private
- */
- documentClickHandler = function (e) {
- // ignore right clicks
- if(e.which !== 3)
- base.closeDropDown();
- };
- handlePasteEvt = function(e) {
- var elm = getWysiwygDoc().body,
- checkCount = 0,
- pastearea = elm.ownerDocument.createElement('div'),
- prePasteContent = elm.ownerDocument.createDocumentFragment();
- rangeHelper.saveRange();
- document.body.appendChild(pastearea);
- if (e && e.clipboardData && e.clipboardData.getData)
- {
- var html, handled=true;
- if ((html = e.clipboardData.getData('text/html')) || (html = e.clipboardData.getData('text/plain')))
- pastearea.innerHTML = html;
- else
- handled = false;
- if(handled)
- {
- handlePasteData(elm, pastearea);
- if (e.preventDefault)
- {
- e.stopPropagation();
- e.preventDefault();
- }
- return false;
- }
- }
- while(elm.firstChild)
- prePasteContent.appendChild(elm.firstChild);
- function handlePaste(elm, pastearea) {
- if (elm.childNodes.length > 0)
- {
- while(elm.firstChild)
- pastearea.appendChild(elm.firstChild);
- while(prePasteContent.firstChild)
- elm.appendChild(prePasteContent.firstChild);
- handlePasteData(elm, pastearea);
- }
- else
- {
- // Allow max 25 checks before giving up.
- // Needed inscase empty input is posted or
- // something gose wrong.
- if(checkCount > 25)
- {
- while(prePasteContent.firstChild)
- elm.appendChild(prePasteContent.firstChild);
- return;
- }
- ++checkCount;
- setTimeout(function () {
- handlePaste(elm, pastearea);
- }, 20);
- }
- }
- handlePaste(elm, pastearea);
- base.focus();
- return true;
- };
- /**
- * @param {Element} elm
- * @param {Element} pastearea
- * @private
- */
- handlePasteData = function(elm, pastearea) {
- // fix any invalid nesting
- $.sceditor.dom.fixNesting(pastearea);
- var pasteddata = pastearea.innerHTML;
- if(base.options.getHtmlHandler)
- pasteddata = base.options.getHtmlHandler(pasteddata, $(pastearea));
- pastearea.parentNode.removeChild(pastearea);
- if(base.options.getTextHandler)
- pasteddata = base.options.getTextHandler(pasteddata, true);
- rangeHelper.restoreRange();
- rangeHelper.insertHTML(pasteddata);
- };
- /**
- * Closes the current drop down
- *
- * @param bool focus If to focus the editor on close
- * @function
- * @name closeDropDown
- * @memberOf jQuery.sceditor.prototype
- */
- base.closeDropDown = function (focus) {
- if($dropdown) {
- $dropdown.remove();
- $dropdown = null;
- }
- if(focus === true)
- base.focus();
- };
- /**
- * Gets the WYSIWYG editors document
- * @private
- */
- getWysiwygDoc = function () {
- if (wysiwygEditor.contentDocument)
- return wysiwygEditor.contentDocument;
- if (wysiwygEditor.contentWindow && wysiwygEditor.contentWindow.document)
- return wysiwygEditor.contentWindow.document;
- if (wysiwygEditor.document)
- return wysiwygEditor.document;
- return null;
- };
- /**
- * <p>Inserts HTML into WYSIWYG editor.</p>
- *
- * <p>If endHtml is specified instead of the inserted HTML replacing the selected
- * text the selected text will be placed between html and endHtml. If there is
- * no selected text html and endHtml will be concated together.</p>
- *
- * @param {string} html
- * @param {string} [endHtml=null]
- * @param {boolean} [overrideCodeBlocking=false]
- * @function
- * @name wysiwygEditorInsertHtml
- * @memberOf jQuery.sceditor.prototype
- */
- base.wysiwygEditorInsertHtml = function (html, endHtml, overrideCodeBlocking) {
- base.focus();
- // don't apply to code elements
- if(!overrideCodeBlocking && ($(rangeHelper.parentNode()).is('code') ||
- $(rangeHelper.parentNode()).parents('code').length !== 0))
- return;
- rangeHelper.insertHTML(html, endHtml);
- };
- /**
- * Like wysiwygEditorInsertHtml except it will convert any HTML into text
- * before inserting it.
- *
- * @param {String} text
- * @param {String} [endText=null]
- * @function
- * @name wysiwygEditorInsertText
- * @memberOf jQuery.sceditor.prototype
- */
- base.wysiwygEditorInsertText = function (text, endText) {
- var escape = function(str) {
- if(!str)
- return str;
- return str.replace(/&/g, "&")
- .replace(/</g, "<")
- .replace(/>/g, ">")
- .replace(/ /g, " ")
- .replace(/\r\n|\r/g, "\n")
- .replace(/\n/g, "<br />");
- };
- base.wysiwygEditorInsertHtml(escape(text), escape(endText));
- };
- /**
- * <p>Inserts text into either WYSIWYG or textEditor depending on which
- * mode the editor is in.</p>
- *
- * <p>If endText is specified any selected text will be placed between
- * text and endText. If no text is selected text and endText will
- * just be concated together.</p>
- *
- * @param {String} text
- * @param {String} [endText=null]
- * @since 1.3.5
- * @function
- * @name insertText
- * @memberOf jQuery.sceditor.prototype
- */
- base.insertText = function (text, endText) {
- if(base.inSourceMode())
- base.textEditorInsertText(text, endText);
- else
- base.wysiwygEditorInsertText(text, endText);
- return this;
- };
- /**
- * Like wysiwygEditorInsertHtml but inserts text into the text
- * (source mode) editor instead
- *
- * @param {String} text
- * @param {String} [endText=null]
- * @function
- * @name textEditorInsertText
- * @memberOf jQuery.sceditor.prototype
- */
- base.textEditorInsertText = function (text, endText) {
- var range, start, end, txtLen;
- textEditor.focus();
- if(typeof textEditor.selectionStart !== "undefined")
- {
- start = textEditor.selectionStart;
- end = textEditor.selectionEnd;
- txtLen = text.length;
- if(endText)
- text += textEditor.value.substring(start, end) + endText;
- textEditor.value = textEditor.value.substring(0, start) + text + textEditor.value.substring(end, textEditor.value.length);
- if(endText)
- textEditor.selectionStart = (start + text.length) - endText.length;
- else
- textEditor.selectionStart = start + text.length;
- textEditor.selectionEnd = textEditor.selectionStart;
- }
- else if(typeof document.selection.createRange !== "undefined")
- {
- range = document.selection.createRange();
- if(endText)
- text += range.text + endText;
- range.text = text;
- if(endText)
- range.moveEnd('character', 0-endText.length);
- range.moveStart('character', range.End - range.Start);
- range.select();
- }
- else
- textEditor.value += text + endText;
- textEditor.focus();
- };
- /**
- * Gets the current rangeHelper instance
- *
- * @return jQuery.sceditor.rangeHelper
- * @function
- * @name getRangeHelper
- * @memberOf jQuery.sceditor.prototype
- */
- base.getRangeHelper = function () {
- return rangeHelper;
- };
- /**
- * Gets the value of the editor
- *
- * @since 1.3.5
- * @return {string}
- * @function
- * @name val
- * @memberOf jQuery.sceditor.prototype
- */
- /**
- * Sets the value of the editor
- *
- * @param {String} val
- * @param {Boolean} [filter]
- * @return {this}
- * @since 1.3.5
- * @function
- * @name val^2
- * @memberOf jQuery.sceditor.prototype
- */
- base.val = function (val, filter) {
- if(typeof val === "string")
- {
- if(base.inSourceMode())
- base.setTextareaValue(val);
- else
- {
- if(filter !== false && base.options.getTextHandler)
- val = base.options.getTextHandler(val);
- base.setWysiwygEditorValue(val);
- }
- return this;
- }
- return base.inSourceMode() ?
- base.getTextareaValue(false) :
- base.getWysiwygEditorValue();
- };
- /**
- * <p>Inserts HTML/BBCode into the editor</p>
- *
- * <p>If end is supplied any slected text will be placed between
- * start and end. If there is no selected text start and end
- * will be concated together.</p>
- *
- * @param {String} start
- * @param {String} [end=null]
- * @param {Boolean} [filter=true]
- * @param {Boolean} [convertEmoticons=true]
- * @return {this}
- * @since 1.3.5
- * @function
- * @name insert
- * @memberOf jQuery.sceditor.prototype
- */
- base.insert = function (start, end, filter, convertEmoticons) {
- if(base.inSourceMode())
- base.textEditorInsertText(start, end);
- else
- {
- if(end)
- {
- var html = base.getRangeHelper().selectedHtml(),
- frag = $('<div>').appendTo($('body')).hide().html(html);
- if(filter !== false && base.options.getHtmlHandler)
- {
- html = base.options.getHtmlHandler(html, frag);
- frag.remove();
- }
- start += html + end;
- }
- if(filter !== false && base.options.getTextHandler)
- start = base.options.getTextHandler(start, true);
- if(convertEmoticons !== false)
- start = replaceEmoticons(start);
- base.wysiwygEditorInsertHtml(start);
- }
- return this;
- };
- /**
- * Gets the WYSIWYG editors HTML which is between the body tags
- *
- * @param {bool} [filter=true]
- * @return {string}
- * @function
- * @name getWysiwygEditorValue
- * @memberOf jQuery.sceditor.prototype
- */
- base.getWysiwygEditorValue = function (filter) {
- // Possible replacement:
- // if(!$.sceditor.isWysiwygSupported())
- //if (!base.options.supportedWysiwyg)
- // return;
- var $body = $wysiwygEditor.contents().find("body"),
- html;
- // fix any invalid nesting
- $.sceditor.dom.fixNesting($body.get(0));
- html = $body.html();
- if(filter !== false && base.options.getHtmlHandler)
- html = base.options.getHtmlHandler(html, $body);
- return html;
- };
- /**
- * Gets the text editor value
- *
- * @param {bool} [filter=true]
- * @return {string}
- * @function
- * @name getTextareaValue
- * @memberOf jQuery.sceditor.prototype
- */
- base.getTextareaValue = function (filter) {
- var val = $textEditor.val();
- if(filter !== false && base.options.getTextHandler)
- val = base.options.getTextHandler(val);
- return val;
- };
- /**
- * Sets the WYSIWYG HTML editor value. Should only be the HTML
- * contained within the body tags
- *
- * @param {string} value
- * @function
- * @name setWysiwygEditorValue
- * @memberOf jQuery.sceditor.prototype
- */
- base.setWysiwygEditorValue = function (value) {
- if(!value)
- value = '<p>' + ($.sceditor.ie ? '' : '<br />') + '</p>';
- getWysiwygDoc().body.innerHTML = replaceEmoticons(value);
- };
- /**
- * Sets the text editor value
- *
- * @param {string} value
- * @function
- * @name setTextareaValue
- * @memberOf jQuery.sceditor.prototype
- */
- base.setTextareaValue = function (value) {
- $textEditor.val(value);
- };
- /**
- * Updates the textarea that the editor is replacing
- * with the value currently inside the editor.
- *
- * @function
- * @name updateTextareaValue
- * @memberOf jQuery.sceditor.prototype
- */
- base.updateTextareaValue = function () {
- if(base.inSourceMode())
- $textarea.val(base.getTextareaValue(false));
- else
- $textarea.val(base.getWysiwygEditorValue());
- };
- /**
- * Replaces any emoticon codes in the passed HTML with their emoticon images
- * @private
- */
- replaceEmoticons = function (html) {
- if(base.options.toolbar.indexOf('emoticon') === -1)
- return html;
- var emoticons = $.extend({}, base.options.emoticons.more, base.options.emoticons.dropdown, base.options.emoticons.hidden);
- $.each(emoticons, function (key, url) {
- // In SMF an empty entry means a new line
- if (url == '')
- return;
- // escape the key before using it as a regex
- // and append the regex to only find emoticons outside
- // of HTML tags
- var reg = $.sceditor.regexEscape(key) + "(?=([^\\<\\>]*?<(?!/code)|[^\\<\\>]*?$))",
- group = '';
- // Make sure the emoticon is surrounded by whitespace or is at the start/end of a string or html tag
- if(base.options.emoticonsCompat)
- {
- reg = "((>|^|\\s|\xA0|\u2002|\u2003|\u2009| ))" + reg + "(?=(\\s|$|<|\xA0|\u2002|\u2003|\u2009| ))";
- group = '$1';
- }
- html = html.replace(
- new RegExp(reg, 'gm'),
- group + _tmpl('emoticon', {key: key, url: url})
- );
- });
- return html;
- };
- /**
- * If the editor is in source code mode
- *
- * @return {bool}
- * @function
- * @name inSourceMode
- * @memberOf jQuery.sceditor.prototype
- */
- base.inSourceMode = function () {
- return $textEditor.is(':visible');
- };
- /**
- * Gets if the editor is in sourceMode
- *
- * @return boolean
- * @function
- * @name sourceMode
- * @memberOf jQuery.sceditor.prototype
- */
- /**
- * Sets if the editor is in sourceMode
- *
- * @param {bool} enable
- * @return {this}
- * @function
- * @name sourceMode^2
- * @memberOf jQuery.sceditor.prototype
- */
- base.sourceMode = function (enable) {
- if(typeof enable !== 'boolean')
- return base.inSourceMode();
- if((base.inSourceMode() && !enable) || (!base.inSourceMode() && enable))
- base.toggleTextMode();
- return this;
- };
- /**
- * Switches between the WYSIWYG and plain text modes
- *
- * @function
- * @name toggleTextMode
- * @memberOf jQuery.sceditor.prototype
- */
- base.toggleTextMode = function () {
- // don't allow switching to WYSIWYG if doesn't support it
- if(!$.sceditor.isWysiwygSupported() && base.inSourceMode())
- return;
- if(base.inSourceMode())
- base.setWysiwygEditorValue(base.getTextareaValue());
- else
- base.setTextareaValue(base.getWysiwygEditorValue());
- lastRange = null;
- $textEditor.toggle();
- $wysiwygEditor.toggle();
- $editorContainer.removeClass('sourceMode');
- $editorContainer.removeClass('wysiwygMode');
- if(base.inSourceMode())
- $editorContainer.addClass('sourceMode');
- else
- $editorContainer.addClass('wysiwygMode');
- updateToolBar();
- };
- textEditorSelectedText = function () {
- textEditor.focus();
- if(textEditor.selectionStart != null)
- return textEditor.value.substring(textEditor.selectionStart, textEditor.selectionEnd);
- else if(document.selection.createRange)
- return document.selection.createRange().text;
- };
- /**
- * Handles the passed command
- * @private
- */
- handleCommand = function (caller, command) {
- // check if in text mode and handle text commands
- if(base.inSourceMode())
- {
- if(command.txtExec)
- {
- if($.isArray(command.txtExec))
- base.textEditorInsertText.apply(base, command.txtExec);
- else
- command.txtExec.call(base, caller, textEditorSelectedText());
- }
- return;
- }
- if(!command.exec)
- return;
- if($.isFunction(command.exec))
- command.exec.call(base, caller);
- else
- base.execCommand(command.exec, command.hasOwnProperty("execParam") ? command.execParam : null);
- };
- /**
- * Fucuses the editors input area
- *
- * @return {this}
- * @function
- * @name focus
- * @memberOf jQuery.sceditor.prototype
- */
- base.focus = function () {
- if(!base.inSourceMode())
- {
- wysiwygEditor.contentWindow.focus();
- // Needed for IE < 9
- if(lastRange) {
- rangeHelper.selectRange(lastRange);
- // remove the stored range after being set.
- // If the editor loses focus it should be
- // saved again.
- lastRange = null;
- }
- }
- else
- textEditor.focus();
- return this;
- };
- /**
- * Blurs the editors input area
- *
- * @return {this}
- * @function
- * @name blur
- * @memberOf jQuery.sceditor.prototype
- * @since 1.3.6
- */
- base.blur = function () {
- // Must use an element that isn't display:hidden or visibility:hidden for iOS
- // so create a special blur element to use
- if(!$blurElm)
- $blurElm = $('<input style="width:0; height:0; opacity:0; filter: alpha(opacity=0)" type="text" />').appendTo($editorContainer);
- $blurElm.removeAttr("disabled")
- .focus()
- .blur()
- .attr("disabled", "disabled");
- return this;
- };
- /**
- * Saves the current range. Needed for IE because it forgets
- * where the cursor was and what was selected
- * @private
- */
- saveRange = function () {
- /* this is only needed for IE */
- if(!$.sceditor.ie)
- return;
- lastRange = rangeHelper.selectedRange();
- };
- /**
- * Executes a command on the WYSIWYG editor
- *
- * @param {String|Function} command
- * @param {String|Boolean} [param]
- * @function
- * @name execCommand
- * @memberOf jQuery.sceditor.prototype
- */
- base.execCommand = function (command, param) {
- var executed = false,
- $parentNode = $(rangeHelper.parentNode());
- base.focus();
- // don't apply any comannds to code elements
- if($parentNode.is('code') || $parentNode.parents('code').length !== 0)
- return;
- if(getWysiwygDoc())
- {
- try
- {
- executed = getWysiwygDoc().execCommand(command, false, param);
- }
- catch (e) {}
- }
- // show error if execution failed and an error message exists
- if(!executed && base.commands[command] && base.commands[command].errorMessage)
- alert(base._(base.commands[command].errorMessage));
- };
- /**
- * Handles any key press in the WYSIWYG editor
- *
- * @private
- */
- handleKeyPress = function(e) {
- base.closeDropDown();
- var $parentNode = $(rangeHelper.parentNode());
- // "Fix" (ok it's a cludge) for blocklevel elements being duplicated in some browsers when
- // enter is pressed instead of inserting a newline
- if(e.which === 13)
- {
- if($parentNode.is('code,blockquote,pre') || $parentNode.parents('code,blockquote,pre').length !== 0)
- {
- lastRange = null;
- base.wysiwygEditorInsertHtml('<br />', null, true);
- return false;
- }
- }
- // make sure there is always a newline after code or quote tags
- var d = getWysiwygDoc();
- $.sceditor.dom.rTraverse(d.body, function(node) {
- if((node.nodeType === 3 && node.nodeValue !== "") ||
- node.nodeName.toLowerCase() === 'br') {
- // this is the last text or br node, if its in a code or quote tag
- // then add a newline after it
- if($(node).parents('code, blockquote').length > 0)
- $(d.body).append(d.createElement('br'));
- return false;
- }
- }, true);
- // don't apply to code elements
- if($parentNode.is('code') || $parentNode.parents('code').length !== 0)
- return;
- var i = keyPressFuncs.length;
- while(i--)
- keyPressFuncs[i].call(base, e, wysiwygEditor, $textEditor);
- };
- /**
- * Handles any mousedown press in the WYSIWYG editor
- * @private
- */
- handleFormReset = function() {
- base.val($textarea.val());
- };
- /**
- * Handles any mousedown press in the WYSIWYG editor
- * @private
- */
- handleMouseDown = function() {
- base.closeDropDown();
- lastRange = null;
- };
- /**
- * Handles the window resize event. Needed to resize then editor
- * when the window size changes in fluid deisgns.
- * @ignore
- */
- handleWindowResize = function() {
- if(base.options.height && base.options.height.toString().indexOf("%") > -1)
- base.height($editorContainer.parent().height() *
- (parseFloat(base.options.height) / 100));
- if(base.options.width && base.options.width.toString().indexOf("%") > -1)
- base.width($editorContainer.parent().width() *
- (parseFloat(base.options.width) / 100));
- };
- /**
- * Translates the string into the locale language.
- *
- * Replaces any {0}, {1}, {2}, ect. with the params provided.
- *
- * @param {string} str
- * @param {...String} args
- * @return {string}
- * @function
- * @name _
- * @memberOf jQuery.sceditor.prototype
- */
- base._ = function() {
- var args = arguments;
- if(locale && locale[args[0]])
- args[0] = locale[args[0]];
- return args[0].replace(/\{(\d+)\}/g, function(str, p1) {
- return typeof args[p1-0+1] !== "undefined" ?
- args[p1-0+1] :
- '{' + p1 + '}';
- });
- };
- /**
- * Init the locale variable with the specified locale if possible
- * @private
- * @return void
- */
- initLocale = function() {
- if($.sceditor.locale[base.options.locale])
- locale = $.sceditor.locale[base.options.locale];
- else
- {
- var lang = base.options.locale.split("-");
- if($.sceditor.locale[lang[0]])
- locale = $.sceditor.locale[lang[0]];
- }
- if(locale && locale.dateFormat)
- base.options.dateFormat = locale.dateFormat;
- };
- // run the initializer
- init();
- };
- /**
- * Detects which version of IE is being used if any.
- *
- * Will be the IE version number or undefined if not IE.
- *
- * Source: https://gist.github.com/527683
- * @type {int}
- * @memberOf jQuery.sceditor
- */
- $.sceditor.ie = (function(){
- var undef,
- v = 3,
- div = document.createElement('div'),
- all = div.getElementsByTagName('i');
- do {
- div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->';
- } while (all[0]);
- return v > 4 ? v : undef;
- }());
- /**
- * Detects if WYSIWYG is supported by the browser
- *
- * @return {bool}
- * @memberOf jQuery.sceditor
- */
- $.sceditor.isWysiwygSupported = function() {
- var contentEditable = $('<div contenteditable="true">')[0].contentEditable,
- contentEditableSupported = typeof contentEditable !== 'undefined' && contentEditable !== 'inherit',
- userAgent = navigator.userAgent,
- match;
- if(!contentEditableSupported)
- return false;
- // I think blackberry supports it or will at least
- // give a valid value for the contentEditable detection above
- // so it's' not included here.
- // The latest WebOS dose support contentEditable.
- // But I still till need to check if all supported
- // versions of WebOS support contentEditable
- // I hate having to use UA sniffing but as some mobile browsers say they support
- // contentediable/design mode when it isn't usable (i.e. you can't eneter text, ect.).
- // This is the only way I can think of to detect them. It's also how every other editor
- // I've seen detects them
- var isUnsupported = /Opera Mobi|Opera Mini/i.test(userAgent);
- if(/Android/i.test(userAgent))
- {
- isUnsupported = true;
- if(/Safari/.test(userAgent))
- {
- // android browser 534+ supports content editable
- match = /Safari\/(\d+)/.exec(userAgent);
- isUnsupported = (!match || !match[1] ? true : match[1] < 534);
- }
- }
- // Amazon Silk doesn't but as it uses webkit like android
- // it might in a later version if it uses version >= 534
- if(/ Silk\//i.test(userAgent))
- {
- match = /AppleWebKit\/(\d+)/.exec(userAgent);
- isUnsupported = (!match || !match[1] ? true : match[1] < 534);
- }
- // iOS 5+ supports content editable
- if(/iPhone|iPod|iPad/i.test(userAgent))
- isUnsupported = !/OS 5(_\d)+ like Mac OS X/i.test(userAgent);
- // FireFox dose support WYSIWYG on mobiles so override
- // any previous value if using FF
- if(/fennec/i.test(userAgent))
- isUnsupported = false;
- return !isUnsupported;
- };
- /**
- * Escapes a string so it's safe to use in regex
- *
- * @param {string} str
- * @return {string}
- * @memberOf jQuery.sceditor
- */
- $.sceditor.regexEscape = function (str) {
- return str.replace(/[\$\?\[\]\.\*\(\)\|\\]/g, "\\$&")
- .replace("<", "<")
- .replace(">", ">");
- };
- $.sceditor.locale = {};
- $.sceditor.commands = {
- // START_COMMAND: Bold
- bold: {
- exec: "bold",
- tooltip: "Bold"
- },
- // END_COMMAND
- // START_COMMAND: Italic
- italic: {
- exec: "italic",
- tooltip: "Italic"
- },
- // END_COMMAND
- // START_COMMAND: Underline
- underline: {
- exec: "underline",
- tooltip: "Underline"
- },
- // END_COMMAND
- // START_COMMAND: Strikethrough
- strike: {
- exec: "strikethrough",
- tooltip: "Strikethrough"
- },
- // END_COMMAND
- // START_COMMAND: Subscript
- subscript: {
- exec: "subscript",
- tooltip: "Subscript"
- },
- // END_COMMAND
- // START_COMMAND: Superscript
- superscript: {
- exec: "superscript",
- tooltip: "Superscript"
- },
- // END_COMMAND
- // START_COMMAND: Left
- left: {
- exec: "justifyleft",
- tooltip: "Align left"
- },
- // END_COMMAND
- // START_COMMAND: Centre
- center: {
- exec: "justifycenter",
- tooltip: "Center"
- },
- // END_COMMAND
- // START_COMMAND: Right
- right: {
- exec: "justifyright",
- tooltip: "Align right"
- },
- // END_COMMAND
- // START_COMMAND: Justify
- justify: {
- exec: "justifyfull",
- tooltip: "Justify"
- },
- // END_COMMAND
- // START_COMMAND: Font
- font: {
- _dropDown: function(editor, caller, callback) {
- var fonts = editor.options.fonts.split(","),
- content = $("<div />"),
- /** @private */
- clickFunc = function () {
- callback($(this).data('font'));
- editor.closeDropDown(true);
- return false;
- };
- for (var i=0; i < fonts.length; i++)
- content.append(_tmpl('fontOpt', {font: fonts[i]}, true).click(clickFunc));
- editor.createDropDown(caller, "font-picker", content);
- },
- exec: function (caller) {
- var editor = this;
- $.sceditor.command.get('font')._dropDown(
- editor,
- caller,
- function(fontName) {
- editor.execCommand("fontname", fontName);
- }
- );
- },
- tooltip: "Font Name"
- },
- // END_COMMAND
- // START_COMMAND: Size
- size: {
- _dropDown: function(editor, caller, callback) {
- var sizes = [0, 8, 10, 12, 14, 18, 24, 36];
- var content = $("<div />"),
- /** @private */
- clickFunc = function (e) {
- callback($(this).data('size'));
- editor.closeDropDown(true);
- e.preventDefault();
- };
- for (var i=1; i<= 7; i++)
- content.append(_tmpl('sizeOpt', {size: i, points: sizes[i]}, true).click(clickFunc));
- editor.createDropDown(caller, "fontsize-picker", content);
- },
- exec: function (caller) {
- var editor = this;
- $.sceditor.command.get('size')._dropDown(
- editor,
- caller,
- function(fontSize) {
- editor.execCommand("fontsize", fontSize);
- }
- );
- },
- tooltip: "Font Size"
- },
- // END_COMMAND
- // START_COMMAND: Colour
- color: {
- _dropDown: function(editor, caller, callback) {
- var genColor = {r: 255, g: 255, b: 255},
- content = $("<div />"),
- colorColumns = editor.options.colors?editor.options.colors.split("|"):new Array(21),
- // IE is slow at string concation so use an array
- html = [],
- htmlIndex = 0;
- for (var i=0; i < colorColumns.length; ++i) {
- var colors = colorColumns[i]?colorColumns[i].split(","):new Array(21);
- html[htmlIndex++] = '<div class="sceditor-color-column">';
- for (var x=0; x < colors.length; ++x) {
- // use pre defined colour if can otherwise use the generated color
- var color = colors[x]?colors[x]:"#" + genColor.r.toString(16) + genColor.g.toString(16) + genColor.b.toString(16);
- html[htmlIndex++] = '<a href="#" class="sceditor-color-option" style="background-color: '+color+'" data-color="'+color+'"></a>';
- // calculate the next generated color
- if(x%5===0)
- genColor = {r: genColor.r, g: genColor.g-51, b: 255};
- else
- genColor = {r: genColor.r, g: genColor.g, b: genColor.b-51};
- }
- html[htmlIndex++] = '</div>';
- // calculate the next generated color
- if(i%5===0)
- genColor = {r: genColor.r-51, g: 255, b: 255};
- else
- genColor = {r: genColor.r, g: 255, b: 255};
- }
- content.append(html.join(''))
- .find('a')
- .click(function (e) {
- callback($(this).attr('data-color'));
- editor.closeDropDown(true);
- e.preventDefault();
- });
- editor.createDropDown(caller, "color-picker", content);
- },
- exec: function (caller) {
- var editor = this;
- $.sceditor.command.get('color')._dropDown(
- editor,
- caller,
- function(color) {
- editor.execCommand("forecolor", color);
- }
- );
- },
- tooltip: "Font Color"
- },
- // END_COMMAND
- // START_COMMAND: Remove Format
- removeformat: {
- exec: "removeformat",
- tooltip: "Remove Formatting"
- },
- // END_COMMAND
- // START_COMMAND: Cut
- cut: {
- exec: "cut",
- tooltip: "Cut",
- errorMessage: "Your browser does not allow the cut command. Please use the keyboard shortcut Ctrl/Cmd-X"
- },
- // END_COMMAND
- // START_COMMAND: Copy
- copy: {
- exec: "copy",
- tooltip: "Copy",
- errorMessage: "Your browser does not allow the copy command. Please use the keyboard shortcut Ctrl/Cmd-C"
- },
- // END_COMMAND
- // START_COMMAND: Paste
- paste: {
- exec: "paste",
- tooltip: "Paste",
- errorMessage: "Your browser does not allow the paste command. Please use the keyboard shortcut Ctrl/Cmd-V"
- },
- // END_COMMAND
- // START_COMMAND: Paste Text
- pastetext: {
- exec: function (caller) {
- var val,
- editor = this,
- content = _tmpl("pastetext", {
- label: editor._("Paste your text inside the following box:"),
- insert: editor._("Insert")
- }, true);
- content.find('.button').click(function (e) {
- val = content.find("#txt").val();
- if(val)
- editor.wysiwygEditorInsertText(val);
- editor.closeDropDown(true);
- e.preventDefault();
- });
- editor.createDropDown(caller, "pastetext", content);
- },
- tooltip: "Paste Text"
- },
- // END_COMMAND
- // START_COMMAND: Bullet List
- bulletlist: {
- exec: "insertunorderedlist",
- tooltip: "Bullet list"
- },
- // END_COMMAND
- // START_COMMAND: Ordered List
- orderedlist: {
- exec: "insertorderedlist",
- tooltip: "Numbered list"
- },
- // END_COMMAND
- // START_COMMAND: Table
- table: {
- exec: function (caller) {
- var editor = this,
- content = _tmpl("table", {
- rows: editor._("Rows:"),
- cols: editor._("Cols:"),
- insert: editor._("Insert")
- }, true);
- content.find('.button').click(function (e) {
- var rows = content.find("#rows").val() - 0,
- cols = content.find("#cols").val() - 0,
- html = '<table>';
- if(rows < 1 || cols < 1)
- return;
- for (var row=0; row < rows; row++) {
- html += '<tr>';
- for (var col=0; col < cols; col++)
- html += '<td>' + ($.sceditor.ie ? '' : '<br class="sceditor-ignore" />') + '</td>';
- html += '</tr>';
- }
- html += '</table>';
- editor.wysiwygEditorInsertHtml(html);
- editor.closeDropDown(true);
- e.preventDefault();
- });
- editor.createDropDown(caller, "inserttable", content);
- },
- tooltip: "Insert a table"
- },
- // END_COMMAND
- // START_COMMAND: Horizontal Rule
- horizontalrule: {
- exec: "inserthorizontalrule",
- tooltip: "Insert a horizontal rule"
- },
- // END_COMMAND
- // START_COMMAND: Code
- code: {
- exec: function () {
- this.wysiwygEditorInsertHtml('<code>', '<br /></code>');
- },
- tooltip: "Code"
- },
- // END_COMMAND
- // START_COMMAND: Image
- image: {
- exec: function (caller) {
- var editor = this,
- content = _tmpl("image", {
- url: editor._("URL:"),
- width: editor._("Width (optional):"),
- height: editor._("Height (optional):"),
- insert: editor._("Insert")
- }, true);
- content.find('.button').click(function (e) {
- var val = content.find("#image").val(),
- attrs = '',
- width, height;
- if((width = content.find("#width").val()))
- attrs += ' width="' + width + '"';
- if((height = content.find("#height").val()))
- attrs += ' height="' + height + '"';
- if(val && val !== "http://")
- editor.wysiwygEditorInsertHtml('<img' + attrs + ' src="' + val + '" />');
- editor.closeDropDown(true);
- e.preventDefault();
- });
- editor.createDropDown(caller, "insertimage", content);
- },
- tooltip: "Insert an image"
- },
- // END_COMMAND
- // START_COMMAND: E-mail
- email: {
- exec: function (caller) {
- var editor = this,
- content = _tmpl("email", {
- label: editor._("E-mail:"),
- insert: editor._("Insert")
- }, true);
- content.find('.button').click(function (e) {
- var val = content.find("#email").val();
- if(val)
- {
- // needed for IE to reset the last range
- editor.focus();
- if(!editor.getRangeHelper().selectedHtml())
- editor.wysiwygEditorInsertHtml('<a href="' + 'mailto:' + val + '">' + val + '</a>');
- else
- editor.execCommand("createlink", 'mailto:' + val);
- }
- editor.closeDropDown(true);
- e.preventDefault();
- });
- editor.createDropDown(caller, "insertemail", content);
- },
- tooltip: "Insert an email"
- },
- // END_COMMAND
- // START_COMMAND: Link
- link: {
- exec: function (caller) {
- var editor = this,
- content = _tmpl("link", {
- url: editor._("URL:"),
- desc: editor._("Description (optional):"),
- ins: editor._("Insert")
- }, true);
- content.find('.button').click(function (e) {
- var val = content.find("#link").val(),
- description = content.find("#des").val();
- if(val !== "" && val !== "http://") {
- // needed for IE to reset the last range
- editor.focus();
- if(!editor.getRangeHelper().selectedHtml() || description)
- {
- if(!description)
- description = val;
- editor.wysiwygEditorInsertHtml('<a target="_blank" href="' + val + '">' + description + '</a>');
- }
- else
- editor.execCommand("createlink", val);
- }
- editor.closeDropDown(true);
- e.preventDefault();
- });
- editor.createDropDown(caller, "insertlink", content);
- },
- tooltip: "Insert a link"
- },
- // END_COMMAND
- // START_COMMAND: Unlink
- unlink: {
- exec: "unlink",
- tooltip: "Unlink"
- },
- // END_COMMAND
- // START_COMMAND: Quote
- quote: {
- exec: function (caller, html, author) {
- var before = '<blockquote>',
- end = '</blockquote>';
- // if there is HTML passed set end to null so any selected
- // text is replaced
- if(html)
- {
- author = (author ? '<cite>' + author + '</cite>' : '');
- before = before + author + html + end + '<br />';
- end = null;
- }
- // if not add a newline to the end of the inserted quote
- else if(this.getRangeHelper().selectedHtml() === "")
- end = '<br />' + end;
- this.wysiwygEditorInsertHtml(before, end);
- },
- tooltip: "Insert a Quote"
- },
- // END_COMMAND
- // START_COMMAND: Emoticons
- emoticon: {
- exec: function (caller) {
- var appendEmoticon,
- editor = this,
- end = (editor.options.emoticonsCompat ? ' ' : ''),
- content = $('<div />'),
- line = $('<div />');
- appendEmoticon = function (code, emoticon) {
- line.append($('<img />')
- .attr({
- src: emoticon,
- alt: code
- })
- .click(function (e) {
- editor.insert($(this).attr('alt') + end);
- editor.closeDropDown(true);
- e.preventDefault();
- })
- );
- if(line.children().length > 3) {
- content.append(line);
- line = $('<div />');
- }
- };
- $.each(editor.options.emoticons.dropdown, appendEmoticon);
- if(line.children().length > 0)
- content.append(line);
- if(editor.options.emoticons.more) {
- var more = $(
- this._('<a class="sceditor-more">{0}</a>', this._("More"))
- ).click(function () {
- var emoticons = $.extend({}, editor.options.emoticons.dropdown, editor.options.emoticons.more);
- content = $('<div />');
- $.each(emoticons, appendEmoticon);
- if(line.children().length > 0)
- content.append(line);
- editor.createDropDown(caller, "insertemoticon", content);
- return false;
- });
- content.append(more);
- }
- editor.createDropDown(caller, "insertemoticon", content);
- },
- txtExec: function(caller) {
- $.sceditor.command.get('emoticon').exec.call(this, caller);
- },
- keyPress: function (e)
- {
- // make sure emoticons command is in the toolbar before running
- if(this.options.toolbar.indexOf('emoticon') === -1)
- return;
- var editor = this,
- pos = 0,
- curChar = String.fromCharCode(e.which);
- if(!editor.EmoticonsCache) {
- editor.EmoticonsCache = [];
- $.each($.extend({}, editor.options.emoticons.more, editor.options.emoticons.dropdown, editor.options.emoticons.hidden), function(key, url) {
- editor.EmoticonsCache[pos++] = [
- key,
- _tmpl("emoticon", {key: key, url: url})
- ];
- });
- editor.EmoticonsCache.sort(function(a, b){
- return a[0].length - b[0].length;
- });
- }
- if(!editor.longestEmoticonCode)
- editor.longestEmoticonCode = editor.EmoticonsCache[editor.EmoticonsCache.length - 1][0].length;
- if(editor.getRangeHelper().raplaceKeyword(editor.EmoticonsCache, true, true, editor.longestEmoticonCode, editor.options.emoticonsCompat, curChar))
- {
- if(/^\s$/.test(curChar) && editor.options.emoticonsCompat)
- return true;
- e.preventDefault();
- e.stopPropagation();
- return false;
- }
- },
- tooltip: "Insert an emoticon"
- },
- // END_COMMAND
- // START_COMMAND: YouTube
- youtube: {
- _dropDown: function (editor, caller, handleIdFunc) {
- var matches,
- content = _tmpl("youtubeMenu", {
- label: editor._("Video URL:"),
- insert: editor._("Insert")
- }, true);
- content.find('.button').click(function (e) {
- var val = content.find("#link").val().replace("http://", "");
- if (val !== "") {
- matches = val.match(/(?:v=|v\/|embed\/|youtu.be\/)(.{11})/);
- if (matches) val = matches[1];
- if (/^[a-zA-Z0-9_\-]{11}$/.test(val))
- handleIdFunc(val);
- else
- alert('Invalid YouTube video');
- }
- editor.closeDropDown(true);
- e.preventDefault();
- });
- editor.createDropDown(caller, "insertlink", content);
- },
- exec: function (caller) {
- var editor = this;
- $.sceditor.command.get('youtube')._dropDown(
- editor,
- caller,
- function(id) {
- editor.wysiwygEditorInsertHtml(_tmpl("youtube", { id: id }));
- }
- );
- },
- tooltip: "Insert a YouTube video"
- },
- // END_COMMAND
- // START_COMMAND: Date
- date: {
- _date: function (editor) {
- var now = new Date(),
- year = now.getYear(),
- month = now.getMonth()+1,
- day = now.getDate();
- if(year < 2000)
- year = 1900 + year;
- if(month < 10)
- month = "0" + month;
- if(day < 10)
- day = "0" + day;
- return editor.options.dateFormat.replace(/year/i, year).replace(/month/i, month).replace(/day/i, day);
- },
- exec: function () {
- this.insertText($.sceditor.command.get('date')._date(this));
- },
- txtExec: function () {
- this.insertText($.sceditor.command.get('date')._date(this));
- },
- tooltip: "Insert current date"
- },
- // END_COMMAND
- // START_COMMAND: Time
- time: {
- _time: function () {
- var now = new Date(),
- hours = now.getHours(),
- mins = now.getMinutes(),
- secs = now.getSeconds();
- if(hours < 10)
- hours = "0" + hours;
- if(mins < 10)
- mins = "0" + mins;
- if(secs < 10)
- secs = "0" + secs;
- return hours + ':' + mins + ':' + secs;
- },
- exec: function () {
- this.insertText($.sceditor.command.get('time')._time());
- },
- txtExec: function () {
- this.insertText($.sceditor.command.get('time')._time());
- },
- tooltip: "Insert current time"
- },
- // END_COMMAND
- // START_COMMAND: Ltr
- ltr: {
- exec: function() {
- var editor = this,
- elm = editor.getRangeHelper().getFirstBlockParent(),
- $elm = $(elm);
- editor.focus();
- if(!elm || $elm.is('body'))
- {
- editor.execCommand("formatBlock", "p");
- elm = editor.getRangeHelper().getFirstBlockParent();
- $elm = $(elm);
- if(!elm || $elm.is('body'))
- return;
- }
- if($elm.css('direction') === 'ltr')
- $(elm).css('direction', '');
- else
- $(elm).attr('direction', 'ltr');
- },
- tooltip: "Left-to-Right"
- },
- // END_COMMAND
- // START_COMMAND: Rtl
- rtl: {
- exec: function() {
- var editor = this,
- elm = editor.getRangeHelper().getFirstBlockParent(),
- $elm = $(elm);
- editor.focus();
- if(!elm || $elm.is('body'))
- {
- editor.execCommand("formatBlock", "p");
- elm = editor.getRangeHelper().getFirstBlockParent();
- $elm = $(elm);
- if(!elm || $elm.is('body'))
- return;
- }
- if($elm.css('direction') === 'rtl')
- $(elm).css('direction', '');
- else
- $(elm).css('direction', 'rtl');
- },
- tooltip: "Right-to-Left"
- },
- // END_COMMAND
- // START_COMMAND: Print
- print: {
- exec: "print",
- tooltip: "Print"
- },
- // END_COMMAND
- // START_COMMAND: Source
- source: {
- exec: function () {
- this.toggleTextMode();
- },
- txtExec: function () {
- this.toggleTextMode();
- },
- tooltip: "View source"
- },
- // END_COMMAND
- // this is here so that commands above can be removed
- // without having to remove the , after the last one.
- // Needed for IE.
- ignore: {}
- };
- /**
- * Range helper class
- * @class rangeHelper
- * @name jQuery.sceditor.rangeHelper
- */
- $.sceditor.rangeHelper = function(w, d) {
- var win, doc,
- isW3C = true,
- startMarker = "sceditor-start-marker",
- endMarker = "sceditor-end-marker",
- base = this,
- init, _createMarker, _getOuterText, _selectOuterText;
- /**
- * @constructor
- * @param Window window
- * @param Document document
- * @private
- */
- init = function (window, document) {
- doc = document || window.contentDocument || window.document;
- win = window;
- isW3C = !!window.getSelection;
- }(w, d);
- /**
- * <p>Inserts HTML into the current range replacing any selected
- * text.</p>
- *
- * <p>If endHTML is specified the selected contents will be put between
- * html and endHTML. If there is nothing selected html and endHTML are
- * just concated together.</p>
- *
- * @param {string} html
- * @param {string} endHTML
- * @function
- * @name insertHTML
- * @memberOf jQuery.sceditor.rangeHelper.prototype
- */
- base.insertHTML = function(html, endHTML) {
- var node, div;
- if(endHTML)
- html += base.selectedHtml() + endHTML;
- if(isW3C)
- {
- div = doc.createElement('div');
- node = doc.createDocumentFragment();
- div.innerHTML = html;
- while(div.firstChild)
- node.appendChild(div.firstChild);
- base.insertNode(node);
- }
- else
- base.selectedRange().pasteHTML(html);
- };
- /**
- * <p>The same as insertHTML except with DOM nodes instead</p>
- *
- * <p><strong>Warning:</strong> the nodes must belong to the
- * document they are being inserted into. Some browsers
- * will throw exceptions if they don't.</p>
- *
- * @param {Node} node
- * @param {Node} endNode
- *
- * @function
- * @name insertNode
- * @memberOf jQuery.sceditor.rangeHelper.prototype
- */
- base.insertNode = function(node, endNode) {
- if(isW3C)
- {
- var toInsert = doc.createDocumentFragment(),
- range = base.selectedRange(),
- selection, selectAfter;
- toInsert.appendChild(node);
- if(endNode)
- {
- toInsert.appendChild(range.extractContents());
- toInsert.appendChild(endNode);
- }
- selectAfter = toInsert.lastChild;
- range.deleteContents();
- range.insertNode(toInsert);
- selection = doc.createRange();
- selection.setStartAfter(selectAfter);
- base.selectRange(selection);
- }
- else
- base.insertHTML(node.outerHTML, endNode?endNode.outerHTML:null);
- };
- /**
- * <p>Clones the selected Range</p>
- *
- * <p>IE <= 8 will return a TextRange, all other browsers
- * will return a Range object.</p>
- *
- * @return {Range|TextRange}
- * @function
- * @name cloneSelected
- * @memberOf jQuery.sceditor.rangeHelper.prototype
- */
- base.cloneSelected = function() {
- if(!isW3C)
- return base.selectedRange().duplicate();
- return base.selectedRange().cloneRange();
- };
- /**
- * <p>Gets the selected Range</p>
- *
- * <p>IE <= 8 will return a TextRange, all other browsers
- * will return a Range object.</p>
- *
- * @return {Range|TextRange}
- * @function
- * @name selectedRange
- * @memberOf jQuery.sceditor.rangeHelper.prototype
- */
- base.selectedRange = function() {
- var sel;
- if(win.getSelection)
- sel = win.getSelection();
- else
- sel = doc.selection;
- if(sel.getRangeAt && sel.rangeCount <= 0)
- sel.addRange(doc.createRange());
- if(!isW3C)
- return sel.createRange();
- return sel.getRangeAt(0);
- };
- /**
- * Gets the currently selected HTML
- *
- * @return {string}
- * @function
- * @name selectedHtml
- * @memberOf jQuery.sceditor.rangeHelper.prototype
- */
- base.selectedHtml = function() {
- var range = base.selectedRange();
- if(!range)
- return '';
- // IE9+ and all other browsers
- if (window.XMLSerializer)
- return new XMLSerializer().serializeToString(range.cloneContents());
- // IE < 9
- if(!isW3C)
- {
- if(range.text !== '' && range.htmlText)
- return range.htmlText;
- }
- return '';
- };
- /**
- * Gets the parent node of the selected contents in the range
- *
- * @return {HTMLElement}
- * @function
- * @name parentNode
- * @memberOf jQuery.sceditor.rangeHelper.prototype
- */
- base.parentNode = function() {
- var range = base.selectedRange();
- if(isW3C)
- return range.commonAncestorContainer;
- else
- return range.parentElement();
- };
- /**
- * Gets the first block level parent of the selected
- * contents of the range.
- *
- * @return {HTMLElement}
- * @function
- * @name getFirstBlockParent
- * @memberOf jQuery.sceditor.rangeHelper.prototype
- */
- base.getFirstBlockParent = function() {
- var func = function(node) {
- if(!$.sceditor.dom.isInline(node))
- return node;
- var p = node.parentNode;
- if(p)
- return func(p);
- return null;
- };
- return func(base.parentNode());
- };
- /**
- * Inserts a node at either the start or end of the current selection
- *
- * @param {Bool} start
- * @param {Node} node
- * @function
- * @name insertNodeAt
- * @memberOf jQuery.sceditor.rangeHelper.prototype
- */
- base.insertNodeAt = function(start, node) {
- var range = base.cloneSelected();
- range.collapse(start);
- if(range.insertNode)
- range.insertNode(node);
- else
- range.pasteHTML(node.outerHTML);
- };
- /**
- * Creates a marker node
- *
- * @param {String} id
- * @return {Node}
- * @private
- */
- _createMarker = function(id) {
- base.removeMarker(id);
- var marker = doc.createElement("span");
- marker.id = id;
- marker.style.lineHeight = "0";
- marker.style.display = "none";
- marker.className = "sceditor-selection";
- return marker;
- };
- /**
- * Inserts start/end markers for the current selection
- * which can be used by restoreRange to re-select the
- * range.
- *
- * @memberOf jQuery.sceditor.rangeHelper.prototype
- * @function
- * @name insertMarkers
- */
- base.insertMarkers = function() {
- base.insertNodeAt(true, _createMarker(startMarker));
- base.insertNodeAt(false, _createMarker(endMarker));
- };
- /**
- * Gets the marker with the specified ID
- *
- * @param {String} id
- * @return {Node}
- * @function
- * @name getMarker
- * @memberOf jQuery.sceditor.rangeHelper.prototype
- */
- base.getMarker = function(id) {
- return doc.getElementById(id);
- };
- /**
- * Removes the marker with the specified ID
- *
- * @param {String} id
- * @function
- * @name removeMarker
- * @memberOf jQuery.sceditor.rangeHelper.prototype
- */
- base.removeMarker = function(id) {
- var marker = base.getMarker(id);
- if(marker)
- marker.parentNode.removeChild(marker);
- };
- /**
- * Removes the start/end markers
- *
- * @function
- * @name removeMarkers
- * @memberOf jQuery.sceditor.rangeHelper.prototype
- */
- base.removeMarkers = function() {
- base.removeMarker(startMarker);
- base.removeMarker(endMarker);
- };
- /**
- * Saves the current range location. Alias of insertMarkers()
- *
- * @function
- * @name saveRage
- * @memberOf jQuery.sceditor.rangeHelper.prototype
- */
- base.saveRange = function() {
- base.insertMarkers();
- };
- /**
- * Select the specified range
- *
- * @param {Range|TextRange} range
- * @function
- * @name selectRange
- * @memberOf jQuery.sceditor.rangeHelper.prototype
- */
- base.selectRange = function(range) {
- if(!isW3C)
- range.select();
- else
- {
- win.getSelection().removeAllRanges();
- win.getSelection().addRange(range);
- }
- };
- /**
- * Restores the last range saved by saveRange() or insertMarkers()
- *
- * @function
- * @name restoreRange
- * @memberOf jQuery.sceditor.rangeHelper.prototype
- */
- base.restoreRange = function() {
- var range = base.selectedRange(),
- start = base.getMarker(startMarker),
- end = base.getMarker(endMarker);
- if(!start || !end)
- return false;
- if(!isW3C)
- {
- range = doc.body.createTextRange();
- var marker = doc.body.createTextRange();
- marker.moveToElementText(start);
- range.setEndPoint('StartToStart', marker);
- range.moveStart('character', 0);
- marker.moveToElementText(end);
- range.setEndPoint('EndToStart', marker);
- range.moveEnd('character', 0);
- base.selectRange(range);
- }
- else
- {
- range = doc.createRange();
- range.setStartBefore(start);
- range.setEndAfter(end);
- base.selectRange(range);
- }
- base.removeMarkers();
- };
- /**
- * Selects the text left and right of the current selection
- * @param int left
- * @param int right
- * @private
- */
- _selectOuterText = function(left, right) {
- var range = base.cloneSelected();
- range.collapse(false);
- if(!isW3C)
- {
- range.moveStart('character', 0-left);
- range.moveEnd('character', right);
- }
- else
- {
- range.setStart(range.startContainer, range.startOffset-left);
- range.setEnd(range.endContainer, range.endOffset+right);
- //range.deleteContents();
- }
- base.selectRange(range);
- };
- /**
- * Gets the text left or right of the current selection
- * @param bool before
- * @param int length
- * @private
- */
- _getOuterText = function(before, length) {
- var ret = "",
- range = base.cloneSelected();
- range.collapse(false);
- if(before)
- {
- if(!isW3C)
- {
- range.moveStart('character', 0-length);
- ret = range.text;
- }
- else
- {
- ret = range.startContainer.textContent.substr(0, range.startOffset);
- ret = ret.substr(Math.max(0, ret.length - length));
- }
- }
- else
- {
- if(!isW3C)
- {
- range.moveEnd('character', length);
- ret = range.text;
- }
- else
- ret = range.startContainer.textContent.substr(range.startOffset, length);
- }
- return ret;
- };
- /**
- * Replaces keys with values based on the current range
- *
- * @param {Array} rep
- * @param {Bool} includePrev If to include text before or just text after
- * @param {Bool} repSorted If the keys array is pre sorted
- * @param {Int} longestKey Length of the longest key
- * @param {Bool} requireWhiteSpace If the key must be surrounded by whitespace
- * @function
- * @name raplaceKeyword
- * @memberOf jQuery.sceditor.rangeHelper.prototype
- */
- base.raplaceKeyword = function(rep, includeAfter, repSorted, longestKey, requireWhiteSpace, curChar) {
- if(!repSorted)
- rep.sort(function(a, b){
- return a.length - b.length;
- });
- var maxKeyLen = longestKey || rep[rep.length-1][0].length,
- before, after, str, i, start, left, pat, lookStart;
- before = after = str = "";
- if(requireWhiteSpace)
- {
- // forcing spaces around doesn't work with textRanges as they will select text
- // on the other side of an image causing space-img-key to be returned as
- // space-key which would be valid when it's not.
- if(!isW3C)
- return false;
- ++maxKeyLen;
- }
- before = _getOuterText(true, maxKeyLen);
- if(includeAfter)
- after = _getOuterText(false, maxKeyLen);
- str = before + (curChar!=null?curChar:"") + after;
- i = rep.length;
- while(i--)
- {
- pat = new RegExp("(?:[\\s\xA0\u2002\u2003\u2009])" + $.sceditor.regexEscape(rep[i][0]) + "(?=[\\s\xA0\u2002\u2003\u2009])");
- lookStart = before.length - 1 - rep[i][0].length;
- if(requireWhiteSpace)
- --lookStart;
- lookStart = Math.max(0, lookStart);
- if((!requireWhiteSpace && (start = str.indexOf(rep[i][0], lookStart)) > -1) ||
- (requireWhiteSpace && (start = str.substr(lookStart).search(pat)) > -1))
- {
- if(requireWhiteSpace)
- start += lookStart + 1;
- // make sure the substr is between before and after not entierly in one
- // or the other
- if(start > before.length || start+rep[i][0].length + (requireWhiteSpace?1:0) < before.length)
- continue;
- left = before.length - start;
- _selectOuterText(left, rep[i][0].length-left-(curChar!=null&&/^\S/.test(curChar)?1:0));
- base.insertHTML(rep[i][1]);
- return true;
- }
- }
- return false;
- };
- };
- /**
- * Static DOM helper class
- * @class dom
- * @name jQuery.sceditor.dom
- */
- $.sceditor.dom =
- /** @lends jQuery.sceditor.dom */
- {
- /**
- * Loop all child nodes of the passed node
- *
- * The function should accept 1 parameter being the node.
- * If the function returns false the loop will be exited.
- *
- * @param {HTMLElement} node
- * @param {function} func Function that is called for every node, should accept 1 param for the node
- * @param {bool} innermostFirst If the innermost node should be passed to the function before it's parents
- * @param {bool} siblingsOnly If to only traverse the nodes siblings
- * @param {bool} reverse If to traverse the nodes in reverse
- */
- traverse: function(node, func, innermostFirst, siblingsOnly, reverse) {
- if(node)
- {
- node = reverse ? node.lastChild : node.firstChild;
- while(node != null)
- {
- var next = reverse ? node.previousSibling : node.nextSibling;
- if(!innermostFirst && func(node) === false)
- return false;
- // traverse all children
- if(!siblingsOnly && this.traverse(node, func, innermostFirst, siblingsOnly, reverse) === false)
- return false;
- if(innermostFirst && func(node) === false)
- return false;
- // move to next child
- node = next;
- }
- }
- },
- /**
- * Like traverse but loops in reverse
- * @see traverse
- */
- rTraverse: function(node, func, innermostFirst, siblingsOnly) {
- this.traverse(node, func, innermostFirst, siblingsOnly, true);
- },
- /**
- * List of block level elements seperated by bars (|)
- * @type {string}
- */
- blockLevelList: "|body|hr|p|div|h1|h2|h3|h4|h5|h6|address|pre|form|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|blockquote|center|",
- /**
- * Checks if an element is inline
- *
- * @return {bool}
- */
- isInline: function(elm, includeCodeAsBlock) {
- if(!elm || elm.nodeType !== 1)
- return true;
- if(includeCodeAsBlock && elm.tagName.toLowerCase() === 'code')
- return false;
- return $.sceditor.dom.blockLevelList.indexOf("|" + elm.tagName.toLowerCase() + "|") < 0;
- },
- /**
- * Copys the CSS from 1 node to another
- *
- * @param {HTMLElement} from
- * @param {HTMLElement} to
- */
- copyCSS: function(from, to) {
- to.style.cssText = from.style.cssText;
- },
- /**
- * Fixes block level elements inside in inline elements.
- *
- * @param {HTMLElement} node
- */
- fixNesting: function(node) {
- var base = this,
- getLastInlineParent = function(node) {
- while(base.isInline(node.parentNode, true))
- node = node.parentNode;
- return node;
- };
- base.traverse(node, function(node) {
- // if node is an element, and it is blocklevel and the parent isn't block level
- // then it needs fixing
- if(node.nodeType === 1 && !base.isInline(node, true) && base.isInline(node.parentNode, true))
- {
- var parent = getLastInlineParent(node),
- rParent = parent.parentNode,
- before = base.extractContents(parent, node),
- middle = node;
- // copy current styling so when moved out of the parent
- // it still has the same styling
- base.copyCSS(middle, middle);
- rParent.insertBefore(before, parent);
- rParent.insertBefore(middle, parent);
- }
- });
- },
- /**
- * Finds the common parent of two nodes
- *
- * @param {HTMLElement} node1
- * @param {HTMLElement} node2
- * @return {HTMLElement}
- */
- findCommonAncestor: function(node1, node2) {
- // not as fast as making two arrays of parents and comparing
- // but is a lot smaller and as it's currently only used with
- // fixing invalid nesting it doesn't need to be very fast
- return $(node1).parents().has($(node2)).first();
- },
- /**
- * Removes unused whitespace from the root and all it's children
- *
- * @param HTMLElement root
- * @return void
- */
- removeWhiteSpace: function(root) {
- // 00A0 is non-breaking space which should not be striped
- var regex = /[^\S|\u00A0]+/g;
- this.traverse(root, function(node) {
- if(node.nodeType === 3 && $(node).parents('code, pre').length === 0 && node.nodeValue)
- {
- // new lines in text nodes are always ignored in normal handling
- node.nodeValue = node.nodeValue.replace(/[\r\n]/, "");
- //remove empty nodes
- if(!node.nodeValue.length)
- {
- node.parentNode.removeChild(node);
- return;
- }
- if(!/\S|\u00A0/.test(node.nodeValue))
- node.nodeValue = " ";
- else if(regex.test(node.nodeValue))
- node.nodeValue = node.nodeValue.replace(regex, " ");
- }
- });
- },
- /**
- * Extracts all the nodes between the start and end nodes
- *
- * @param {HTMLElement} startNode The node to start extracting at
- * @param {HTMLElement} endNode The node to stop extracting at
- * @return {DocumentFragment}
- */
- extractContents: function(startNode, endNode) {
- var base = this,
- $commonAncestor = base.findCommonAncestor(startNode, endNode),
- commonAncestor = !$commonAncestor?null:$commonAncestor.get(0),
- startReached = false,
- endReached = false;
- return (function extract(root) {
- var df = startNode.ownerDocument.createDocumentFragment();
- base.traverse(root, function(node) {
- // if end has been reached exit loop
- if(endReached || (node === endNode && startReached))
- {
- endReached = true;
- return false;
- }
- if(node === startNode)
- startReached = true;
- var c, n;
- if(startReached)
- {
- // if the start has been reached and this elm contains
- // the end node then clone it
- if(jQuery.contains(node, endNode) && node.nodeType === 1)
- {
- c = extract(node);
- n = node.cloneNode(false);
- n.appendChild(c);
- df.appendChild(n);
- }
- // otherwise just move it
- else
- df.appendChild(node);
- }
- // if this node contains the start node then add it
- else if(jQuery.contains(node, startNode) && node.nodeType === 1)
- {
- c = extract(node);
- n = node.cloneNode(false);
- n.appendChild(c);
- df.appendChild(n);
- }
- }, false);
- return df;
- }(commonAncestor));
- }
- };
- /**
- * Static command helper class
- * @class command
- * @name jQuery.sceditor.command
- */
- $.sceditor.command =
- /** @lends jQuery.sceditor.command */
- {
- /**
- * Gets a command
- *
- * @param {String} name
- * @return {Object|null}
- * @since v1.3.5
- */
- get: function(name) {
- return $.sceditor.commands[name] || null;
- },
- /**
- * <p>Adds a command to the editor or updates an exisiting
- * command if a command with the specified name already exists.</p>
- *
- * <p>Once a command is add it can be included in the toolbar by
- * adding it's name to the toolbar option in the constructor. It
- * can also be executed manually by calling {@link jQuery.sceditor.execCommand}</p>
- *
- * @example
- * $.sceditor.command.set("hello",
- * {
- * exec: function() {
- * alert("Hello World!");
- * }
- * });
- *
- * @param {String} name
- * @param {Object} cmd
- * @return {this|false} Returns false if name or cmd is false
- * @since v1.3.5
- */
- set: function(name, cmd) {
- if(!name || !cmd)
- return false;
- // merge any existing command properties
- cmd = $.extend($.sceditor.commands[name] || {}, cmd);
- cmd.remove = function() { $.sceditor.command.remove(name); };
- $.sceditor.commands[name] = cmd;
- return this;
- },
- /**
- * Removes a command
- *
- * @param {String} name
- * @return {this}
- * @since v1.3.5
- */
- remove: function(name) {
- if($.sceditor.commands[name])
- delete $.sceditor.commands[name];
- return this;
- }
- };
- /**
- * Checks if a command with the specified name exists
- *
- * @param {String} name
- * @return {Bool}
- * @deprecated Since v1.3.5
- * @memberOf jQuery.sceditor
- */
- $.sceditor.commandExists = function(name) {
- return !!$.sceditor.command.get(name);
- };
- /**
- * Adds/updates a command.
- *
- * Only name and exec are required. Exec is only required if
- * the command dose not already exist.
- *
- * @param {String} name The commands name
- * @param {String|Function} exec The commands exec function or string for the native execCommand
- * @param {String} tooltip The commands tooltip text
- * @param {Function} keypress Function that gets called every time a key is pressed
- * @param {Function|Array} txtExec Called when the command is executed in source mode or array containing prepend and optional append
- * @return {Bool}
- * @deprecated Since v1.3.5
- * @memberOf jQuery.sceditor
- */
- $.sceditor.setCommand = function(name, exec, tooltip, keypress, txtExec) {
- return !!$.sceditor.command.set(name, {
- exec: exec,
- tooltip: tooltip,
- keypress: keypress,
- txtExec: txtExec
- });
- };
- $.sceditor.defaultOptions = {
- // Toolbar buttons order and groups. Should be comma seperated and have a bar | to seperate groups
- toolbar: "bold,italic,underline,strike,subscript,superscript|left,center,right,justify|" +
- "font,size,color,removeformat|cut,copy,paste,pastetext|bulletlist,orderedlist|" +
- "table|code,quote|horizontalrule,image,email,link,unlink|emoticon,youtube,date,time|" +
- "ltr,rtl|print,source",
- // Stylesheet to include in the WYSIWYG editor. Will style the WYSIWYG elements
- style: "jquery.sceditor.default.css",
- // Comma seperated list of fonts for the font selector
- fonts: "Arial,Arial Black,Comic Sans MS,Courier New,Georgia,Impact,Sans-serif,Serif,Times New Roman,Trebuchet MS,Verdana",
- // Colors should be comma seperated and have a bar | to signal a new column. If null the colors will be auto generated.
- colors: null,
- locale: "en",
- charset: "utf-8",
- // compatibility mode for if you have emoticons such as :/ This mode requires
- // emoticons to be surrounded by whitespace or end of line chars. This mode
- // has limited As You Type emoticon converstion support (end of line chars)
- // are not accepted as whitespace so only emoticons surrounded by whitespace
- // will work
- emoticonsCompat: false,
- emoticonsRoot: '',
- emoticons: {
- dropdown: {
- ":)": "emoticons/smile.png",
- ":angel:": "emoticons/angel.png",
- ":angry:": "emoticons/angry.png",
- "8-)": "emoticons/cool.png",
- ":'(": "emoticons/cwy.png",
- ":ermm:": "emoticons/ermm.png",
- ":D": "emoticons/grin.png",
- "<3": "emoticons/heart.png",
- ":(": "emoticons/sad.png",
- ":O": "emoticons/shocked.png",
- ":P": "emoticons/tongue.png",
- ";)": "emoticons/wink.png"
- },
- more: {
- ":alien:": "emoticons/alien.png",
- ":blink:": "emoticons/blink.png",
- ":blush:": "emoticons/blush.png",
- ":cheerful:": "emoticons/cheerful.png",
- ":devil:": "emoticons/devil.png",
- ":dizzy:": "emoticons/dizzy.png",
- ":getlost:": "emoticons/getlost.png",
- ":happy:": "emoticons/happy.png",
- ":kissing:": "emoticons/kissing.png",
- ":ninja:": "emoticons/ninja.png",
- ":pinch:": "emoticons/pinch.png",
- ":pouty:": "emoticons/pouty.png",
- ":sick:": "emoticons/sick.png",
- ":sideways:": "emoticons/sideways.png",
- ":silly:": "emoticons/silly.png",
- ":sleeping:": "emoticons/sleeping.png",
- ":unsure:": "emoticons/unsure.png",
- ":woot:": "emoticons/w00t.png",
- ":wassat:": "emoticons/wassat.png"
- },
- hidden: {
- ":whistling:": "emoticons/whistling.png",
- ":love:": "emoticons/wub.png"
- }
- },
- // Width of the editor. Set to null for automatic with
- width: null,
- // Height of the editor including toolbat. Set to null for automatic height
- height: null,
- // If to allow the editor to be resized
- resizeEnabled: true,
- // Min resize to width, set to null for half textarea width or -1 for unlimited
- resizeMinWidth: null,
- // Min resize to height, set to null for half textarea height or -1 for unlimited
- resizeMinHeight: null,
- // Max resize to height, set to null for double textarea height or -1 for unlimited
- resizeMaxHeight: null,
- // Max resize to width, set to null for double textarea width or -1 for unlimited
- resizeMaxWidth: null,
- getHtmlHandler: null,
- getTextHandler: null,
- // date format. year, month and day will be replaced with the users current year, month and day.
- dateFormat: "year-month-day",
- toolbarContainer: null,
- // Curently experimental
- enablePasteFiltering: false,
- readOnly: false,
- rtl: false,
- autofocus: false,
- autoExpand: false,
- // If to run the editor without WYSIWYG support
- runWithoutWysiwygSupport: false,
- id: null,
- //add css to dropdown menu (eg. z-index)
- dropDownCss: { }
- };
- $.fn.sceditor = function (options) {
- if((!options || !options.runWithoutWysiwygSupport) && !$.sceditor.isWysiwygSupported())
- return;
- return this.each(function () {
- (new $.sceditor(this, options));
- });
- };
- })(jQuery, window, document);
- (function($) {
- var extensionMethods = {
- InsertText: function(text, bClear) {
- var bIsSource = this.inSourceMode();
- // @TODO make it put the quote close to the current selection
- if (!bIsSource)
- this.toggleTextMode();
- var current_value = bClear ? text + "\n" : this.getTextareaValue(false) + "\n" + text + "\n";
- this.setTextareaValue(current_value);
- if (!bIsSource)
- this.toggleTextMode();
- },
- getText: function() {
- var current_value = '';
- if(this.inSourceMode())
- current_value = this.getTextareaValue(false);
- else
- current_value = this.getWysiwygEditorValue();
- return current_value;
- },
- appendEmoticon: function (code, emoticon) {
- if (emoticon == '')
- line.append($('<br />'));
- else
- line.append($('<img />')
- .attr({
- src: emoticon,
- alt: code,
- })
- .click(function (e) {
- var start = '', end = '';
-
- if (base.options.emoticonsCompat)
- {
- start = '<span> ';
- end = ' </span>';
- }
- if (base.inSourceMode())
- base.textEditorInsertText(' ' + $(this).attr('alt') + ' ');
- else
- base.wysiwygEditorInsertHtml(start + '<img src="' + $(this).attr("src") +
- '" data-sceditor-emoticon="' + $(this).attr('alt') + '" />' + end);
- e.preventDefault();
- })
- );
- if (line.children().length > 0)
- content.append(line);
- $(".sceditor-toolbar").append(content);
- },
- storeLastState: function (){
- this.wasSource = this.inSourceMode();
- },
- setTextMode: function () {
- if (!this.inSourceMode())
- this.toggleTextMode();
- },
- createPermanentDropDown: function() {
- var emoticons = $.extend({}, this.options.emoticons.dropdown);
- var popup_exists = false;
- content = $('<div class="sceditor-insertemoticon" />');
- line = $('<div />');
- base = this;
- for (smiley_popup in this.options.emoticons.popup)
- {
- popup_exists = true;
- break;
- }
- if (popup_exists)
- {
- this.options.emoticons.more = this.options.emoticons.popup;
- moreButton = $('<div class="sceditor-more-button" />').attr({'class': "sceditor-more"}).text('[' + this._('More') + ']').click(function () {
- if ($(".sceditor-smileyPopup").length > 0)
- {
- $(".sceditor-smileyPopup").fadeIn('fast');
- }
- else
- {
- var emoticons = $.extend({}, base.options.emoticons.popup);
- var popup_position;
- var titlebar = $('<div class="catbg sceditor-popup-grip"/>');
- popupContent = $('<div id="sceditor-popup"/>');
- allowHide = true;
- line = $('<div id="sceditor-popup-smiley"/>');
- adjheight = 0;
- popupContent.append(titlebar);
- closeButton = $('<span />').text('[' + base._('Close') + ']').click(function () {
- $(".sceditor-smileyPopup").fadeOut('fast');
- });
- $.each(emoticons, base.appendEmoticon);
- if (line.children().length > 0)
- popupContent.append(line);
- if (typeof closeButton !== "undefined")
- popupContent.append(closeButton);
- // IE needs unselectable attr to stop it from unselecting the text in the editor.
- // The editor can cope if IE does unselect the text it's just not nice.
- if(base.ieUnselectable !== false) {
- content = $(content);
- content.find(':not(input,textarea)').filter(function() { return this.nodeType===1; }).attr('unselectable', 'on');
- }
- $dropdown = $('<div class="sceditor-dropdown sceditor-smileyPopup" />').append(popupContent);
- $dropdown.appendTo($('body'));
- dropdownIgnoreLastClick = true;
- adjheight = closeButton.height() + titlebar.height();
- $dropdown.css({
- position: "fixed",
- top: $(window).height() * 0.2,
- left: $(window).width() * 0.5 - ($dropdown.find('#sceditor-popup-smiley').width() / 2),
- "max-width": "50%",
- "max-height": "50%",
- }).find('#sceditor-popup-smiley').css({
- height: $dropdown.height() - adjheight,
- "overflow": "auto"
- });
- $('.sceditor-smileyPopup').animaDrag({
- speed: 150,
- interval: 120,
- grip: '.sceditor-popup-grip'
- });
- // stop clicks within the dropdown from being handled
- $dropdown.click(function (e) {
- e.stopPropagation();
- });
- }
- });
- }
- $.each(emoticons, base.appendEmoticon);
- if (typeof moreButton !== "undefined")
- content.append(moreButton);
- }
- };
- $.extend(true, $['sceditor'].prototype, extensionMethods);
- })(jQuery);
- $.sceditor.setCommand(
- 'ftp',
- function (caller) {
- var editor = this,
- content = $(this._('<form><div><label for="link">{0}</label> <input type="text" id="link" value="ftp://" /></div>' +
- '<div><label for="des">{1}</label> <input type="text" id="des" value="" /></div></form>',
- this._("URL:"),
- this._("Description (optional):")
- ))
- .submit(function () {return false;});
- content.append($(
- this._('<div><input type="button" class="button" value="{0}" /></div>',
- this._("Insert")
- )).click(function (e) {
- var val = $(this).parent("form").find("#link").val(),
- description = $(this).parent("form").find("#des").val();
- if(val !== "" && val !== "ftp://") {
- // needed for IE to reset the last range
- editor.focus();
- if(!editor.getRangeHelper().selectedHtml() || description)
- {
- if(!description)
- description = val;
-
- editor.wysiwygEditorInsertHtml('<a href="' + val + '">' + description + '</a>');
- }
- else
- editor.execCommand("createlink", val);
- }
- editor.closeDropDown(true);
- e.preventDefault();
- }));
- editor.createDropDown(caller, "insertlink", content);
- },
- 'Insert FTP Link'
- );
- $.sceditor.setCommand(
- 'glow',
- function () {
- this.wysiwygEditorInsertHtml('[glow=red,2,300]', '[/glow]');
- },
- 'Glow'
- );
- $.sceditor.setCommand(
- 'shadow',
- function () {
- this.wysiwygEditorInsertHtml('[shadow=red,left]', '[/shadow]');
- },
- 'Shadow'
- );
- $.sceditor.setCommand(
- 'tt',
- function () {
- this.wysiwygEditorInsertHtml('<tt>', '</tt>');
- },
- 'Teletype'
- );
- $.sceditor.setCommand(
- 'pre',
- function () {
- this.wysiwygEditorInsertHtml('<pre>', '</pre>');
- },
- 'Pre'
- );
- /**
- * AnimaDrag
- * Animated jQuery Drag and Drop Plugin
- * Version 0.5.1 beta
- * Author Abel Mohler
- * Released with the MIT License: http://www.opensource.org/licenses/mit-license.php
- */
- (function($){
- $.fn.animaDrag = function(o, callback) {
- var defaults = {
- speed: 400,
- interval: 300,
- easing: null,
- cursor: 'move',
- boundary: document.body,
- grip: null,
- overlay: true,
- after: function(e) {
- // $(this).height(this.startheight);
- // $(this).width(this.startwidth);
- },
- during: function(e) {
- $(this).height(this.startheight);
- $(this).width(this.startwidth);
- },
- before: function(e) {
- this.startheight = $(this).innerHeight();
- this.startwidth = $(this).innerWidth();
- },
- afterEachAnimation: function(e) {},
- startheight: 0,
- startwidth: 0
- }
- if(typeof callback == 'function') {
- defaults.after = callback;
- }
- o = $.extend(defaults, o || {});
- return this.each(function() {
- var id, startX, startY, draggableStartX, draggableStartY, dragging = false, Ev, draggable = this,
- grip = ($(this).find(o.grip).length > 0) ? $(this).find(o.grip) : $(this);
- if(o.boundary) {
- var limitTop = $(o.boundary).offset().top, limitLeft = $(o.boundary).offset().left,
- limitBottom = limitTop + $(o.boundary).innerHeight(), limitRight = limitLeft + $(o.boundary).innerWidth();
- }
- grip.mousedown(function(e) {
- o.before.call(draggable, e);
- var lastX, lastY;
- dragging = true;
- Ev = e;
- startX = lastX = e.pageX;
- startY = lastY = e.pageY;
- draggableStartX = $(draggable).offset().left;
- draggableStartY = $(draggable).offset().top;
- $(draggable).css({
- position: 'absolute',
- left: draggableStartX + 'px',
- top: draggableStartY + 'px',
- cursor: o.cursor,
- zIndex: '1010'
- }).addClass('anima-drag').appendTo(document.body);
- if(o.overlay && $('#anima-drag-overlay').length == 0) {
- $('<div id="anima-drag-overlay"></div>').css({
- position: 'absolute',
- top: '0',
- left: '0',
- zIndex: '1000',
- width: $(document.body).outerWidth() + 'px',
- height: $(document.body).outerHeight() + 'px'
- }).appendTo(document.body);
- }
- else if(o.overlay) {
- $('#anima-drag-overlay').show();
- }
- id = setInterval(function() {
- if(lastX != Ev.pageX || lastY != Ev.pageY) {
- var positionX = draggableStartX - (startX - Ev.pageX), positionY = draggableStartY - (startY - Ev.pageY);
- if(positionX < limitLeft && o.boundary) {
- positionX = limitLeft;
- }
- else if(positionX + $(draggable).innerWidth() > limitRight && o.boundary) {
- positionX = limitRight - $(draggable).outerWidth();
- }
- if(positionY < limitTop && o.boundary) {
- positionY = limitTop;
- }
- else if(positionY + $(draggable).innerHeight() > limitBottom && o.boundary) {
- positionY = limitBottom - $(draggable).outerHeight();
- }
- $(draggable).stop().animate({
- left: positionX + 'px',
- top: positionY + 'px'
- }, o.speed, o.easing, function(){o.afterEachAnimation.call(draggable, Ev)});
- }
- lastX = Ev.pageX;
- lastY = Ev.pageY;
- }, o.interval);
- ($.browser.safari || e.preventDefault());
- });
- $(document).mousemove(function(e) {
- if(dragging) {
- Ev = e;
- o.during.call(draggable, e);
- }
- });
- $(document).mouseup(function(e) {
- if(dragging) {
- $(draggable).css({
- cursor: '',
- zIndex: '990'
- }).removeClass('anima-drag');
- $('#anima-drag-overlay').hide().appendTo(document.body);
- clearInterval(id);
- o.after.call(draggable, e);
- dragging = false;
- }
- });
- });
- }
- })(jQuery);
|