1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861 |
- /*
- * Medium.js
- *
- * Copyright 2013, Jacob Kelley - http://jakiestfu.com/
- * Released under the MIT Licence
- * http://opensource.org/licenses/MIT
- *
- * Github: http://github.com/jakiestfu/Medium.js/
- * Version: 1.0
- */
- (function (w, d) {
- 'use strict';
- var Medium = (function () {
-
- var trim = function (string) {
- return string.replace(/^\s+|\s+$/g, '');
- },
- arrayContains = function (array, variable) {
- var i = array.length;
- while (i--) {
- if (array[i] === variable) {
- return true;
- }
- }
- return false;
- },
- //two modes, wild (native) or domesticated (rangy + undo.js)
- rangy = w['rangy'] || null,
- undo = w['Undo'] || null,
- wild = (!rangy || !undo),
- domesticated = (!wild),
- key = w.Key = {
- 'backspace': 8,
- 'tab': 9,
- 'enter': 13,
- 'shift': 16,
- 'ctrl': 17,
- 'alt': 18,
- 'pause': 19,
- 'capsLock': 20,
- 'escape': 27,
- 'pageUp': 33,
- 'pageDown': 34,
- 'end': 35,
- 'home': 36,
- 'leftArrow': 37,
- 'upArrow': 38,
- 'rightArrow': 39,
- 'downArrow': 40,
- 'insert': 45,
- 'delete': 46,
- '0': 48,
- '1': 49,
- '2': 50,
- '3': 51,
- '4': 52,
- '5': 53,
- '6': 54,
- '7': 55,
- '8': 56,
- '9': 57,
- 'a': 65,
- 'b': 66,
- 'c': 67,
- 'd': 68,
- 'e': 69,
- 'f': 70,
- 'g': 71,
- 'h': 72,
- 'i': 73,
- 'j': 74,
- 'k': 75,
- 'l': 76,
- 'm': 77,
- 'n': 78,
- 'o': 79,
- 'p': 80,
- 'q': 81,
- 'r': 82,
- 's': 83,
- 't': 84,
- 'u': 85,
- 'v': 86,
- 'w': 87,
- 'x': 88,
- 'y': 89,
- 'z': 90,
- 'leftWindow': 91,
- 'rightWindowKey': 92,
- 'select': 93,
- 'numpad0': 96,
- 'numpad1': 97,
- 'numpad2': 98,
- 'numpad3': 99,
- 'numpad4': 100,
- 'numpad5': 101,
- 'numpad6': 102,
- 'numpad7': 103,
- 'numpad8': 104,
- 'numpad9': 105,
- 'multiply': 106,
- 'add': 107,
- 'subtract': 109,
- 'decimalPoint': 110,
- 'divide': 111,
- 'f1': 112,
- 'f2': 113,
- 'f3': 114,
- 'f4': 115,
- 'f5': 116,
- 'f6': 117,
- 'f7': 118,
- 'f8': 119,
- 'f9': 120,
- 'f10': 121,
- 'f11': 122,
- 'f12': 123,
- 'numLock': 144,
- 'scrollLock': 145,
- 'semiColon': 186,
- 'equalSign': 187,
- 'comma': 188,
- 'dash': 189,
- 'period': 190,
- 'forwardSlash': 191,
- 'graveAccent': 192,
- 'openBracket': 219,
- 'backSlash': 220,
- 'closeBraket': 221,
- 'singleQuote': 222
- },
- /**
- * Medium.js - Taking control of content editable
- * @constructor
- * @param {Object} [userSettings] user options
- */
- Medium = function (userSettings) {
- var medium = this,
- action = new Medium.Action(),
- cache = new Medium.Cache(),
- cursor = new Medium.Cursor(),
- html = new Medium.HtmlAssistant(),
- utils = new Medium.Utilities(),
- selection = new Medium.Selection(),
- intercept = {
- focus: function (e) {
- e = e || w.event;
- Medium.activeElement = el;
- },
- blur: function (e) {
- e = e || w.event;
- if (Medium.activeElement === el) {
- Medium.activeElement = null;
- }
- html.placeholders();
- },
- down: function (e) {
- e = e || w.event;
- var keepEvent = true;
- //in Chrome it sends out this event before every regular event, not sure why
- if (e.keyCode === 229) return;
- utils.isCommand(e, function () {
- cache.cmd = true;
- }, function () {
- cache.cmd = false;
- });
- utils.isShift(e, function () {
- cache.shift = true;
- }, function () {
- cache.shift = false;
- });
- utils.isModifier(e, function (cmd) {
- if (cache.cmd) {
- if (( (settings.mode === Medium.inlineMode) || (settings.mode === Medium.partialMode) ) && cmd !== "paste") {
- utils.preventDefaultEvent(e);
- return;
- }
- var cmdType = typeof cmd,
- fn = null;
- if (cmdType === "function") {
- fn = cmd;
- } else {
- fn = intercept.command[cmd];
- }
- keepEvent = fn.call(medium, e);
- if (keepEvent === false) {
- utils.preventDefaultEvent(e);
- utils.stopPropagation(e);
- }
- }
- });
- if (settings.maxLength !== -1) {
- var len = html.text().length,
- hasSelection = false,
- selection = w.getSelection();
- if (selection) {
- hasSelection = !selection.isCollapsed;
- }
- if (len >= settings.maxLength && !utils.isSpecial(e) && !utils.isNavigational(e) && !hasSelection) {
- return utils.preventDefaultEvent(e);
- }
- }
- switch (e.keyCode) {
- case key['enter']:
- intercept.enterKey(e);
- break;
- case key['backspace']:
- case key['delete']:
- intercept.backspaceOrDeleteKey(e);
- break;
- }
- return keepEvent;
- },
- up: function (e) {
- e = e || w.event;
- utils.isCommand(e, function () {
- cache.cmd = false;
- }, function () {
- cache.cmd = true;
- });
- html.clean();
- html.placeholders();
- //here we have a key context, so if you need to create your own object within a specific context it is doable
- var keyContext;
- if (
- settings.keyContext !== null
- && ( keyContext = settings.keyContext[e.keyCode] )
- ) {
- var el = cursor.parent();
- if (el) {
- keyContext.call(medium, e, el);
- }
- }
- action.preserveElementFocus();
- },
- command: {
- bold: function (e) {
- utils.preventDefaultEvent(e);
- // IE uses strong instead of b
- (new Medium.Element(medium, 'bold'))
- .setClean(false)
- .invoke(settings.beforeInvokeElement);
- },
- underline: function (e) {
- utils.preventDefaultEvent(e);
- (new Medium.Element(medium, 'underline'))
- .setClean(false)
- .invoke(settings.beforeInvokeElement);
- },
- italicize: function (e) {
- utils.preventDefaultEvent(e);
- (new Medium.Element(medium, 'italic'))
- .setClean(false)
- .invoke(settings.beforeInvokeElement);
- },
- quote: function (e) {
- },
- paste: function (e) {
- medium.makeUndoable();
- if (settings.pasteAsText) {
- var sel = utils.selection.saveSelection();
- utils.pasteHook(function (text) {
- utils.selection.restoreSelection(sel);
- text = text.replace(/\n/g, '<br>');
- (new Medium.Html(medium, text))
- .setClean(false)
- .insert(settings.beforeInsertHtml, true);
- html.clean();
- html.placeholders();
- });
- } else {
- html.clean();
- html.placeholders();
- }
- }
- },
- enterKey: function (e) {
- if (settings.mode === Medium.inlineMode) {
- return utils.preventDefaultEvent(e);
- }
- if (!cache.shift) {
- var focusedElement = html.atCaret() || {},
- children = el.children,
- lastChild = focusedElement === el.lastChild ? el.lastChild : null,
- makeHR,
- secondToLast,
- paragraph;
- if (
- lastChild
- && lastChild !== el.firstChild
- && settings.autoHR
- && settings.mode !== 'partial'
- && settings.tags.horizontalRule
- ) {
- utils.preventDefaultEvent(e);
- makeHR =
- html.text(lastChild) === ""
- && lastChild.nodeName.toLowerCase() === settings.tags.paragraph;
- if (makeHR && children.length >= 2) {
- secondToLast = children[children.length - 2];
- if (secondToLast.nodeName.toLowerCase() === settings.tags.horizontalRule) {
- makeHR = false;
- }
- }
- if (makeHR) {
- html.addTag(settings.tags.horizontalRule, false, true, focusedElement);
- focusedElement = focusedElement.nextSibling;
- }
- if ((paragraph = html.addTag(settings.tags.paragraph, true, null, focusedElement)) !== null) {
- paragraph.innerHTML = '';
- cursor.set(0, paragraph);
- }
- }
- }
- return true;
- },
- backspaceOrDeleteKey: function (e) {
- if (el.lastChild === null) return;
- var lastChild = el.lastChild,
- beforeLastChild = lastChild.previousSibling;
- if (
- lastChild
- && settings.tags.horizontalRule
- && lastChild.nodeName.toLocaleLowerCase() === settings.tags.horizontalRule
- ) {
- el.removeChild(lastChild);
- } else if (
- lastChild
- && beforeLastChild
- && utils.html.text(lastChild).length < 1
- && beforeLastChild.nodeName.toLowerCase() === settings.tags.horizontalRule
- && lastChild.nodeName.toLowerCase() === settings.tags.paragraph
- ) {
- el.removeChild(lastChild);
- el.removeChild(beforeLastChild);
- }
- }
- },
- defaultSettings = {
- element: null,
- modifier: 'auto',
- placeholder: "",
- autofocus: false,
- autoHR: true,
- mode: Medium.richMode,
- maxLength: -1,
- modifiers: {
- 'b': 'bold',
- 'i': 'italicize',
- 'u': 'underline',
- 'v': 'paste'
- },
- tags: {
- 'break': 'br',
- 'horizontalRule': 'hr',
- 'paragraph': 'p',
- 'outerLevel': ['pre', 'blockquote', 'figure'],
- 'innerLevel': ['a', 'b', 'u', 'i', 'img', 'strong']
- },
- cssClasses: {
- editor: 'Medium',
- pasteHook: 'Medium-paste-hook',
- placeholder: 'Medium-placeholder',
- clear: 'Medium-clear'
- },
- attributes: {
- remove: ['style', 'class']
- },
- pasteAsText: true,
- beforeInvokeElement: function () {
- //this = Medium.Element
- },
- beforeInsertHtml: function () {
- //this = Medium.Html
- },
- beforeAddTag: function (tag, shouldFocus, isEditable, afterElement) {
- },
- keyContext: null,
- pasteEventHandler: function (e) {
- e = e || w.event;
- medium.makeUndoable();
- var length = medium.value().length,
- totalLength;
- if (settings.pasteAsText) {
- utils.preventDefaultEvent(e);
- var
- sel = utils.selection.saveSelection(),
- text = prompt(Medium.Messages.pastHere) || '';
- if (text.length > 0) {
- el.focus();
- Medium.activeElement = el;
- utils.selection.restoreSelection(sel);
- //encode the text first
- text = html.encodeHtml(text);
- //cut down it's length
- totalLength = text.length + length;
- if (settings.maxLength > 0 && totalLength > settings.maxLength) {
- text = text.substring(0, settings.maxLength - length);
- }
- if (settings.mode !== Medium.inlineMode) {
- text = text.replace(/\n/g, '<br>');
- }
- (new Medium.Html(medium, text))
- .setClean(false)
- .insert(settings.beforeInsertHtml, true);
- html.clean();
- html.placeholders();
- return false;
- }
- } else {
- setTimeout(function () {
- html.clean();
- html.placeholders();
- }, 20);
- }
- }
- },
- settings = utils.deepExtend(defaultSettings, userSettings),
- el,
- newVal,
- i,
- bridge = {};
- for (i in defaultSettings) {
- // Override defaults with data-attributes
- if (
- typeof defaultSettings[i] !== 'object'
- && defaultSettings.hasOwnProperty(i)
- && settings.element.getAttribute('data-medium-' + key)
- ) {
- newVal = settings.element.getAttribute('data-medium-' + key);
- if (newVal.toLowerCase() === "false" || newVal.toLowerCase() === "true") {
- newVal = newVal.toLowerCase() === "true";
- }
- settings[i] = newVal;
- }
- }
- if (settings.modifiers) {
- for (i in settings.modifiers) {
- if (typeof(key[i]) !== 'undefined') {
- settings.modifiers[key[i]] = settings.modifiers[i];
- }
- }
- }
- if (settings.keyContext) {
- for (i in settings.keyContext) {
- if (typeof(key[i]) !== 'undefined') {
- settings.keyContext[key[i]] = settings.keyContext[i];
- }
- }
- }
- // Extend Settings
- el = settings.element;
- // Editable
- el.contentEditable = true;
- el.className
- += (' ' + settings.cssClasses.editor)
- + (' ' + settings.cssClasses.editor + '-' + settings.mode);
- settings.tags = (settings.tags || {});
- if (settings.tags.outerLevel) {
- settings.tags.outerLevel = settings.tags.outerLevel.concat([settings.tags.paragraph, settings.tags.horizontalRule]);
- }
- this.settings = settings;
- this.element = el;
- this.intercept = intercept;
- this.action = action;
- this.cache = cache;
- this.cursor = cursor;
- this.html = html;
- this.utils = utils;
- this.selection = selection;
- bridge.element = el;
- bridge.medium = this;
- bridge.settings = settings;
- bridge.action = action;
- bridge.cache = cache;
- bridge.cursor = cursor;
- bridge.html = html;
- bridge.intercept = intercept;
- bridge.utils = utils;
- bridge.selection = selection;
- action.setBridge(bridge);
- cache.setBridge(bridge);
- cursor.setBridge(bridge);
- html.setBridge(bridge);
- utils.setBridge(bridge);
- selection.setBridge(bridge);
- // Initialize editor
- html.clean();
- html.placeholders();
- action.preserveElementFocus();
- // Capture Events
- action.listen();
- if (wild) {
- this.makeUndoable = function () {
- };
- } else {
- this.dirty = false;
- this.undoable = new Medium.Undoable(this);
- this.undo = this.undoable.undo;
- this.redo = this.undoable.redo;
- this.makeUndoable = this.undoable.makeUndoable;
- }
- el.medium = this;
- // Set as initialized
- cache.initialized = true;
- };
- Medium.prototype = {
- /**
- *
- * @param {String|Object} html
- * @param {Function} [callback]
- * @returns {Medium}
- */
- insertHtml: function (html, callback) {
- var result = (new Medium.Html(this, html))
- .insert(this.settings.beforeInsertHtml);
- this.utils.triggerEvent(this.element, "change");
- if (callback) {
- callback.apply(result);
- }
- return this;
- },
- /**
- *
- * @param {String} tagName
- * @param {Object} [attributes]
- * @returns {Medium}
- */
- invokeElement: function (tagName, attributes) {
- var settings = this.settings,
- attributes = attributes || {},
- remove = attributes.remove || [];
- switch (settings.mode) {
- case Medium.inlineMode:
- case Medium.partialMode:
- return this;
- default:
- }
- //invoke works off class, so if it isn't there, we just add it
- if (remove.length > 0) {
- if (!arrayContains(settings, 'class')) {
- remove.push('class');
- }
- }
- (new Medium.Element(this, tagName, attributes))
- .invoke(this.settings.beforeInvokeElement);
- this.utils.triggerEvent(this.element, "change");
- return this;
- },
- /**
- * @returns {string}
- */
- behavior: function () {
- return (wild ? 'wild' : 'domesticated');
- },
- /**
- *
- * @param value
- * @returns {Medium}
- */
- value: function (value) {
- if (typeof value !== 'undefined') {
- this.element.innerHTML = value;
- this.html.clean();
- this.html.placeholders();
- } else {
- return this.element.innerHTML;
- }
- return this;
- },
- /**
- * Focus on element
- * @returns {Medium}
- */
- focus: function () {
- var el = this.element;
- el.focus();
- return this;
- },
- /**
- * Select all text
- * @returns {Medium}
- */
- select: function () {
- var el = this.element,
- range,
- selection;
- el.focus();
- if (d.body.createTextRange) {
- range = d.body.createTextRange();
- range.moveToElementText(el);
- range.select();
- } else if (w.getSelection) {
- selection = w.getSelection();
- range = d.createRange();
- range.selectNodeContents(el);
- selection.removeAllRanges();
- selection.addRange(range);
- }
- return this;
- },
- isActive: function () {
- return (Medium.activeElement === this.element);
- },
- destroy: function () {
- var el = this.element,
- intercept = this.intercept,
- settings = this.settings,
- placeholder = this.placeholder || null;
- if (placeholder !== null && placeholder.setup) {
- //remove placeholder
- placeholder.parentNode.removeChild(placeholder);
- delete el.placeHolderActive;
- }
- //remove contenteditable
- el.removeAttribute('contenteditable');
- //remove classes
- el.className = trim(el.className
- .replace(settings.cssClasses.editor, '')
- .replace(settings.cssClasses.clear, '')
- .replace(settings.cssClasses.editor + '-' + settings.mode, ''));
- //remove events
- this.utils
- .removeEvent(el, 'keyup', intercept.up)
- .removeEvent(el, 'keydown', intercept.down)
- .removeEvent(el, 'focus', intercept.focus)
- .removeEvent(el, 'blur', intercept.focus)
- .removeEvent(el, 'paste', settings.pasteEventHandler);
- },
- // Clears the element and restores the placeholder
- clear: function () {
- this.element.innerHTML = '';
- this.html.placeholders();
- }
- };
- /**
- * @param {Medium} medium
- * @param {String} tagName
- * @param {Object} [attributes]
- * @constructor
- */
- Medium.Element = function (medium, tagName, attributes) {
- this.medium = medium;
- this.element = medium.settings.element;
- if (wild) {
- this.tagName = tagName;
- } else {
- switch (tagName.toLowerCase()) {
- case 'bold':
- this.tagName = 'b';
- break;
- case 'italic':
- this.tagName = 'i';
- break;
- case 'underline':
- this.tagName = 'u';
- break;
- default:
- this.tagName = tagName;
- }
- }
- this.attributes = attributes || {};
- this.clean = true;
- };
- /**
- * @constructor
- * @param {Medium} medium
- * @param {String|HtmlElement} html
- */
- Medium.Html = function (medium, html) {
- this.medium = medium;
- this.element = medium.settings.element;
- this.html = html;
- this.clean = true;
- };
- /**
- *
- * @constructor
- */
- Medium.Injector = function () {
- };
- if (wild) {
- Medium.Element.prototype = {
- /**
- * @methodOf Medium.Element
- * @param {Function} [fn]
- */
- invoke: function (fn) {
- if (Medium.activeElement === this.element) {
- if (fn) {
- fn.apply(this);
- }
- d.execCommand(this.tagName, false);
- }
- },
- setClean: function () {
- return this;
- }
- };
- Medium.Injector.prototype = {
- /**
- * @methodOf Medium.Injector
- * @param {String|HtmlElement} htmlRaw
- * @param {Boolean} [selectInserted]
- * @returns {null}
- */
- inject: function (htmlRaw, selectInserted) {
- this.insertHTML(htmlRaw, selectInserted);
- return null;
- }
- };
- /**
- *
- * @constructor
- */
- Medium.Undoable = function () {
- };
- }
- //if medium is domesticated (ie, not wild)
- else {
- rangy.rangePrototype.insertNodeAtEnd = function (node) {
- var range = this.cloneRange();
- range.collapse(false);
- range.insertNode(node);
- range.detach();
- this.setEndAfter(node);
- };
- Medium.Element.prototype = {
- /**
- * @methodOf Medium.Element
- * @param {Function} [fn]
- */
- invoke: function (fn) {
- if (Medium.activeElement === this.element) {
- if (fn) {
- fn.apply(this);
- }
- var
- attr = this.attributes,
- tagName = this.tagName.toLowerCase(),
- applier,
- cl;
- if (attr.className !== undefined) {
- cl = (attr.className.split[' '] || [attr.className]).shift();
- delete attr.className;
- } else {
- cl = 'medium-' + tagName;
- }
- applier = rangy.createClassApplier(cl, {
- elementTagName: tagName,
- elementAttributes: this.attributes
- });
- this.medium.makeUndoable();
- applier.toggleSelection(w);
- if (this.clean) {
- //cleanup
- this.medium.html.clean();
- this.medium.html.placeholders();
- }
- }
- },
- /**
- *
- * @param {Boolean} clean
- * @returns {Medium.Element}
- */
- setClean: function (clean) {
- this.clean = clean;
- return this;
- }
- };
- Medium.Injector.prototype = {
- /**
- * @methodOf Medium.Injector
- * @param {String|HtmlElement} htmlRaw
- * @returns {HtmlElement}
- */
- inject: function (htmlRaw) {
- var html, isConverted = false;
- if (typeof htmlRaw === 'string') {
- var htmlConverter = d.createElement('div');
- htmlConverter.innerHTML = htmlRaw;
- html = htmlConverter.childNodes;
- isConverted = true;
- } else {
- html = htmlRaw;
- }
- this.insertHTML('<span id="wedge"></span>');
- var wedge = d.getElementById('wedge'),
- parent = wedge.parentNode,
- i = 0;
- wedge.removeAttribute('id');
- if (isConverted) {
- while (i < html.length) {
- parent.insertBefore(html[i], wedge);
- }
- } else {
- parent.insertBefore(html, wedge);
- }
- parent.removeChild(wedge);
- wedge = null;
- return html;
- }
- };
- /**
- * @param {Medium} medium
- * @constructor
- */
- Medium.Undoable = function (medium) {
- var me = this,
- element = medium.settings.element,
- utils = medium.utils,
- addEvent = utils.addEvent,
- startValue = element.innerHTML,
- timer,
- stack = new Undo.Stack(),
- EditCommand = Undo.Command.extend({
- constructor: function (oldValue, newValue) {
- this.oldValue = oldValue;
- this.newValue = newValue;
- },
- execute: function () {
- },
- undo: function () {
- element.innerHTML = this.oldValue;
- medium.canUndo = stack.canUndo();
- medium.canRedo = stack.canRedo();
- medium.dirty = stack.dirty();
- },
- redo: function () {
- element.innerHTML = this.newValue;
- medium.canUndo = stack.canUndo();
- medium.canRedo = stack.canRedo();
- medium.dirty = stack.dirty();
- }
- }),
- makeUndoable = function () {
- var newValue = element.innerHTML;
- // ignore meta key presses
- if (newValue != startValue) {
- if (!me.movingThroughStack) {
- // this could try and make a diff instead of storing snapshots
- stack.execute(new EditCommand(startValue, newValue));
- startValue = newValue;
- medium.dirty = stack.dirty();
- }
- utils.triggerEvent(medium.settings.element, "change");
- }
- };
- this.medium = medium;
- this.timer = timer;
- this.stack = stack;
- this.makeUndoable = makeUndoable;
- this.EditCommand = EditCommand;
- this.movingThroughStack = false;
- addEvent(element, 'keyup', function (e) {
- if (e.ctrlKey || e.keyCode === key.z) {
- utils.preventDefaultEvent(e);
- return;
- }
- // a way too simple algorithm in place of single-character undo
- clearTimeout(timer);
- timer = setTimeout(function () {
- makeUndoable();
- }, 250);
- });
- addEvent(element, 'keydown', function (e) {
- if (!e.ctrlKey || e.keyCode !== key.z) {
- me.movingThroughStack = false;
- return true;
- }
- utils.preventDefaultEvent(e);
- me.movingThroughStack = true;
- if (e.shiftKey) {
- stack.canRedo() && stack.redo()
- } else {
- stack.canUndo() && stack.undo();
- }
- });
- };
- }
- //Thank you Tim Down (super uber genius): http://stackoverflow.com/questions/6690752/insert-html-at-caret-in-a-contenteditable-div/6691294#6691294
- Medium.Injector.prototype.insertHTML = function (html, selectPastedContent) {
- var sel, range;
- if (w.getSelection) {
- // IE9 and non-IE
- sel = w.getSelection();
- if (sel.getRangeAt && sel.rangeCount) {
- range = sel.getRangeAt(0);
- range.deleteContents();
- // Range.createContextualFragment() would be useful here but is
- // only relatively recently standardized and is not supported in
- // some browsers (IE9, for one)
- var el = d.createElement("div");
- el.innerHTML = html;
- var frag = d.createDocumentFragment(), node, lastNode;
- while ((node = el.firstChild)) {
- lastNode = frag.appendChild(node);
- }
- var firstNode = frag.firstChild;
- range.insertNode(frag);
- // Preserve the selection
- if (lastNode) {
- range = range.cloneRange();
- range.setStartAfter(lastNode);
- if (selectPastedContent) {
- range.setStartBefore(firstNode);
- } else {
- range.collapse(true);
- }
- sel.removeAllRanges();
- sel.addRange(range);
- }
- }
- } else if ((sel = d.selection) && sel.type != "Control") {
- // IE < 9
- var originalRange = sel.createRange();
- originalRange.collapse(true);
- sel.createRange().pasteHTML(html);
- if (selectPastedContent) {
- range = sel.createRange();
- range.setEndPoint("StartToStart", originalRange);
- range.select();
- }
- }
- };
- Medium.Html.prototype = {
- setBridge: function (bridge) {
- for (var i in bridge) {
- this[i] = bridge[i];
- }
- },
- /**
- * @methodOf Medium.Html
- * @param {Function} [fn]
- * @param {Boolean} [selectInserted]
- * @returns {HtmlElement}
- */
- insert: function (fn, selectInserted) {
- if (Medium.activeElement === this.element) {
- if (fn) {
- fn.apply(this);
- }
- var inserted = this.injector.inject(this.html, selectInserted);
- if (this.clean) {
- //cleanup
- this.medium.html.clean();
- this.medium.html.placeholders();
- }
- this.medium.makeUndoable();
- return inserted;
- } else {
- return null;
- }
- },
- /**
- * @attributeOf {Medium.Injector} Medium.Html
- */
- injector: new Medium.Injector(),
- /**
- * @methodOf Medium.Html
- * @param clean
- * @returns {Medium.Html}
- */
- setClean: function (clean) {
- this.clean = clean;
- return this;
- }
- };
- Medium.Utilities = function () {
- };
- Medium.Utilities.prototype = {
- setBridge: function (bridge) {
- for (var i in bridge) {
- this[i] = bridge[i];
- }
- },
- /*
- * Keyboard Interface events
- */
- isCommand: function (e, fnTrue, fnFalse) {
- var s = this.settings;
- if ((s.modifier === 'ctrl' && e.ctrlKey ) ||
- (s.modifier === 'cmd' && e.metaKey ) ||
- (s.modifier === 'auto' && (e.ctrlKey || e.metaKey) )
- ) {
- return fnTrue.call();
- } else {
- return fnFalse.call();
- }
- },
- isShift: function (e, fnTrue, fnFalse) {
- if (e.shiftKey) {
- return fnTrue.call();
- } else {
- return fnFalse.call();
- }
- },
- isModifier: function (e, fn) {
- var cmd = this.settings.modifiers[e.keyCode];
- if (cmd) {
- return fn.call(null, cmd);
- }
- return false;
- },
- special: (function () {
- var special = {};
- special[key['backspace']] = true;
- special[key['shift']] = true;
- special[key['ctrl']] = true;
- special[key['alt']] = true;
- special[key['delete']] = true;
- special[key['cmd']] = true;
- return special;
- })(),
- isSpecial: function (e) {
- if (this.cache.cmd) {
- return true;
- }
- return typeof this.special[e.keyCode] !== 'undefined';
- },
- navigational: (function () {
- var navigational = {};
- navigational[key['upArrow']] = true;
- navigational[key['downArrow']] = true;
- navigational[key['leftArrow']] = true;
- navigational[key['rightArrow']] = true;
- return navigational;
- })(),
- isNavigational: function (e) {
- return typeof this.navigational[e.keyCode] !== 'undefined';
- },
- /*
- * Handle Events
- */
- addEvent: function addEvent(element, eventName, func) {
- if (element.addEventListener) {
- element.addEventListener(eventName, func, false);
- } else if (element.attachEvent) {
- element.attachEvent("on" + eventName, func);
- } else {
- element['on' + eventName] = func;
- }
- return this;
- },
- removeEvent: function removeEvent(element, eventName, func) {
- if (element.removeEventListener) {
- element.removeEventListener(eventName, func, false);
- } else if (element.detachEvent) {
- element.detachEvent("on" + eventName, func);
- } else {
- element['on' + eventName] = null;
- }
- return this;
- },
- preventDefaultEvent: function (e) {
- if (e.preventDefault) {
- e.preventDefault();
- } else {
- e.returnValue = false;
- }
- return this;
- },
- stopPropagation: function (e) {
- e = e || window.event;
- e.cancelBubble = true;
- if (e.stopPropagation !== undefined) {
- e.stopPropagation();
- }
- },
- triggerEvent: function (element, eventName) {
- var e;
- if (d.createEvent) {
- e = d.createEvent("HTMLEvents");
- e.initEvent(eventName, true, true);
- e.eventName = eventName;
- element.dispatchEvent(e);
- } else {
- e = d.createEventObject();
- element.fireEvent("on" + eventName, e);
- }
- return this;
- },
- deepExtend: function (destination, source) {
- for (var property in source) {
- if (
- source[property]
- && source[property].constructor
- && source[property].constructor === Object
- ) {
- destination[property] = destination[property] || {};
- this.deepExtend(destination[property], source[property]);
- } else {
- destination[property] = source[property];
- }
- }
- return destination;
- },
- /*
- * This is a Paste Hook. When the user pastes
- * content, this ultimately converts it into
- * plain text before inserting the data.
- */
- pasteHook: function (fn) {
- var textarea = d.createElement('textarea'),
- el = this.element,
- existingValue,
- existingLength,
- overallLength,
- s = this.settings,
- medium = this.medium,
- html = this.html;
- textarea.className = s.cssClasses.pasteHook;
- el.parentNode.appendChild(textarea);
- textarea.focus();
- if (!wild) {
- medium.makeUndoable();
- }
- setTimeout(function () {
- el.focus();
- if (s.maxLength > 0) {
- existingValue = html.text(el);
- existingLength = existingValue.length;
- overallLength = existingLength + textarea.value.length;
- if (overallLength > existingLength) {
- textarea.value = textarea.value.substring(0, s.maxLength - existingLength);
- }
- }
- fn(textarea.value);
- html.deleteNode(textarea);
- }, 2);
- },
- setupContents: function () {
- var el = this.element,
- children = el.children,
- childNodes = el.childNodes,
- initialParagraph;
- if (
- !this.settings.tags.paragraph
- || children.length > 0
- || this.settings.mode === Medium.inlineMode
- ) {
- return;
- }
- //has content, but no children
- if (childNodes.length > 0) {
- initialParagraph = d.createElement(this.settings.tags.paragraph);
- if (el.innerHTML.match('^[&]nbsp[;]')) {
- el.innerHTML = el.innerHTML.substring(6, el.innerHTML.length - 1);
- }
- initialParagraph.innerHTML = el.innerHTML;
- el.innerHTML = '';
- el.appendChild(initialParagraph);
- this.cursor.set(initialParagraph.innerHTML.length, initialParagraph);
- } else {
- initialParagraph = d.createElement(this.settings.tags.paragraph);
- initialParagraph.innerHTML = ' ';
- el.appendChild(initialParagraph);
- }
- },
- traverseAll: function (element, options, depth) {
- var children = element.childNodes,
- length = children.length,
- i = 0,
- node,
- depth = depth || 1;
- options = options || {};
- if (length > 0) {
- for (; i < length; i++) {
- node = children[i];
- switch (node.nodeType) {
- case 1:
- this.traverseAll(node, options, depth + 1);
- if (options.element !== undefined) options.element(node, i, depth, element);
- break;
- case 3:
- if (options.fragment !== undefined) options.fragment(node, i, depth, element);
- }
- //length may change
- length = children.length;
- //if length did change, and we are at the last item, this causes infinite recursion, so if we are at the last item, then stop to prevent this
- if (node === element.lastChild) {
- i = length;
- }
- }
- }
- }
- };
- /*
- * Handle Selection Logic
- */
- Medium.Selection = function () {
- };
- Medium.Selection.prototype = {
- setBridge: function (bridge) {
- for (var i in bridge) {
- this[i] = bridge[i];
- }
- },
- saveSelection: function () {
- if (w.getSelection) {
- var sel = w.getSelection();
- if (sel.rangeCount > 0) {
- return sel.getRangeAt(0);
- }
- } else if (d.selection && d.selection.createRange) { // IE
- return d.selection.createRange();
- }
- return null;
- },
- restoreSelection: function (range) {
- if (range) {
- if (w.getSelection) {
- var sel = w.getSelection();
- sel.removeAllRanges();
- sel.addRange(range);
- } else if (d.selection && range.select) { // IE
- range.select();
- }
- }
- }
- };
- /*
- * Handle Cursor Logic
- */
- Medium.Cursor = function () {
- };
- Medium.Cursor.prototype = {
- setBridge: function (bridge) {
- for (var i in bridge) {
- this[i] = bridge[i];
- }
- },
- set: function (pos, el) {
- var range,
- html = this.html;
- if (d.createRange) {
- var selection = w.getSelection(),
- lastChild = html.lastChild(),
- length = html.text(lastChild).length - 1,
- toModify = el ? el : lastChild,
- theLength = ((typeof pos !== 'undefined') && (pos !== null) ? pos : length);
- range = d.createRange();
- try{
- range.setStart(toModify, theLength);
- }
- catch(e){};
- range.collapse(true);
- selection.removeAllRanges();
- selection.addRange(range);
- } else {
- range = d.body.createTextRange();
- range.moveToElementText(el);
- range.collapse(false);
- range.select();
- }
- },
- parent: function () {
- var target = null, range;
- if (w.getSelection) {
- range = w.getSelection().getRangeAt(0);
- target = range.commonAncestorContainer;
- target = (target.nodeType === 1
- ? target
- : target.parentNode
- );
- }
- else if (d.selection) {
- target = d.selection.createRange().parentElement();
- }
- if (target.tagName == 'SPAN') {
- target = target.parentNode;
- }
- return target;
- },
- caretToBeginning: function (el) {
- this.set(0, el);
- },
- caretToEnd: function (el) {
- this.set(this.html.text(el).length, el);
- }
- };
- /*
- * HTML Abstractions
- */
- Medium.HtmlAssistant = function () {
- };
- Medium.HtmlAssistant.prototype = {
- setBridge: function (bridge) {
- for (var i in bridge) {
- this[i] = bridge[i];
- }
- },
- encodeHtml: function (html) {
- return d.createElement('a').appendChild(
- d.createTextNode(html)).parentNode.innerHTML;
- },
- text: function (node, val) {
- node = node || this.settings.element;
- if (val) {
- if ((node.textContent) && (typeof (node.textContent) != "undefined")) {
- node.textContent = val;
- } else {
- node.innerText = val;
- }
- }
- else if (node.innerText) {
- return trim(node.innerText);
- }
- else if (node.textContent) {
- return trim(node.textContent);
- }
- //document fragment
- else if (node.data) {
- return trim(node.data);
- }
- //for good measure
- return '';
- },
- changeTag: function (oldNode, newTag) {
- var newNode = d.createElement(newTag),
- node,
- nextNode;
- node = oldNode.firstChild;
- while (node) {
- nextNode = node.nextSibling;
- newNode.appendChild(node);
- node = nextNode;
- }
- oldNode.parentNode.insertBefore(newNode, oldNode);
- oldNode.parentNode.removeChild(oldNode);
- return newNode;
- },
- deleteNode: function (el) {
- el.parentNode.removeChild(el);
- },
- placeholders: function () {
- //in IE8, just gracefully degrade to no placeholders
- if (!w.getComputedStyle) return;
- var that = this,
- s = this.settings,
- placeholder = this.medium.placeholder || (this.medium.placeholder = d.createElement('div')),
- el = s.element,
- style = placeholder.style,
- elStyle = w.getComputedStyle(el, null),
- qStyle = function (prop) {
- return elStyle.getPropertyValue(prop)
- },
- utils = this.utils,
- text = utils.html.text(el),
- cursor = this.cursor,
- childCount = el.children.length;
- el.placeholder = placeholder;
- // Empty Editor
- if (text.length < 1 && childCount < 2) {
- if (el.placeHolderActive) return;
- if (!el.innerHTML.match('<' + s.tags.paragraph)) {
- el.innerHTML = '';
- }
- // We need to add placeholders
- if (s.placeholder.length > 0) {
- if (!placeholder.setup) {
- placeholder.setup = true;
- //background & background color
- style.background = qStyle('background');
- style.backgroundColor = qStyle('background-color');
- //text size & text color
- style.fontSize = qStyle('font-size');
- style.color = elStyle.color;
- //begin box-model
- //margin
- style.marginTop = qStyle('margin-top');
- style.marginBottom = qStyle('margin-bottom');
- style.marginLeft = qStyle('margin-left');
- style.marginRight = qStyle('margin-right');
- //padding
- style.paddingTop = qStyle('padding-top');
- style.paddingBottom = qStyle('padding-bottom');
- style.paddingLeft = qStyle('padding-left');
- style.paddingRight = qStyle('padding-right');
- //border
- style.borderTopWidth = qStyle('border-top-width');
- style.borderTopColor = qStyle('border-top-color');
- style.borderTopStyle = qStyle('border-top-style');
- style.borderBottomWidth = qStyle('border-bottom-width');
- style.borderBottomColor = qStyle('border-bottom-color');
- style.borderBottomStyle = qStyle('border-bottom-style');
- style.borderLeftWidth = qStyle('border-left-width');
- style.borderLeftColor = qStyle('border-left-color');
- style.borderLeftStyle = qStyle('border-left-style');
- style.borderRightWidth = qStyle('border-right-width');
- style.borderRightColor = qStyle('border-right-color');
- style.borderRightStyle = qStyle('border-right-style');
- //end box model
- //element setup
- placeholder.className = s.cssClasses.placeholder + ' ' + s.cssClasses.placeholder + '-' + s.mode;
- placeholder.innerHTML = '<div>' + s.placeholder + '</div>';
- el.parentNode.insertBefore(placeholder, el);
- }
- el.className += ' ' + s.cssClasses.clear;
- style.display = '';
- // Add base P tag and do auto focus, give it a min height if el has one
- style.minHeight = el.clientHeight + 'px';
- style.minWidth = el.clientWidth + 'px';
- if (s.mode !== Medium.inlineMode) {
- utils.setupContents();
- if (childCount === 0 && el.firstChild) {
- cursor.set(0, el.firstChild);
- }
- }
- }
- el.placeHolderActive = true;
- } else if (el.placeHolderActive) {
- el.placeHolderActive = false;
- style.display = 'none';
- el.className = trim(el.className.replace(s.cssClasses.clear, ''));
- utils.setupContents();
- }
- },
- /**
- * Cleans element
- * @param {HtmlElement} [el] default is settings.element
- */
- clean: function (el) {
- /*
- * Deletes invalid nodes
- * Removes Attributes
- */
- var s = this.settings,
- placeholderClass = s.cssClasses.placeholder,
- attributesToRemove = (s.attributes || {}).remove || [],
- tags = s.tags || {},
- onlyOuter = tags.outerLevel || null,
- onlyInner = tags.innerLevel || null,
- outerSwitch = {},
- innerSwitch = {},
- paragraphTag = (tags.paragraph || '').toUpperCase(),
- html = this.html,
- attr,
- text,
- j;
- el = el || s.element;
- if (onlyOuter !== null) {
- for (j = 0; j < onlyOuter.length; j++) {
- outerSwitch[onlyOuter[j].toUpperCase()] = true;
- }
- }
- if (onlyInner !== null) {
- for (j = 0; j < onlyInner.length; j++) {
- innerSwitch[onlyInner[j].toUpperCase()] = true;
- }
- }
- this.utils.traverseAll(el, {
- element: function (child, i, depth, parent) {
- var nodeName = child.nodeName,
- shouldDelete = true;
- // Remove attributes
- for (j = 0; j < attributesToRemove.length; j++) {
- attr = attributesToRemove[j];
- if (child.hasAttribute(attr)) {
- if (child.getAttribute(attr) !== placeholderClass) {
- child.removeAttribute(attr);
- }
- }
- }
- if (onlyOuter === null && onlyInner === null) {
- return;
- }
- if (depth === 1 && outerSwitch[nodeName] !== undefined) {
- shouldDelete = false;
- } else if (depth > 1 && innerSwitch[nodeName] !== undefined) {
- shouldDelete = false;
- }
- // Convert tags or delete
- if (shouldDelete) {
- if (w.getComputedStyle(child, null).getPropertyValue('display') === 'block') {
- if (paragraphTag.length > 0 && paragraphTag !== nodeName) {
- html.changeTag(child, paragraphTag);
- }
- if (depth > 1) {
- while (parent.childNodes.length > i) {
- parent.parentNode.insertBefore(parent.lastChild, parent.nextSibling);
- }
- }
- } else {
- switch (nodeName) {
- case 'BR':
- if (child === child.parentNode.lastChild) {
- if (child === child.parentNode.firstChild) {
- break;
- }
- text = document.createTextNode("");
- text.innerHTML = ' ';
- parent.insertBefore(text, child);
- break;
- }
- default:
- while (child.firstChild !== null) {
- parent.insertBefore(child.firstChild, child);
- }
- html.deleteNode(child);
- break;
- }
- }
- }
- }
- });
- },
- lastChild: function () {
- return this.element.lastChild;
- },
- addTag: function (tag, shouldFocus, isEditable, afterElement) {
- if (!this.settings.beforeAddTag(tag, shouldFocus, isEditable, afterElement)) {
- var newEl = d.createElement(tag),
- toFocus;
- if (typeof isEditable !== "undefined" && isEditable === false) {
- newEl.contentEditable = false;
- }
- if (newEl.innerHTML.length == 0) {
- newEl.innerHTML = ' ';
- }
- if (afterElement && afterElement.nextSibling) {
- afterElement.parentNode.insertBefore(newEl, afterElement.nextSibling);
- toFocus = afterElement.nextSibling;
- } else {
- this.settings.element.appendChild(newEl);
- toFocus = this.html.lastChild();
- }
- if (shouldFocus) {
- this.cache.focusedElement = toFocus;
- this.cursor.set(0, toFocus);
- }
- return newEl;
- }
- return null;
- },
- baseAtCaret: function () {
- if (!this.medium.isActive()) return null;
- var sel = w.getSelection ? w.getSelection() : document.selection;
- if (sel.rangeCount) {
- var selRange = sel.getRangeAt(0),
- container = selRange.endContainer;
- switch (container.nodeType) {
- case 3:
- if (container.data && container.data.length != selRange.endOffset) return false;
- break;
- }
- return container;
- }
- return null;
- },
- atCaret: function () {
- var container = this.baseAtCaret() || {},
- el = this.element;
- if (container === false) return null;
- while (container && container.parentNode !== el) {
- container = container.parentNode;
- }
- if (container && container.nodeType == 1) {
- return container;
- }
- return null;
- }
- };
- Medium.Action = function () {
- };
- Medium.Action.prototype = {
- setBridge: function (bridge) {
- for (var i in bridge) {
- this[i] = bridge[i];
- }
- },
- listen: function () {
- var el = this.element,
- intercept = this.intercept;
- this.utils
- .addEvent(el, 'keyup', intercept.up)
- .addEvent(el, 'keydown', intercept.down)
- .addEvent(el, 'focus', intercept.focus)
- .addEvent(el, 'blur', intercept.blur)
- .addEvent(el, 'paste', this.settings.pasteEventHandler);
- },
- preserveElementFocus: function () {
- // Fetch node that has focus
- var anchorNode = w.getSelection ? w.getSelection().anchorNode : d.activeElement;
- if (anchorNode) {
- var cache = this.medium.cache,
- s = this.settings,
- cur = anchorNode.parentNode,
- children = s.element.children,
- diff = cur !== cache.focusedElement,
- elementIndex = 0,
- i;
- // anchorNode is our target if element is empty
- if (cur === s.element) {
- cur = anchorNode;
- }
- // Find our child index
- for (i = 0; i < children.length; i++) {
- if (cur === children[i]) {
- elementIndex = i;
- break;
- }
- }
- // Focused element is different
- if (diff) {
- cache.focusedElement = cur;
- cache.focusedElementIndex = elementIndex;
- }
- }
- }
- };
- Medium.Cache = function () {
- this.initialized = false;
- this.cmd = false;
- this.focusedElement = null
- };
- Medium.Cache.prototype = {
- setBridge: function (bridge) {
- for (var i in bridge) {
- this[i] = bridge[i];
- }
- }
- };
- //Modes;
- Medium.inlineMode = 'inline';
- Medium.partialMode = 'partial';
- Medium.richMode = 'rich';
- Medium.Messages = {
- pastHere: 'Paste Here'
- };
- return Medium;
- }());
- if (typeof define === 'function' && define['amd']) {
- define(function () { return Medium; });
- } else if (typeof module !== 'undefined' && module.exports) {
- module.exports = Medium;
- } else if (typeof this !== 'undefined') {
- this.Medium = Medium;
- }
- }).call(this, window, document);
|