From b3bf7bf5634f1eccc302afaa0996ce277b9ec28d Mon Sep 17 00:00:00 2001 From: Matthew Borden Date: Fri, 11 Jul 2014 20:14:18 +1000 Subject: [PATCH] Removed Local Server --- serve.js | 13 - source/Constants.js | 46 - source/Editor.js | 2352 ----------------------------------------- source/Node.js | 460 -------- source/Range.js | 511 --------- source/Squire-UI.css | 148 --- source/Squire-UI.html | 67 -- source/Squire-UI.js | 167 --- source/TreeWalker.js | 91 -- source/document.html | 57 - source/ie8dom.js | 157 --- source/ie8range.js | 487 --------- source/ie8types.js | 63 -- source/intro.js | 5 - source/outro.js | 13 - 15 files changed, 4637 deletions(-) delete mode 100644 serve.js delete mode 100644 source/Constants.js delete mode 100644 source/Editor.js delete mode 100644 source/Node.js delete mode 100644 source/Range.js delete mode 100644 source/Squire-UI.css delete mode 100644 source/Squire-UI.html delete mode 100644 source/Squire-UI.js delete mode 100644 source/TreeWalker.js delete mode 100644 source/document.html delete mode 100644 source/ie8dom.js delete mode 100644 source/ie8range.js delete mode 100644 source/ie8types.js delete mode 100644 source/intro.js delete mode 100644 source/outro.js diff --git a/serve.js b/serve.js deleted file mode 100644 index 73c914a..0000000 --- a/serve.js +++ /dev/null @@ -1,13 +0,0 @@ -var static = require('node-static'); -var sys = require('sys'); -var exec = require('child_process').exec; - -var file = new static.Server('./'); - -function puts(error, stdout, stderr) { sys.puts(stdout) }; - -require('http').createServer(function (request, response) { - request.addListener('end', function () { - file.serve(request, response); - }).resume(); -}).listen(8080); \ No newline at end of file diff --git a/source/Constants.js b/source/Constants.js deleted file mode 100644 index 7bb1f24..0000000 --- a/source/Constants.js +++ /dev/null @@ -1,46 +0,0 @@ -/*global doc, navigator */ -/*jshint strict:false */ - -var DOCUMENT_POSITION_PRECEDING = 2; // Node.DOCUMENT_POSITION_PRECEDING -var ELEMENT_NODE = 1; // Node.ELEMENT_NODE; -var TEXT_NODE = 3; // Node.TEXT_NODE; -var SHOW_ELEMENT = 1; // NodeFilter.SHOW_ELEMENT; -var SHOW_TEXT = 4; // NodeFilter.SHOW_TEXT; -var FILTER_ACCEPT = 1; // NodeFilter.FILTER_ACCEPT; -var FILTER_SKIP = 3; // NodeFilter.FILTER_SKIP; - -var START_TO_START = 0; // Range.START_TO_START -var START_TO_END = 1; // Range.START_TO_END -var END_TO_END = 2; // Range.END_TO_END -var END_TO_START = 3; // Range.END_TO_START - -var win = doc.defaultView; - -var ua = navigator.userAgent; - -var isIOS = /iP(?:ad|hone|od)/.test( ua ); -var isMac = /Mac OS X/.test( ua ); - -var isGecko = /Gecko\//.test( ua ); -var isIE8or9or10 = /Trident\/[456]\./.test( ua ); -var isIE8 = ( win.ie === 8 ); -var isOpera = !!win.opera; -var isWebKit = /WebKit\//.test( ua ); - -var ctrlKey = isMac ? 'meta-' : 'ctrl-'; - -var useTextFixer = isIE8or9or10 || isOpera; -var cantFocusEmptyTextNodes = isIE8or9or10 || isWebKit; -var losesSelectionOnBlur = isIE8or9or10; -var hasBuggySplit = ( function () { - var div = doc.createElement( 'DIV' ), - text = doc.createTextNode( '12' ); - div.appendChild( text ); - text.splitText( 2 ); - return div.childNodes.length !== 2; -}() ); - -// Use [^ \t\r\n] instead of \S so that nbsp does not count as white-space -var notWS = /[^ \t\r\n]/; - -var indexOf = Array.prototype.indexOf; diff --git a/source/Editor.js b/source/Editor.js deleted file mode 100644 index c0a54d9..0000000 --- a/source/Editor.js +++ /dev/null @@ -1,2352 +0,0 @@ -/*global - DOCUMENT_POSITION_PRECEDING, - ELEMENT_NODE, - TEXT_NODE, - SHOW_ELEMENT, - SHOW_TEXT, - FILTER_ACCEPT, - FILTER_SKIP, - win, - isIOS, - isMac, - isGecko, - isIE8or9or10, - isIE8, - isOpera, - ctrlKey, - useTextFixer, - cantFocusEmptyTextNodes, - losesSelectionOnBlur, - hasBuggySplit, - notWS, - indexOf, - - TreeWalker, - - hasTagAttributes, - isLeaf, - isInline, - isBlock, - isContainer, - getBlockWalker, - getPreviousBlock, - getNextBlock, - getNearest, - getPath, - getLength, - detach, - replaceWith, - empty, - fixCursor, - split, - mergeInlines, - mergeWithBlock, - mergeContainers, - fixContainer, - createElement, - - forEachTextNodeInRange, - getTextContentInRange, - insertNodeInRange, - extractContentsOfRange, - deleteContentsOfRange, - insertTreeFragmentIntoRange, - isNodeContainedInRange, - moveRangeBoundariesDownTree, - moveRangeBoundariesUpTree, - getStartBlockOfRange, - getEndBlockOfRange, - rangeDoesStartAtBlockBoundary, - rangeDoesEndAtBlockBoundary, - expandRangeToBlockBoundaries, - - top, - console, - setTimeout -*/ -/*jshint strict:false */ - -function Squire ( doc ) { - var win = doc.defaultView; - var body = doc.body; - this._win = win; - this._doc = doc; - this._body = body; - - this._events = {}; - - this._lastSelection = null; - - // IE loses selection state of iframe on blur, so make sure we - // cache it just before it loses focus. - if ( losesSelectionOnBlur ) { - this.addEventListener( 'beforedeactivate', this.getSelection ); - } - - this._hasZWS = false; - - this._lastAnchorNode = null; - this._lastFocusNode = null; - this._path = ''; - - this.addEventListener( 'keyup', this._updatePathOnEvent ); - this.addEventListener( 'mouseup', this._updatePathOnEvent ); - - win.addEventListener( 'focus', this, false ); - win.addEventListener( 'blur', this, false ); - - this._undoIndex = -1; - this._undoStack = []; - this._undoStackLength = 0; - this._isInUndoState = false; - - this.defaultBlockProperties = undefined; - - this.addEventListener( 'keyup', this._docWasChanged ); - - // IE sometimes fires the beforepaste event twice; make sure it is not run - // again before our after paste function is called. - this._awaitingPaste = false; - this.addEventListener( isIE8or9or10 ? 'beforecut' : 'cut', this._onCut ); - this.addEventListener( isIE8or9or10 ? 'beforepaste' : 'paste', this._onPaste ); - - if ( isIE8 ) { - this.addEventListener( 'keyup', this._ieSelAllClean ); - } - // Opera does not fire keydown repeatedly. - this.addEventListener( isOpera ? 'keypress' : 'keydown', this._onKey ); - - // Fix IE8/9's buggy implementation of Text#splitText. - // If the split is at the end of the node, it doesn't insert the newly split - // node into the document, and sets its value to undefined rather than ''. - // And even if the split is not at the end, the original node is removed - // from the document and replaced by another, rather than just having its - // data shortened. - if ( hasBuggySplit ) { - win.Text.prototype.splitText = function ( offset ) { - var afterSplit = this.ownerDocument.createTextNode( - this.data.slice( offset ) ), - next = this.nextSibling, - parent = this.parentNode, - toDelete = this.length - offset; - if ( next ) { - parent.insertBefore( afterSplit, next ); - } else { - parent.appendChild( afterSplit ); - } - if ( toDelete ) { - this.deleteData( offset, toDelete ); - } - return afterSplit; - }; - } - - body.setAttribute( 'contenteditable', 'true' ); - this.setHTML( '' ); - - // Remove Firefox's built-in controls - try { - doc.execCommand( 'enableObjectResizing', false, 'false' ); - doc.execCommand( 'enableInlineTableEditing', false, 'false' ); - } catch ( error ) {} -} - -var proto = Squire.prototype; - -proto.createElement = function ( tag, props, children ) { - return createElement( this._doc, tag, props, children ); -}; - -proto.createDefaultBlock = function ( children ) { - return fixCursor( - this.createElement( 'DIV', this.defaultBlockProperties, children ) - ); -}; - -proto.didError = function ( error ) { - console.log( error ); -}; - -proto.getDocument = function () { - return this._doc; -}; - -// --- Events --- - -// Subscribing to these events won't automatically add a listener to the -// document node, since these events are fired in a custom manner by the -// editor code. -var customEvents = { - focus: 1, blur: 1, - pathChange: 1, select: 1, input: 1, undoStateChange: 1 -}; - -proto.fireEvent = function ( type, event ) { - var handlers = this._events[ type ], - i, l, obj; - if ( handlers ) { - if ( !event ) { - event = {}; - } - if ( event.type !== type ) { - event.type = type; - } - // Clone handlers array, so any handlers added/removed do not affect it. - handlers = handlers.slice(); - for ( i = 0, l = handlers.length; i < l; i += 1 ) { - obj = handlers[i]; - try { - if ( obj.handleEvent ) { - obj.handleEvent( event ); - } else { - obj.call( this, event ); - } - } catch ( error ) { - error.details = 'Squire: fireEvent error. Event type: ' + type; - this.didError( error ); - } - } - } - return this; -}; - -proto.handleEvent = function ( event ) { - this.fireEvent( event.type, event ); -}; - -proto.addEventListener = function ( type, fn ) { - var handlers = this._events[ type ]; - if ( !fn ) { - this.didError({ - name: 'Squire: addEventListener with null or undefined fn', - message: 'Event type: ' + type - }); - return this; - } - if ( !handlers ) { - handlers = this._events[ type ] = []; - if ( !customEvents[ type ] ) { - this._doc.addEventListener( type, this, false ); - } - } - handlers.push( fn ); - return this; -}; - -proto.removeEventListener = function ( type, fn ) { - var handlers = this._events[ type ], - l; - if ( handlers ) { - l = handlers.length; - while ( l-- ) { - if ( handlers[l] === fn ) { - handlers.splice( l, 1 ); - } - } - if ( !handlers.length ) { - delete this._events[ type ]; - if ( !customEvents[ type ] ) { - this._doc.removeEventListener( type, this, false ); - } - } - } - return this; -}; - -// --- Selection and Path --- - -proto._createRange = - function ( range, startOffset, endContainer, endOffset ) { - if ( range instanceof this._win.Range ) { - return range.cloneRange(); - } - var domRange = this._doc.createRange(); - domRange.setStart( range, startOffset ); - if ( endContainer ) { - domRange.setEnd( endContainer, endOffset ); - } else { - domRange.setEnd( range, startOffset ); - } - return domRange; -}; - -proto.setSelection = function ( range ) { - if ( range ) { - // iOS bug: if you don't focus the iframe before setting the - // selection, you can end up in a state where you type but the input - // doesn't get directed into the contenteditable area but is instead - // lost in a black hole. Very strange. - if ( isIOS ) { - this._win.focus(); - } - var sel = this._win.getSelection(); - sel.removeAllRanges(); - sel.addRange( range ); - } - return this; -}; - -proto.getSelection = function () { - var sel = this._win.getSelection(), - selection, startContainer, endContainer; - if ( sel.rangeCount ) { - selection = sel.getRangeAt( 0 ).cloneRange(); - startContainer = selection.startContainer; - endContainer = selection.endContainer; - // FF sometimes throws an error reading the isLeaf property. Let's - // catch and log it to see if we can find what's going on. - try { - // FF can return the selection as being inside an . WTF? - if ( startContainer && isLeaf( startContainer ) ) { - selection.setStartBefore( startContainer ); - } - if ( endContainer && isLeaf( endContainer ) ) { - selection.setEndBefore( endContainer ); - } - } catch ( error ) { - this.didError({ - name: 'Squire#getSelection error', - message: 'Starts: ' + startContainer.nodeName + - '\nEnds: ' + endContainer.nodeName - }); - } - this._lastSelection = selection; - } else { - selection = this._lastSelection; - } - if ( !selection ) { - selection = this._createRange( this._body.firstChild, 0 ); - } - return selection; -}; - -proto.getSelectedText = function () { - return getTextContentInRange( this.getSelection() ); -}; - -proto.getPath = function () { - return this._path; -}; - -// --- Workaround for browsers that can't focus empty text nodes --- - -// WebKit bug: https://bugs.webkit.org/show_bug.cgi?id=15256 - -proto._didAddZWS = function () { - this._hasZWS = true; -}; -proto._removeZWS = function () { - if ( !this._hasZWS ) { - return; - } - var walker = new TreeWalker( this._body, SHOW_TEXT, function () { - return FILTER_ACCEPT; - }, false ), - node, index; - while ( node = walker.nextNode() ) { - while ( ( index = node.data.indexOf( '\u200B' ) ) > -1 ) { - node.deleteData( index, 1 ); - } - } - this._hasZWS = false; -}; - -// --- Path change events --- - -proto._updatePath = function ( range, force ) { - var anchor = range.startContainer, - focus = range.endContainer, - newPath; - if ( force || anchor !== this._lastAnchorNode || - focus !== this._lastFocusNode ) { - this._lastAnchorNode = anchor; - this._lastFocusNode = focus; - newPath = ( anchor && focus ) ? ( anchor === focus ) ? - getPath( focus ) : '(selection)' : ''; - if ( this._path !== newPath ) { - this._path = newPath; - this.fireEvent( 'pathChange', { path: newPath } ); - } - } - if ( anchor !== focus ) { - this.fireEvent( 'select' ); - } -}; - -proto._updatePathOnEvent = function () { - this._updatePath( this.getSelection() ); -}; - -// --- Focus --- - -proto.focus = function () { - // FF seems to need the body to be focussed - // (at least on first load). - if ( isGecko ) { - this._body.focus(); - } - this._win.focus(); - return this; -}; - -proto.blur = function () { - // IE will remove the whole browser window from focus if you call - // win.blur() or body.blur(), so instead we call top.focus() to focus - // the top frame, thus blurring this frame. This works in everything - // except FF, so we need to call body.blur() in that as well. - if ( isGecko ) { - this._body.blur(); - } - top.focus(); - return this; -}; - -// --- Bookmarking --- - -var startSelectionId = 'squire-selection-start'; -var endSelectionId = 'squire-selection-end'; - -proto._saveRangeToBookmark = function ( range ) { - var startNode = this.createElement( 'INPUT', { - id: startSelectionId, - type: 'hidden' - }), - endNode = this.createElement( 'INPUT', { - id: endSelectionId, - type: 'hidden' - }), - temp; - - insertNodeInRange( range, startNode ); - range.collapse( false ); - insertNodeInRange( range, endNode ); - - // In a collapsed range, the start is sometimes inserted after the end! - if ( startNode.compareDocumentPosition( endNode ) & - DOCUMENT_POSITION_PRECEDING ) { - startNode.id = endSelectionId; - endNode.id = startSelectionId; - temp = startNode; - startNode = endNode; - endNode = temp; - } - - range.setStartAfter( startNode ); - range.setEndBefore( endNode ); -}; - -proto._getRangeAndRemoveBookmark = function ( range ) { - var doc = this._doc, - start = doc.getElementById( startSelectionId ), - end = doc.getElementById( endSelectionId ); - - if ( start && end ) { - var startContainer = start.parentNode, - endContainer = end.parentNode, - collapsed; - - var _range = { - startContainer: startContainer, - endContainer: endContainer, - startOffset: indexOf.call( startContainer.childNodes, start ), - endOffset: indexOf.call( endContainer.childNodes, end ) - }; - - if ( startContainer === endContainer ) { - _range.endOffset -= 1; - } - - detach( start ); - detach( end ); - - // Merge any text nodes we split - mergeInlines( startContainer, _range ); - if ( startContainer !== endContainer ) { - mergeInlines( endContainer, _range ); - } - - if ( !range ) { - range = doc.createRange(); - } - range.setStart( _range.startContainer, _range.startOffset ); - range.setEnd( _range.endContainer, _range.endOffset ); - collapsed = range.collapsed; - - moveRangeBoundariesDownTree( range ); - if ( collapsed ) { - range.collapse( true ); - } - } - return range || null; -}; - -// --- Undo --- - -proto._docWasChanged = function ( event ) { - var code = event && event.keyCode; - // Presume document was changed if: - // 1. A modifier key (other than shift) wasn't held down - // 2. The key pressed is not in range 16<=x<=20 (control keys) - // 3. The key pressed is not in range 33<=x<=45 (navigation keys) - if ( !event || ( !event.ctrlKey && !event.metaKey && !event.altKey && - ( code < 16 || code > 20 ) && - ( code < 33 || code > 45 ) ) ) { - if ( this._isInUndoState ) { - this._isInUndoState = false; - this.fireEvent( 'undoStateChange', { - canUndo: true, - canRedo: false - }); - } - this.fireEvent( 'input' ); - } -}; - -// Leaves bookmark -proto._recordUndoState = function ( range ) { - // Don't record if we're already in an undo state - if ( !this._isInUndoState ) { - // Advance pointer to new position - var undoIndex = this._undoIndex += 1, - undoStack = this._undoStack; - - // Truncate stack if longer (i.e. if has been previously undone) - if ( undoIndex < this._undoStackLength) { - undoStack.length = this._undoStackLength = undoIndex; - } - - // Write out data - if ( range ) { - this._saveRangeToBookmark( range ); - } - undoStack[ undoIndex ] = this._getHTML(); - this._undoStackLength += 1; - this._isInUndoState = true; - } -}; - -proto.undo = function () { - // Sanity check: must not be at beginning of the history stack - if ( this._undoIndex !== 0 || !this._isInUndoState ) { - // Make sure any changes since last checkpoint are saved. - this._recordUndoState( this.getSelection() ); - - this._undoIndex -= 1; - this._setHTML( this._undoStack[ this._undoIndex ] ); - var range = this._getRangeAndRemoveBookmark(); - if ( range ) { - this.setSelection( range ); - } - this._isInUndoState = true; - this.fireEvent( 'undoStateChange', { - canUndo: this._undoIndex !== 0, - canRedo: true - }); - this.fireEvent( 'input' ); - } - return this; -}; - -proto.redo = function () { - // Sanity check: must not be at end of stack and must be in an undo - // state. - var undoIndex = this._undoIndex, - undoStackLength = this._undoStackLength; - if ( undoIndex + 1 < undoStackLength && this._isInUndoState ) { - this._undoIndex += 1; - this._setHTML( this._undoStack[ this._undoIndex ] ); - var range = this._getRangeAndRemoveBookmark(); - if ( range ) { - this.setSelection( range ); - } - this.fireEvent( 'undoStateChange', { - canUndo: true, - canRedo: undoIndex + 2 < undoStackLength - }); - this.fireEvent( 'input' ); - } - return this; -}; - -// --- Inline formatting --- - -// Looks for matching tag and attributes, so won't work -// if instead of etc. -proto.hasFormat = function ( tag, attributes, range ) { - // 1. Normalise the arguments and get selection - tag = tag.toUpperCase(); - if ( !attributes ) { attributes = {}; } - if ( !range && !( range = this.getSelection() ) ) { - return false; - } - - // If the common ancestor is inside the tag we require, we definitely - // have the format. - var root = range.commonAncestorContainer, - walker, node; - if ( getNearest( root, tag, attributes ) ) { - return true; - } - - // If common ancestor is a text node and doesn't have the format, we - // definitely don't have it. - if ( root.nodeType === TEXT_NODE ) { - return false; - } - - // Otherwise, check each text node at least partially contained within - // the selection and make sure all of them have the format we want. - walker = new TreeWalker( root, SHOW_TEXT, function ( node ) { - return isNodeContainedInRange( range, node, true ) ? - FILTER_ACCEPT : FILTER_SKIP; - }, false ); - - var seenNode = false; - while ( node = walker.nextNode() ) { - if ( !getNearest( node, tag, attributes ) ) { - return false; - } - seenNode = true; - } - - return seenNode; -}; - -proto._addFormat = function ( tag, attributes, range ) { - // If the range is collapsed we simply insert the node by wrapping - // it round the range and focus it. - var el, walker, startContainer, endContainer, startOffset, endOffset, - textnode, needsFormat; - - if ( range.collapsed ) { - el = fixCursor( this.createElement( tag, attributes ) ); - insertNodeInRange( range, el ); - range.setStart( el.firstChild, el.firstChild.length ); - range.collapse( true ); - } - // Otherwise we find all the textnodes in the range (splitting - // partially selected nodes) and if they're not already formatted - // correctly we wrap them in the appropriate tag. - else { - // We don't want to apply formatting twice so we check each text - // node to see if it has an ancestor with the formatting already. - // Create an iterator to walk over all the text nodes under this - // ancestor which are in the range and not already formatted - // correctly. - walker = new TreeWalker( - range.commonAncestorContainer, - SHOW_TEXT, - function ( node ) { - return isNodeContainedInRange( range, node, true ) ? - FILTER_ACCEPT : FILTER_SKIP; - }, - false - ); - - // Start at the beginning node of the range and iterate through - // all the nodes in the range that need formatting. - startOffset = 0; - endOffset = 0; - textnode = walker.currentNode = range.startContainer; - - if ( textnode.nodeType !== TEXT_NODE ) { - textnode = walker.nextNode(); - } - - do { - needsFormat = !getNearest( textnode, tag, attributes ); - if ( textnode === range.endContainer ) { - if ( needsFormat && textnode.length > range.endOffset ) { - textnode.splitText( range.endOffset ); - } else { - endOffset = range.endOffset; - } - } - if ( textnode === range.startContainer ) { - if ( needsFormat && range.startOffset ) { - textnode = textnode.splitText( range.startOffset ); - } else { - startOffset = range.startOffset; - } - } - if ( needsFormat ) { - el = this.createElement( tag, attributes ); - replaceWith( textnode, el ); - el.appendChild( textnode ); - endOffset = textnode.length; - } - endContainer = textnode; - if ( !startContainer ) { startContainer = endContainer; } - } while ( textnode = walker.nextNode() ); - - // Now set the selection to as it was before - range = this._createRange( - startContainer, startOffset, endContainer, endOffset ); - } - return range; -}; - -proto._removeFormat = function ( tag, attributes, range, partial ) { - // Add bookmark - this._saveRangeToBookmark( range ); - - // We need a node in the selection to break the surrounding - // formatted text. - var doc = this._doc, - fixer; - if ( range.collapsed ) { - if ( cantFocusEmptyTextNodes ) { - fixer = doc.createTextNode( '\u200B' ); - this._didAddZWS(); - } else { - fixer = doc.createTextNode( '' ); - } - insertNodeInRange( range, fixer ); - } - - // Find block-level ancestor of selection - var root = range.commonAncestorContainer; - while ( isInline( root ) ) { - root = root.parentNode; - } - - // Find text nodes inside formatTags that are not in selection and - // add an extra tag with the same formatting. - var startContainer = range.startContainer, - startOffset = range.startOffset, - endContainer = range.endContainer, - endOffset = range.endOffset, - toWrap = [], - examineNode = function ( node, exemplar ) { - // If the node is completely contained by the range then - // we're going to remove all formatting so ignore it. - if ( isNodeContainedInRange( range, node, false ) ) { - return; - } - - var isText = ( node.nodeType === TEXT_NODE ), - child, next; - - // If not at least partially contained, wrap entire contents - // in a clone of the tag we're removing and we're done. - if ( !isNodeContainedInRange( range, node, true ) ) { - // Ignore bookmarks and empty text nodes - if ( node.nodeName !== 'INPUT' && - ( !isText || node.data ) ) { - toWrap.push([ exemplar, node ]); - } - return; - } - - // Split any partially selected text nodes. - if ( isText ) { - if ( node === endContainer && endOffset !== node.length ) { - toWrap.push([ exemplar, node.splitText( endOffset ) ]); - } - if ( node === startContainer && startOffset ) { - node.splitText( startOffset ); - toWrap.push([ exemplar, node ]); - } - } - // If not a text node, recurse onto all children. - // Beware, the tree may be rewritten with each call - // to examineNode, hence find the next sibling first. - else { - for ( child = node.firstChild; child; child = next ) { - next = child.nextSibling; - examineNode( child, exemplar ); - } - } - }, - formatTags = Array.prototype.filter.call( - root.getElementsByTagName( tag ), function ( el ) { - return isNodeContainedInRange( range, el, true ) && - hasTagAttributes( el, tag, attributes ); - } - ); - - if ( !partial ) { - formatTags.forEach( function ( node ) { - examineNode( node, node ); - }); - } - - // Now wrap unselected nodes in the tag - toWrap.forEach( function ( item ) { - // [ exemplar, node ] tuple - var el = item[0].cloneNode( false ), - node = item[1]; - replaceWith( node, el ); - el.appendChild( node ); - }); - // and remove old formatting tags. - formatTags.forEach( function ( el ) { - replaceWith( el, empty( el ) ); - }); - - // Merge adjacent inlines: - this._getRangeAndRemoveBookmark( range ); - if ( fixer ) { - range.collapse( false ); - } - var _range = { - startContainer: range.startContainer, - startOffset: range.startOffset, - endContainer: range.endContainer, - endOffset: range.endOffset - }; - mergeInlines( root, _range ); - range.setStart( _range.startContainer, _range.startOffset ); - range.setEnd( _range.endContainer, _range.endOffset ); - - return range; -}; - -proto.changeFormat = function ( add, remove, range, partial ) { - // Normalise the arguments and get selection - if ( !range && !( range = this.getSelection() ) ) { - return; - } - - // Save undo checkpoint - this._recordUndoState( range ); - this._getRangeAndRemoveBookmark( range ); - - if ( remove ) { - range = this._removeFormat( remove.tag.toUpperCase(), - remove.attributes || {}, range, partial ); - } - if ( add ) { - range = this._addFormat( add.tag.toUpperCase(), - add.attributes || {}, range ); - } - - this.setSelection( range ); - this._updatePath( range, true ); - - // We're not still in an undo state - this._docWasChanged(); - - return this; -}; - -// --- Block formatting --- - -var tagAfterSplit = { - DIV: 'DIV', - PRE: 'DIV', - H1: 'DIV', - H2: 'DIV', - H3: 'DIV', - H4: 'DIV', - H5: 'DIV', - H6: 'DIV', - P: 'DIV', - DT: 'DD', - DD: 'DT', - LI: 'LI' -}; - -var splitBlock = function ( block, node, offset ) { - var splitTag = tagAfterSplit[ block.nodeName ], - nodeAfterSplit = split( node, offset, block.parentNode ); - - // Make sure the new node is the correct type. - if ( nodeAfterSplit.nodeName !== splitTag ) { - block = createElement( nodeAfterSplit.ownerDocument, splitTag ); - block.className = nodeAfterSplit.dir === 'rtl' ? 'dir-rtl' : ''; - block.dir = nodeAfterSplit.dir; - replaceWith( nodeAfterSplit, block ); - block.appendChild( empty( nodeAfterSplit ) ); - nodeAfterSplit = block; - } - return nodeAfterSplit; -}; - -proto.forEachBlock = function ( fn, mutates, range ) { - if ( !range && !( range = this.getSelection() ) ) { - return this; - } - - // Save undo checkpoint - if ( mutates ) { - this._recordUndoState( range ); - this._getRangeAndRemoveBookmark( range ); - } - - var start = getStartBlockOfRange( range ), - end = getEndBlockOfRange( range ); - if ( start && end ) { - do { - if ( fn( start ) || start === end ) { break; } - } while ( start = getNextBlock( start ) ); - } - - if ( mutates ) { - this.setSelection( range ); - - // Path may have changed - this._updatePath( range, true ); - - // We're not still in an undo state - this._docWasChanged(); - } - return this; -}; - -proto.modifyBlocks = function ( modify, range ) { - if ( !range && !( range = this.getSelection() ) ) { - return this; - } - - // 1. Save undo checkpoint and bookmark selection - if ( this._isInUndoState ) { - this._saveRangeToBookmark( range ); - } else { - this._recordUndoState( range ); - } - - // 2. Expand range to block boundaries - expandRangeToBlockBoundaries( range ); - - // 3. Remove range. - var body = this._body, - frag; - moveRangeBoundariesUpTree( range, body ); - frag = extractContentsOfRange( range, body ); - - // 4. Modify tree of fragment and reinsert. - insertNodeInRange( range, modify.call( this, frag ) ); - - // 5. Merge containers at edges - if ( range.endOffset < range.endContainer.childNodes.length ) { - mergeContainers( range.endContainer.childNodes[ range.endOffset ] ); - } - mergeContainers( range.startContainer.childNodes[ range.startOffset ] ); - - // 6. Restore selection - this._getRangeAndRemoveBookmark( range ); - this.setSelection( range ); - this._updatePath( range, true ); - - // 7. We're not still in an undo state - this._docWasChanged(); - - return this; -}; - -var increaseBlockQuoteLevel = function ( frag ) { - return this.createElement( 'BLOCKQUOTE', [ - frag - ]); -}; - -var decreaseBlockQuoteLevel = function ( frag ) { - var blockquotes = frag.querySelectorAll( 'blockquote' ); - Array.prototype.filter.call( blockquotes, function ( el ) { - return !getNearest( el.parentNode, 'BLOCKQUOTE' ); - }).forEach( function ( el ) { - replaceWith( el, empty( el ) ); - }); - return frag; -}; - -var removeBlockQuote = function (/* frag */) { - return this.createDefaultBlock([ - this.createElement( 'INPUT', { - id: startSelectionId, - type: 'hidden' - }), - this.createElement( 'INPUT', { - id: endSelectionId, - type: 'hidden' - }) - ]); -}; - -var makeList = function ( self, frag, type ) { - var walker = getBlockWalker( frag ), - node, tag, prev, newLi; - - while ( node = walker.nextNode() ) { - tag = node.parentNode.nodeName; - if ( tag !== 'LI' ) { - newLi = self.createElement( 'LI', { - 'class': node.dir === 'rtl' ? 'dir-rtl' : undefined, - dir: node.dir || undefined - }); - // Have we replaced the previous block with a new
    /
      ? - if ( ( prev = node.previousSibling ) && - prev.nodeName === type ) { - prev.appendChild( newLi ); - } - // Otherwise, replace this block with the
        /
          - else { - replaceWith( - node, - self.createElement( type, [ - newLi - ]) - ); - } - newLi.appendChild( node ); - } else { - node = node.parentNode.parentNode; - tag = node.nodeName; - if ( tag !== type && ( /^[OU]L$/.test( tag ) ) ) { - replaceWith( node, - self.createElement( type, [ empty( node ) ] ) - ); - } - } - } -}; - -var makeUnorderedList = function ( frag ) { - makeList( this, frag, 'UL' ); - return frag; -}; - -var makeOrderedList = function ( frag ) { - makeList( this, frag, 'OL' ); - return frag; -}; - -var removeList = function ( frag ) { - var lists = frag.querySelectorAll( 'UL, OL' ), - i, l, ll, list, listFrag, children, child; - for ( i = 0, l = lists.length; i < l; i += 1 ) { - list = lists[i]; - listFrag = empty( list ); - children = listFrag.childNodes; - ll = children.length; - while ( ll-- ) { - child = children[ll]; - replaceWith( child, empty( child ) ); - } - fixContainer( listFrag ); - replaceWith( list, listFrag ); - } - return frag; -}; - -var increaseListLevel = function ( frag ) { - var items = frag.querySelectorAll( 'LI' ), - i, l, item, - type, newParent; - for ( i = 0, l = items.length; i < l; i += 1 ) { - item = items[i]; - if ( !isContainer( item.firstChild ) ) { - // type => 'UL' or 'OL' - type = item.parentNode.nodeName; - newParent = item.previousSibling; - if ( !newParent || !( newParent = newParent.lastChild ) || - newParent.nodeName !== type ) { - replaceWith( - item, - this.createElement( 'LI', [ - newParent = this.createElement( type ) - ]) - ); - } - newParent.appendChild( item ); - } - } - return frag; -}; - -var decreaseListLevel = function ( frag ) { - var items = frag.querySelectorAll( 'LI' ); - Array.prototype.filter.call( items, function ( el ) { - return !isContainer( el.firstChild ); - }).forEach( function ( item ) { - var parent = item.parentNode, - newParent = parent.parentNode, - first = item.firstChild, - node = first, - next; - if ( item.previousSibling ) { - parent = split( parent, item, newParent ); - } - while ( node ) { - next = node.nextSibling; - if ( isContainer( node ) ) { - break; - } - newParent.insertBefore( node, parent ); - node = next; - } - if ( newParent.nodeName === 'LI' && first.previousSibling ) { - split( newParent, first, newParent.parentNode ); - } - while ( item !== frag && !item.childNodes.length ) { - parent = item.parentNode; - parent.removeChild( item ); - item = parent; - } - }, this ); - fixContainer( frag ); - return frag; -}; - -// --- Clean --- - -var linkRegExp = /\b((?:(?:ht|f)tps?:\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,}\/)(?:[^\s()<>]+|\([^\s()<>]+\))+(?:\((?:[^\s()<>]+|(?:\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))|([\w\-.%+]+@(?:[\w\-]+\.)+[A-Z]{2,}\b)/i; - -var addLinks = function ( frag ) { - var doc = frag.ownerDocument, - walker = new TreeWalker( frag, SHOW_TEXT, - function ( node ) { - return getNearest( node, 'A' ) ? FILTER_SKIP : FILTER_ACCEPT; - }, false ), - node, data, parent, match, index, endIndex, child; - while ( node = walker.nextNode() ) { - data = node.data; - parent = node.parentNode; - while ( match = linkRegExp.exec( data ) ) { - index = match.index; - endIndex = index + match[0].length; - if ( index ) { - child = doc.createTextNode( data.slice( 0, index ) ); - parent.insertBefore( child, node ); - } - child = doc.createElement( 'A' ); - child.textContent = data.slice( index, endIndex ); - child.href = match[1] ? - /^(?:ht|f)tps?:/.test( match[1] ) ? - match[1] : - 'http://' + match[1] : - 'mailto:' + match[2]; - parent.insertBefore( child, node ); - node.data = data = data.slice( endIndex ); - } - } -}; - -var allowedBlock = /^(?:A(?:DDRESS|RTICLE|SIDE)|BLOCKQUOTE|CAPTION|D(?:[DLT]|IV)|F(?:IGURE|OOTER)|H[1-6]|HEADER|L(?:ABEL|EGEND|I)|O(?:L|UTPUT)|P(?:RE)?|SECTION|T(?:ABLE|BODY|D|FOOT|H|HEAD|R)|UL)$/; - -var fontSizes = { - 1: 10, - 2: 13, - 3: 16, - 4: 18, - 5: 24, - 6: 32, - 7: 48 -}; - -var spanToSemantic = { - backgroundColor: { - regexp: notWS, - replace: function ( doc, colour ) { - return createElement( doc, 'SPAN', { - 'class': 'highlight', - style: 'background-color: ' + colour - }); - } - }, - color: { - regexp: notWS, - replace: function ( doc, colour ) { - return createElement( doc, 'SPAN', { - 'class': 'colour', - style: 'color:' + colour - }); - } - }, - fontWeight: { - regexp: /^bold/i, - replace: function ( doc ) { - return createElement( doc, 'B' ); - } - }, - fontStyle: { - regexp: /^italic/i, - replace: function ( doc ) { - return createElement( doc, 'I' ); - } - }, - fontFamily: { - regexp: notWS, - replace: function ( doc, family ) { - return createElement( doc, 'SPAN', { - 'class': 'font', - style: 'font-family:' + family - }); - } - }, - fontSize: { - regexp: notWS, - replace: function ( doc, size ) { - return createElement( doc, 'SPAN', { - 'class': 'size', - style: 'font-size:' + size - }); - } - } -}; - -var replaceWithTag = function ( tag ) { - return function ( node, parent ) { - var el = createElement( node.ownerDocument, tag ); - parent.replaceChild( el, node ); - el.appendChild( empty( node ) ); - return el; - }; -}; - -var stylesRewriters = { - SPAN: function ( span, parent ) { - var style = span.style, - doc = span.ownerDocument, - attr, converter, css, newTreeBottom, newTreeTop, el; - - for ( attr in spanToSemantic ) { - converter = spanToSemantic[ attr ]; - css = style[ attr ]; - if ( css && converter.regexp.test( css ) ) { - el = converter.replace( doc, css ); - if ( newTreeBottom ) { - newTreeBottom.appendChild( el ); - } - newTreeBottom = el; - if ( !newTreeTop ) { - newTreeTop = el; - } - } - } - - if ( newTreeTop ) { - newTreeBottom.appendChild( empty( span ) ); - parent.replaceChild( newTreeTop, span ); - } - - return newTreeBottom || span; - }, - STRONG: replaceWithTag( 'B' ), - EM: replaceWithTag( 'I' ), - STRIKE: replaceWithTag( 'S' ), - FONT: function ( node, parent ) { - var face = node.face, - size = node.size, - colour = node.color, - doc = node.ownerDocument, - fontSpan, sizeSpan, colourSpan, - newTreeBottom, newTreeTop; - if ( face ) { - fontSpan = createElement( doc, 'SPAN', { - 'class': 'font', - style: 'font-family:' + face - }); - newTreeTop = fontSpan; - newTreeBottom = fontSpan; - } - if ( size ) { - sizeSpan = createElement( doc, 'SPAN', { - 'class': 'size', - style: 'font-size:' + fontSizes[ size ] + 'px' - }); - if ( !newTreeTop ) { - newTreeTop = sizeSpan; - } - if ( newTreeBottom ) { - newTreeBottom.appendChild( sizeSpan ); - } - newTreeBottom = sizeSpan; - } - if ( colour && /^#?([\dA-F]{3}){1,2}$/i.test( colour ) ) { - if ( colour.charAt( 0 ) !== '#' ) { - colour = '#' + colour; - } - colourSpan = createElement( doc, 'SPAN', { - 'class': 'colour', - style: 'color:' + colour - }); - if ( !newTreeTop ) { - newTreeTop = colourSpan; - } - if ( newTreeBottom ) { - newTreeBottom.appendChild( colourSpan ); - } - newTreeBottom = colourSpan; - } - if ( !newTreeTop ) { - newTreeTop = newTreeBottom = createElement( doc, 'SPAN' ); - } - parent.replaceChild( newTreeTop, node ); - newTreeBottom.appendChild( empty( node ) ); - return newTreeBottom; - }, - TT: function ( node, parent ) { - var el = createElement( node.ownerDocument, 'SPAN', { - 'class': 'font', - style: 'font-family:menlo,consolas,"courier new",monospace' - }); - parent.replaceChild( el, node ); - el.appendChild( empty( node ) ); - return el; - } -}; - -var removeEmptyInlines = function ( root ) { - var children = root.childNodes, - l = children.length, - child; - while ( l-- ) { - child = children[l]; - if ( child.nodeType === ELEMENT_NODE && child.nodeName !== 'IMG' ) { - removeEmptyInlines( child ); - if ( isInline( child ) && !child.firstChild ) { - root.removeChild( child ); - } - } - } -}; - -/* - Two purposes: - - 1. Remove nodes we don't want, such as weird tags, comment nodes - and whitespace nodes. - 2. Convert inline tags into our preferred format. -*/ -var cleanTree = function ( node, allowStyles ) { - var children = node.childNodes, - i, l, child, nodeName, nodeType, rewriter, childLength, - data, j, ll; - for ( i = 0, l = children.length; i < l; i += 1 ) { - child = children[i]; - nodeName = child.nodeName; - nodeType = child.nodeType; - rewriter = stylesRewriters[ nodeName ]; - if ( nodeType === ELEMENT_NODE ) { - childLength = child.childNodes.length; - if ( rewriter ) { - child = rewriter( child, node ); - } else if ( !allowedBlock.test( nodeName ) && - !isInline( child ) ) { - i -= 1; - l += childLength - 1; - node.replaceChild( empty( child ), child ); - continue; - } else if ( !allowStyles && child.style.cssText ) { - child.removeAttribute( 'style' ); - } - if ( childLength ) { - cleanTree( child, allowStyles ); - } - } else { - if ( nodeType === TEXT_NODE ) { - data = child.data; - // Use \S instead of notWS, because we want to remove nodes - // which are just nbsp, in order to cleanup
          nbsp
          - // construct. - if ( /\S/.test( data ) ) { - // If the parent node is inline, don't trim this node as - // it probably isn't at the end of the block. - if ( isInline( node ) ) { - continue; - } - j = 0; - ll = data.length; - if ( !i || !isInline( children[ i - 1 ] ) ) { - while ( j < ll && !notWS.test( data.charAt( j ) ) ) { - j += 1; - } - if ( j ) { - child.data = data = data.slice( j ); - ll -= j; - } - } - if ( i + 1 === l || !isInline( children[ i + 1 ] ) ) { - j = ll; - while ( j > 0 && !notWS.test( data.charAt( j - 1 ) ) ) { - j -= 1; - } - if ( j < ll ) { - child.data = data.slice( 0, j ); - } - } - continue; - } - } - node.removeChild( child ); - i -= 1; - l -= 1; - } - } - return node; -}; - -var notWSTextNode = function ( node ) { - return ( node.nodeType === ELEMENT_NODE ? - node.nodeName === 'BR' : - notWS.test( node.data ) ) ? - FILTER_ACCEPT : FILTER_SKIP; -}; -var isLineBreak = function ( br ) { - var block = br.parentNode, - walker; - while ( isInline( block ) ) { - block = block.parentNode; - } - walker = new TreeWalker( - block, SHOW_ELEMENT|SHOW_TEXT, notWSTextNode ); - walker.currentNode = br; - return !!walker.nextNode(); -}; - -//
          elements are treated specially, and differently depending on the -// browser, when in rich text editor mode. When adding HTML from external -// sources, we must remove them, replacing the ones that actually affect -// line breaks with a split of the block element containing it (and wrapping -// any not inside a block). Browsers that want
          elements at the end of -// each block will then have them added back in a later fixCursor method -// call. -var cleanupBRs = function ( root ) { - var brs = root.querySelectorAll( 'BR' ), - brBreaksLine = [], - l = brs.length, - i, br, block; - - // Must calculate whether the
          breaks a line first, because if we - // have two
          s next to each other, after the first one is converted - // to a block split, the second will be at the end of a block and - // therefore seem to not be a line break. But in its original context it - // was, so we should also convert it to a block split. - for ( i = 0; i < l; i += 1 ) { - brBreaksLine[i] = isLineBreak( brs[i] ); - } - while ( l-- ) { - br = brs[l]; - // Cleanup may have removed it - block = br.parentNode; - if ( !block ) { continue; } - while ( isInline( block ) ) { - block = block.parentNode; - } - // If this is not inside a block, replace it by wrapping - // inlines in DIV. - if ( !isBlock( block ) || !tagAfterSplit[ block.nodeName ] ) { - fixContainer( block ); - } - // If in a block we can split, split it instead, but only if there - // is actual text content in the block. Otherwise, the
          is a - // placeholder to stop the block from collapsing, so we must leave - // it. - else { - if ( brBreaksLine[l] ) { - splitBlock( block, br.parentNode, br ); - } - detach( br ); - } - } -}; - -proto._ensureBottomLine = function () { - var body = this._body, - div = body.lastChild; - if ( !div || div.nodeName !== 'DIV' || !isBlock( div ) ) { - body.appendChild( this.createDefaultBlock() ); - } -}; - -// --- Cut and Paste --- - -proto._onCut = function () { - // Save undo checkpoint - var range = this.getSelection(); - var self = this; - this._recordUndoState( range ); - this._getRangeAndRemoveBookmark( range ); - this.setSelection( range ); - setTimeout( function () { - try { - // If all content removed, ensure div at start of body. - self._ensureBottomLine(); - } catch ( error ) { - self.didError( error ); - } - }, 0 ); -}; - -proto._onPaste = function ( event ) { - if ( this._awaitingPaste ) { return; } - - // Treat image paste as a drop of an image file. - var clipboardData = event.clipboardData, - items = clipboardData && clipboardData.items, - fireDrop = false, - hasImage = false, - l, type; - if ( items ) { - l = items.length; - while ( l-- ) { - type = items[l].type; - if ( type === 'text/html' ) { - hasImage = false; - break; - } - if ( /^image\/.*/.test( type ) ) { - hasImage = true; - } - } - if ( hasImage ) { - event.preventDefault(); - this.fireEvent( 'dragover', { - dataTransfer: clipboardData, - /*jshint loopfunc: true */ - preventDefault: function () { - fireDrop = true; - } - /*jshint loopfunc: false */ - }); - if ( fireDrop ) { - this.fireEvent( 'drop', { - dataTransfer: clipboardData - }); - } - return; - } - } - - this._awaitingPaste = true; - - var self = this, - body = this._body, - range = this.getSelection(), - startContainer = range.startContainer, - startOffset = range.startOffset, - endContainer = range.endContainer, - endOffset = range.endOffset; - - var pasteArea = this.createElement( 'DIV', { - style: 'position: absolute; overflow: hidden; top:' + - (body.scrollTop + 30) + 'px; left: 0; width: 1px; height: 1px;' - }); - body.appendChild( pasteArea ); - range.selectNodeContents( pasteArea ); - this.setSelection( range ); - - // A setTimeout of 0 means this is added to the back of the - // single javascript thread, so it will be executed after the - // paste event. - setTimeout( function () { - try { - // Get the pasted content and clean - var frag = empty( detach( pasteArea ) ), - first = frag.firstChild, - range = self._createRange( - startContainer, startOffset, endContainer, endOffset ); - - // Was anything actually pasted? - if ( first ) { - // Safari and IE like putting extra divs around things. - if ( first === frag.lastChild && - first.nodeName === 'DIV' ) { - frag.replaceChild( empty( first ), first ); - } - - frag.normalize(); - addLinks( frag ); - cleanTree( frag, false ); - cleanupBRs( frag ); - removeEmptyInlines( frag ); - - var node = frag, - doPaste = true; - while ( node = getNextBlock( node ) ) { - fixCursor( node ); - } - - self.fireEvent( 'willPaste', { - fragment: frag, - preventDefault: function () { - doPaste = false; - } - }); - - // Insert pasted data - if ( doPaste ) { - insertTreeFragmentIntoRange( range, frag ); - self._docWasChanged(); - range.collapse( false ); - self._ensureBottomLine(); - } - } - - self.setSelection( range ); - self._updatePath( range, true ); - - self._awaitingPaste = false; - } catch ( error ) { - self.didError( error ); - } - }, 0 ); -}; - -// --- Keyboard interaction --- - -var keys = { - 8: 'backspace', - 9: 'tab', - 13: 'enter', - 32: 'space', - 37: 'left', - 39: 'right', - 46: 'delete', - 219: '[', - 221: ']' -}; - -var mapKeyTo = function ( method ) { - return function ( self, event ) { - event.preventDefault(); - self[ method ](); - }; -}; - -var mapKeyToFormat = function ( tag, remove ) { - remove = remove || null; - return function ( self, event ) { - event.preventDefault(); - var range = self.getSelection(); - if ( self.hasFormat( tag, null, range ) ) { - self.changeFormat( null, { tag: tag }, range ); - } else { - self.changeFormat( { tag: tag }, remove, range ); - } - }; -}; - -// If you delete the content inside a span with a font styling, Webkit will -// replace it with a tag (!). If you delete all the text inside a -// link in Opera, it won't delete the link. Let's make things consistent. If -// you delete all text inside an inline tag, remove the inline tag. -var afterDelete = function ( self ) { - try { - var range = self.getSelection(), - node = range.startContainer, - parent; - if ( node.nodeType === TEXT_NODE ) { - node = node.parentNode; - } - // If focussed in empty inline element - if ( isInline( node ) && !node.textContent ) { - do { - parent = node.parentNode; - } while ( isInline( parent ) && - !parent.textContent && ( node = parent ) ); - range.setStart( parent, - indexOf.call( parent.childNodes, node ) ); - range.collapse( true ); - parent.removeChild( node ); - if ( !isBlock( parent ) ) { - parent = getPreviousBlock( parent ); - } - fixCursor( parent ); - moveRangeBoundariesDownTree( range ); - self.setSelection( range ); - self._updatePath( range ); - } - self._ensureBottomLine(); - } catch ( error ) { - self.didError( error ); - } -}; - -// If you select all in IE8 then type, it makes a P; replace it with -// a DIV. -if ( isIE8 ) { - proto._ieSelAllClean = function () { - var firstChild = this._body.firstChild; - if ( firstChild.nodeName === 'P' ) { - this._saveRangeToBookmark( this.getSelection() ); - replaceWith( firstChild, this.createDefaultBlock([ - empty( firstChild ) - ])); - this.setSelection( this._getRangeAndRemoveBookmark() ); - } - }; -} - -var keyHandlers = { - enter: function ( self, event ) { - // We handle this ourselves - event.preventDefault(); - - // Must have some form of selection - var range = self.getSelection(), - block, parent, tag, splitTag, nodeAfterSplit; - if ( !range ) { return; } - - // Save undo checkpoint and add any links in the preceding section. - self._recordUndoState( range ); - addLinks( range.startContainer ); - self._getRangeAndRemoveBookmark( range ); - - // Selected text is overwritten, therefore delete the contents - // to collapse selection. - if ( !range.collapsed ) { - deleteContentsOfRange( range ); - } - - block = getStartBlockOfRange( range ); - if ( block && ( parent = getNearest( block, 'LI' ) ) ) { - block = parent; - } - tag = block ? block.nodeName : 'DIV'; - splitTag = tagAfterSplit[ tag ]; - - // If this is a malformed bit of document, just play it safe - // and insert a
          . - if ( !block ) { - insertNodeInRange( range, self.createElement( 'BR' ) ); - range.collapse( false ); - self.setSelection( range ); - self._updatePath( range, true ); - self._docWasChanged(); - return; - } - - // We need to wrap the contents in divs. - var splitNode = range.startContainer, - splitOffset = range.startOffset, - replacement; - if ( !splitTag ) { - // If the selection point is inside the block, we're going to - // rewrite it so our saved reference points won't be valid. - // Pick a node at a deeper point in the tree to avoid this. - if ( splitNode === block ) { - splitNode = splitOffset ? - splitNode.childNodes[ splitOffset - 1 ] : null; - splitOffset = 0; - if ( splitNode ) { - if ( splitNode.nodeName === 'BR' ) { - splitNode = splitNode.nextSibling; - } else { - splitOffset = getLength( splitNode ); - } - if ( !splitNode || splitNode.nodeName === 'BR' ) { - replacement = fixCursor( self.createElement( 'DIV' ) ); - if ( splitNode ) { - block.replaceChild( replacement, splitNode ); - } else { - block.appendChild( replacement ); - } - splitNode = replacement; - } - } - } - fixContainer( block ); - splitTag = 'DIV'; - if ( !splitNode ) { - splitNode = block.firstChild; - } - range.setStart( splitNode, splitOffset ); - range.setEnd( splitNode, splitOffset ); - block = getStartBlockOfRange( range ); - } - - if ( !block.textContent ) { - // Break list - if ( getNearest( block, 'UL' ) || getNearest( block, 'OL' ) ) { - return self.modifyBlocks( decreaseListLevel, range ); - } - // Break blockquote - else if ( getNearest( block, 'BLOCKQUOTE' ) ) { - return self.modifyBlocks( removeBlockQuote, range ); - } - } - - // Otherwise, split at cursor point. - nodeAfterSplit = splitBlock( block, splitNode, splitOffset ); - - // Focus cursor - // If there's a / etc. at the beginning of the split - // make sure we focus inside it. - while ( nodeAfterSplit.nodeType === ELEMENT_NODE ) { - var child = nodeAfterSplit.firstChild, - next; - - // Don't continue links over a block break; unlikely to be the - // desired outcome. - if ( nodeAfterSplit.nodeName === 'A' ) { - replaceWith( nodeAfterSplit, empty( nodeAfterSplit ) ); - nodeAfterSplit = child; - continue; - } - - while ( child && child.nodeType === TEXT_NODE && !child.data ) { - next = child.nextSibling; - if ( !next || next.nodeName === 'BR' ) { - break; - } - detach( child ); - child = next; - } - - // 'BR's essentially don't count; they're a browser hack. - // If you try to select the contents of a 'BR', FF will not let - // you type anything! - if ( !child || child.nodeName === 'BR' || - ( child.nodeType === TEXT_NODE && !isOpera ) ) { - break; - } - nodeAfterSplit = child; - } - range = self._createRange( nodeAfterSplit, 0 ); - self.setSelection( range ); - self._updatePath( range, true ); - - // Scroll into view - if ( nodeAfterSplit.nodeType === TEXT_NODE ) { - nodeAfterSplit = nodeAfterSplit.parentNode; - } - var doc = self._doc, - body = self._body; - if ( nodeAfterSplit.offsetTop + nodeAfterSplit.offsetHeight > - ( doc.documentElement.scrollTop || body.scrollTop ) + - body.offsetHeight ) { - nodeAfterSplit.scrollIntoView( false ); - } - - // We're not still in an undo state - self._docWasChanged(); - }, - backspace: function ( self, event ) { - var range = self.getSelection(); - // If not collapsed, delete contents - if ( !range.collapsed ) { - self._recordUndoState( range ); - self._getRangeAndRemoveBookmark( range ); - event.preventDefault(); - deleteContentsOfRange( range ); - self._ensureBottomLine(); - self.setSelection( range ); - self._updatePath( range, true ); - } - // If at beginning of block, merge with previous - else if ( rangeDoesStartAtBlockBoundary( range ) ) { - self._recordUndoState( range ); - self._getRangeAndRemoveBookmark( range ); - event.preventDefault(); - var current = getStartBlockOfRange( range ), - previous = current && getPreviousBlock( current ); - // Must not be at the very beginning of the text area. - if ( previous ) { - // If not editable, just delete whole block. - if ( !previous.isContentEditable ) { - detach( previous ); - return; - } - // Otherwise merge. - mergeWithBlock( previous, current, range ); - // If deleted line between containers, merge newly adjacent - // containers. - current = previous.parentNode; - while ( current && !current.nextSibling ) { - current = current.parentNode; - } - if ( current && ( current = current.nextSibling ) ) { - mergeContainers( current ); - } - self.setSelection( range ); - } - // If at very beginning of text area, allow backspace - // to break lists/blockquote. - else if ( current ) { - // Break list - if ( getNearest( current, 'UL' ) || - getNearest( current, 'OL' ) ) { - return self.modifyBlocks( decreaseListLevel, range ); - } - // Break blockquote - else if ( getNearest( current, 'BLOCKQUOTE' ) ) { - return self.modifyBlocks( decreaseBlockQuoteLevel, range ); - } - self.setSelection( range ); - self._updatePath( range, true ); - } - } - // Otherwise, leave to browser but check afterwards whether it has - // left behind an empty inline tag. - else { - var text = range.startContainer.data || ''; - if ( !notWS.test( text.charAt( range.startOffset - 1 ) ) ) { - self._recordUndoState( range ); - self._getRangeAndRemoveBookmark( range ); - self.setSelection( range ); - } - setTimeout( function () { afterDelete( self ); }, 0 ); - } - }, - 'delete': function ( self, event ) { - var range = self.getSelection(); - // If not collapsed, delete contents - if ( !range.collapsed ) { - self._recordUndoState( range ); - self._getRangeAndRemoveBookmark( range ); - event.preventDefault(); - deleteContentsOfRange( range ); - self._ensureBottomLine(); - self.setSelection( range ); - self._updatePath( range, true ); - } - // If at end of block, merge next into this block - else if ( rangeDoesEndAtBlockBoundary( range ) ) { - self._recordUndoState( range ); - self._getRangeAndRemoveBookmark( range ); - event.preventDefault(); - var current = getStartBlockOfRange( range ), - next = current && getNextBlock( current ); - // Must not be at the very end of the text area. - if ( next ) { - // If not editable, just delete whole block. - if ( !next.isContentEditable ) { - detach( next ); - return; - } - // Otherwise merge. - mergeWithBlock( current, next, range ); - // If deleted line between containers, merge newly adjacent - // containers. - next = current.parentNode; - while ( next && !next.nextSibling ) { - next = next.parentNode; - } - if ( next && ( next = next.nextSibling ) ) { - mergeContainers( next ); - } - self.setSelection( range ); - self._updatePath( range, true ); - } - } - // Otherwise, leave to browser but check afterwards whether it has - // left behind an empty inline tag. - else { - // Record undo point if deleting whitespace - var text = range.startContainer.data || ''; - if ( !notWS.test( text.charAt( range.startOffset ) ) ) { - self._recordUndoState( range ); - self._getRangeAndRemoveBookmark( range ); - self.setSelection( range ); - } - setTimeout( function () { afterDelete( self ); }, 0 ); - } - }, - tab: function ( self, event ) { - var range = self.getSelection(), - node, parent; - // If no selection and in an empty block - if ( range.collapsed && - rangeDoesStartAtBlockBoundary( range ) && - rangeDoesEndAtBlockBoundary( range ) ) { - node = getStartBlockOfRange( range ); - // Iterate through the block's parents - while ( parent = node.parentNode ) { - // If we find a UL or OL (so are in a list, node must be an LI) - if ( parent.nodeName === 'UL' || parent.nodeName === 'OL' ) { - // AND the LI is not the first in the list - if ( node.previousSibling ) { - // Then increase the list level - event.preventDefault(); - self.modifyBlocks( increaseListLevel, range ); - } - break; - } - node = parent; - } - event.preventDefault(); - } - }, - space: function ( self ) { - var range = self.getSelection(); - self._recordUndoState( range ); - addLinks( range.startContainer ); - self._getRangeAndRemoveBookmark( range ); - self.setSelection( range ); - }, - left: function ( self ) { - self._removeZWS(); - }, - right: function ( self ) { - self._removeZWS(); - } -}; -// Firefox incorrectly handles Cmd-left/Cmd-right on Mac: -// it goes back/forward in history! Override to do the right -// thing. -// https://bugzilla.mozilla.org/show_bug.cgi?id=289384 -if ( isMac && isGecko && win.getSelection().modify ) { - keyHandlers[ 'meta-left' ] = function ( self, event ) { - event.preventDefault(); - self._win.getSelection().modify( 'move', 'backward', 'lineboundary' ); - }; - keyHandlers[ 'meta-right' ] = function ( self, event ) { - event.preventDefault(); - self._win.getSelection().modify( 'move', 'forward', 'lineboundary' ); - }; -} - -keyHandlers[ ctrlKey + 'b' ] = mapKeyToFormat( 'B' ); -keyHandlers[ ctrlKey + 'i' ] = mapKeyToFormat( 'I' ); -keyHandlers[ ctrlKey + 'u' ] = mapKeyToFormat( 'U' ); -keyHandlers[ ctrlKey + 'shift-7' ] = mapKeyToFormat( 'S' ); -keyHandlers[ ctrlKey + 'shift-5' ] = mapKeyToFormat( 'SUB', { tag: 'SUP' } ); -keyHandlers[ ctrlKey + 'shift-6' ] = mapKeyToFormat( 'SUP', { tag: 'SUB' } ); -keyHandlers[ ctrlKey + 'shift-8' ] = mapKeyTo( 'makeUnorderedList' ); -keyHandlers[ ctrlKey + 'shift-9' ] = mapKeyTo( 'makeOrderedList' ); -keyHandlers[ ctrlKey + '[' ] = mapKeyTo( 'decreaseQuoteLevel' ); -keyHandlers[ ctrlKey + ']' ] = mapKeyTo( 'increaseQuoteLevel' ); -keyHandlers[ ctrlKey + 'y' ] = mapKeyTo( 'redo' ); -keyHandlers[ ctrlKey + 'z' ] = mapKeyTo( 'undo' ); -keyHandlers[ ctrlKey + 'shift-z' ] = mapKeyTo( 'redo' ); - -// Ref: http://unixpapa.com/js/key.html -proto._onKey = function ( event ) { - var code = event.keyCode, - key = keys[ code ], - modifiers = ''; - - if ( !key ) { - key = String.fromCharCode( code ).toLowerCase(); - // Only reliable for letters and numbers - if ( !/^[A-Za-z0-9]$/.test( key ) ) { - key = ''; - } - } - - // On keypress, delete and '.' both have event.keyCode 46 - // Must check event.which to differentiate. - if ( isOpera && event.which === 46 ) { - key = '.'; - } - - // Function keys - if ( 111 < code && code < 124 ) { - key = 'f' + ( code - 111 ); - } - - if ( event.altKey ) { modifiers += 'alt-'; } - if ( event.ctrlKey ) { modifiers += 'ctrl-'; } - if ( event.metaKey ) { modifiers += 'meta-'; } - if ( event.shiftKey ) { modifiers += 'shift-'; } - - key = modifiers + key; - - if ( keyHandlers[ key ] ) { - keyHandlers[ key ]( this, event ); - } -}; - -// --- Get/Set data --- - -proto._getHTML = function () { - return this._body.innerHTML; -}; - -proto._setHTML = function ( html ) { - var node = this._body; - node.innerHTML = html; - do { - fixCursor( node ); - } while ( node = getNextBlock( node ) ); -}; - -proto.getHTML = function ( withBookMark ) { - var brs = [], - node, fixer, html, l, range; - if ( withBookMark && ( range = this.getSelection() ) ) { - this._saveRangeToBookmark( range ); - } - if ( useTextFixer ) { - node = this._body; - while ( node = getNextBlock( node ) ) { - if ( !node.textContent && !node.querySelector( 'BR' ) ) { - fixer = this.createElement( 'BR' ); - node.appendChild( fixer ); - brs.push( fixer ); - } - } - } - html = this._getHTML().replace( /\u200B/g, '' ); - if ( useTextFixer ) { - l = brs.length; - while ( l-- ) { - detach( brs[l] ); - } - } - if ( range ) { - this._getRangeAndRemoveBookmark( range ); - } - return html; -}; - -proto.setHTML = function ( html ) { - var frag = this._doc.createDocumentFragment(), - div = this.createElement( 'DIV' ), - child; - - // Parse HTML into DOM tree - div.innerHTML = html; - frag.appendChild( empty( div ) ); - - cleanTree( frag, true ); - cleanupBRs( frag ); - - fixContainer( frag ); - - // Fix cursor - var node = frag; - while ( node = getNextBlock( node ) ) { - fixCursor( node ); - } - - // Remove existing body children - var body = this._body; - while ( child = body.lastChild ) { - body.removeChild( child ); - } - - // And insert new content - body.appendChild( frag ); - fixCursor( body ); - - // Reset the undo stack - this._undoIndex = -1; - this._undoStack.length = 0; - this._undoStackLength = 0; - this._isInUndoState = false; - - // Record undo state - var range = this._getRangeAndRemoveBookmark() || - this._createRange( body.firstChild, 0 ); - this._recordUndoState( range ); - this._getRangeAndRemoveBookmark( range ); - // IE will also set focus when selecting text so don't use - // setSelection. Instead, just store it in lastSelection, so if - // anything calls getSelection before first focus, we have a range - // to return. - if ( losesSelectionOnBlur ) { - this._lastSelection = range; - } else { - this.setSelection( range ); - } - this._updatePath( range, true ); - - return this; -}; - -proto.insertElement = function ( el, range ) { - if ( !range ) { range = this.getSelection(); } - range.collapse( true ); - if ( isInline( el ) ) { - insertNodeInRange( range, el ); - range.setStartAfter( el ); - } else { - // Get containing block node. - var body = this._body, - splitNode = getStartBlockOfRange( range ) || body, - parent, nodeAfterSplit; - // While at end of container node, move up DOM tree. - while ( splitNode !== body && !splitNode.nextSibling ) { - splitNode = splitNode.parentNode; - } - // If in the middle of a container node, split up to body. - if ( splitNode !== body ) { - parent = splitNode.parentNode; - nodeAfterSplit = split( parent, splitNode.nextSibling, body ); - } - if ( nodeAfterSplit ) { - body.insertBefore( el, nodeAfterSplit ); - range.setStart( nodeAfterSplit, 0 ); - range.setStart( nodeAfterSplit, 0 ); - moveRangeBoundariesDownTree( range ); - } else { - body.appendChild( el ); - // Insert blank line below block. - body.appendChild( this.createDefaultBlock() ); - range.setStart( el, 0 ); - range.setEnd( el, 0 ); - } - this.focus(); - this.setSelection( range ); - this._updatePath( range ); - } - return this; -}; - -proto.insertImage = function ( src ) { - var img = this.createElement( 'IMG', { - src: src - }); - this.insertElement( img ); - return img; -}; - -// --- Formatting --- - -var command = function ( method, arg, arg2 ) { - return function () { - this[ method ]( arg, arg2 ); - return this.focus(); - }; -}; - -proto.addStyles = function ( styles ) { - if ( styles ) { - var head = this._doc.documentElement.firstChild, - style = this.createElement( 'STYLE', { - type: 'text/css' - }); - if ( style.styleSheet ) { - // IE8: must append to document BEFORE adding styles - // or you get the IE7 CSS parser! - head.appendChild( style ); - style.styleSheet.cssText = styles; - } else { - // Everyone else - style.appendChild( this._doc.createTextNode( styles ) ); - head.appendChild( style ); - } - } - return this; -}; - -proto.bold = command( 'changeFormat', { tag: 'B' } ); -proto.italic = command( 'changeFormat', { tag: 'I' } ); -proto.underline = command( 'changeFormat', { tag: 'U' } ); -proto.strikethrough = command( 'changeFormat', { tag: 'S' } ); -proto.subscript = command( 'changeFormat', { tag: 'SUB' }, { tag: 'SUP' } ); -proto.superscript = command( 'changeFormat', { tag: 'SUP' }, { tag: 'SUB' } ); - -proto.removeBold = command( 'changeFormat', null, { tag: 'B' } ); -proto.removeItalic = command( 'changeFormat', null, { tag: 'I' } ); -proto.removeUnderline = command( 'changeFormat', null, { tag: 'U' } ); -proto.removeStrikethrough = command( 'changeFormat', null, { tag: 'S' } ); -proto.removeSubscript = command( 'changeFormat', null, { tag: 'SUB' } ); -proto.removeSuperscript = command( 'changeFormat', null, { tag: 'SUP' } ); - -proto.makeLink = function ( url ) { - var range = this.getSelection(); - if ( range.collapsed ) { - var protocolEnd = url.indexOf( ':' ) + 1; - if ( protocolEnd ) { - while ( url[ protocolEnd ] === '/' ) { protocolEnd += 1; } - } - insertNodeInRange( - range, - this._doc.createTextNode( url.slice( protocolEnd ) ) - ); - } - this.changeFormat({ - tag: 'A', - attributes: { - href: url - } - }, { - tag: 'A' - }, range ); - return this.focus(); -}; -proto.removeLink = function () { - this.changeFormat( null, { - tag: 'A' - }, this.getSelection(), true ); - return this.focus(); -}; - -proto.setFontFace = function ( name ) { - this.changeFormat({ - tag: 'SPAN', - attributes: { - 'class': 'font', - style: 'font-family: ' + name + ', sans-serif;' - } - }, { - tag: 'SPAN', - attributes: { 'class': 'font' } - }); - return this.focus(); -}; -proto.setFontSize = function ( size ) { - this.changeFormat({ - tag: 'SPAN', - attributes: { - 'class': 'size', - style: 'font-size: ' + - ( typeof size === 'number' ? size + 'px' : size ) - } - }, { - tag: 'SPAN', - attributes: { 'class': 'size' } - }); - return this.focus(); -}; - -proto.setTextColour = function ( colour ) { - this.changeFormat({ - tag: 'SPAN', - attributes: { - 'class': 'colour', - style: 'color: ' + colour - } - }, { - tag: 'SPAN', - attributes: { 'class': 'colour' } - }); - return this.focus(); -}; - -proto.setHighlightColour = function ( colour ) { - this.changeFormat({ - tag: 'SPAN', - attributes: { - 'class': 'highlight', - style: 'background-color: ' + colour - } - }, { - tag: 'SPAN', - attributes: { 'class': 'highlight' } - }); - return this.focus(); -}; - -proto.setTextAlignment = function ( alignment ) { - this.forEachBlock( function ( block ) { - block.className = ( block.className - .split( /\s+/ ) - .filter( function ( klass ) { - return !( /align/.test( klass ) ); - }) - .join( ' ' ) + - ' align-' + alignment ).trim(); - block.style.textAlign = alignment; - }, true ); - return this.focus(); -}; - -proto.setTextDirection = function ( direction ) { - this.forEachBlock( function ( block ) { - block.className = ( block.className - .split( /\s+/ ) - .filter( function ( klass ) { - return !( /dir/.test( klass ) ); - }) - .join( ' ' ) + - ' dir-' + direction ).trim(); - block.dir = direction; - }, true ); - return this.focus(); -}; - -proto.increaseQuoteLevel = command( 'modifyBlocks', increaseBlockQuoteLevel ); -proto.decreaseQuoteLevel = command( 'modifyBlocks', decreaseBlockQuoteLevel ); - -proto.makeUnorderedList = command( 'modifyBlocks', makeUnorderedList ); -proto.makeOrderedList = command( 'modifyBlocks', makeOrderedList ); -proto.removeList = command( 'modifyBlocks', removeList ); - -proto.increaseListLevel = command( 'modifyBlocks', increaseListLevel ); -proto.decreaseListLevel = command( 'modifyBlocks', decreaseListLevel ); diff --git a/source/Node.js b/source/Node.js deleted file mode 100644 index ebca6e2..0000000 --- a/source/Node.js +++ /dev/null @@ -1,460 +0,0 @@ -/*global - ELEMENT_NODE, - TEXT_NODE, - SHOW_ELEMENT, - FILTER_ACCEPT, - FILTER_SKIP, - win, - isOpera, - useTextFixer, - cantFocusEmptyTextNodes, - - TreeWalker, - - Text -*/ -/*jshint strict:false */ - -var inlineNodeNames = /^(?:#text|A(?:BBR|CRONYM)?|B(?:R|D[IO])?|C(?:ITE|ODE)|D(?:ATA|FN|EL)|EM|FONT|HR|I(?:NPUT|MG|NS)?|KBD|Q|R(?:P|T|UBY)|S(?:U[BP]|PAN|TR(?:IKE|ONG)|MALL|AMP)?|U|VAR|WBR)$/; - -var leafNodeNames = { - BR: 1, - IMG: 1, - INPUT: 1 -}; - -function every ( nodeList, fn ) { - var l = nodeList.length; - while ( l-- ) { - if ( !fn( nodeList[l] ) ) { - return false; - } - } - return true; -} - -// --- - -function hasTagAttributes ( node, tag, attributes ) { - if ( node.nodeName !== tag ) { - return false; - } - for ( var attr in attributes ) { - if ( node.getAttribute( attr ) !== attributes[ attr ] ) { - return false; - } - } - return true; -} -function areAlike ( node, node2 ) { - return ( - node.nodeType === node2.nodeType && - node.nodeName === node2.nodeName && - node.className === node2.className && - ( ( !node.style && !node2.style ) || - node.style.cssText === node2.style.cssText ) - ); -} - -function isLeaf ( node ) { - return node.nodeType === ELEMENT_NODE && - !!leafNodeNames[ node.nodeName ]; -} -function isInline ( node ) { - return inlineNodeNames.test( node.nodeName ); -} -function isBlock ( node ) { - return node.nodeType === ELEMENT_NODE && - !isInline( node ) && every( node.childNodes, isInline ); -} -function isContainer ( node ) { - return node.nodeType === ELEMENT_NODE && - !isInline( node ) && !isBlock( node ); -} - -function acceptIfBlock ( el ) { - return isBlock( el ) ? FILTER_ACCEPT : FILTER_SKIP; -} -function getBlockWalker ( node ) { - var doc = node.ownerDocument, - walker = new TreeWalker( - doc.body, SHOW_ELEMENT, acceptIfBlock, false ); - walker.currentNode = node; - return walker; -} - -function getPreviousBlock ( node ) { - return getBlockWalker( node ).previousNode(); -} -function getNextBlock ( node ) { - return getBlockWalker( node ).nextNode(); -} -function getNearest ( node, tag, attributes ) { - do { - if ( hasTagAttributes( node, tag, attributes ) ) { - return node; - } - } while ( node = node.parentNode ); - return null; -} - -function getPath ( node ) { - var parent = node.parentNode, - path, id, className, classNames; - if ( !parent || node.nodeType !== ELEMENT_NODE ) { - path = parent ? getPath( parent ) : ''; - } else { - path = getPath( parent ); - path += ( path ? '>' : '' ) + node.nodeName; - if ( id = node.id ) { - path += '#' + id; - } - if ( className = node.className.trim() ) { - classNames = className.split( /\s\s*/ ); - classNames.sort(); - path += '.'; - path += classNames.join( '.' ); - } - } - return path; -} - -function getLength ( node ) { - var nodeType = node.nodeType; - return nodeType === ELEMENT_NODE ? - node.childNodes.length : node.length || 0; -} - -function detach ( node ) { - var parent = node.parentNode; - if ( parent ) { - parent.removeChild( node ); - } - return node; -} -function replaceWith ( node, node2 ) { - var parent = node.parentNode; - if ( parent ) { - parent.replaceChild( node2, node ); - } -} -function empty ( node ) { - var frag = node.ownerDocument.createDocumentFragment(), - childNodes = node.childNodes, - l = childNodes ? childNodes.length : 0; - while ( l-- ) { - frag.appendChild( node.firstChild ); - } - return frag; -} - -function fixCursor ( node ) { - // In Webkit and Gecko, block level elements are collapsed and - // unfocussable if they have no content. To remedy this, a
          must be - // inserted. In Opera and IE, we just need a textnode in order for the - // cursor to appear. - var doc = node.ownerDocument, - root = node, - fixer, child; - - if ( node.nodeName === 'BODY' ) { - if ( !( child = node.firstChild ) || child.nodeName === 'BR' ) { - fixer = doc.createElement( 'DIV' ); - if ( child ) { - node.replaceChild( fixer, child ); - } - else { - node.appendChild( fixer ); - } - node = fixer; - fixer = null; - } - } - - if ( isInline( node ) ) { - if ( !node.firstChild ) { - if ( cantFocusEmptyTextNodes ) { - fixer = doc.createTextNode( '\u200B' ); - if ( win.editor ) { - win.editor._didAddZWS(); - } - } else { - fixer = doc.createTextNode( '' ); - } - } - } else { - if ( useTextFixer ) { - while ( node.nodeType !== TEXT_NODE && !isLeaf( node ) ) { - child = node.firstChild; - if ( !child ) { - fixer = doc.createTextNode( '' ); - break; - } - node = child; - } - if ( node.nodeType === TEXT_NODE ) { - // Opera will collapse the block element if it contains - // just spaces (but not if it contains no data at all). - if ( /^ +$/.test( node.data ) ) { - node.data = ''; - } - } else if ( isLeaf( node ) ) { - node.parentNode.insertBefore( doc.createTextNode( '' ), node ); - } - } - else if ( !node.querySelector( 'BR' ) ) { - fixer = doc.createElement( 'BR' ); - while ( ( child = node.lastElementChild ) && !isInline( child ) ) { - node = child; - } - } - } - if ( fixer ) { - node.appendChild( fixer ); - } - - return root; -} - -function split ( node, offset, stopNode ) { - var nodeType = node.nodeType, - parent, clone, next; - if ( nodeType === TEXT_NODE && node !== stopNode ) { - return split( node.parentNode, node.splitText( offset ), stopNode ); - } - if ( nodeType === ELEMENT_NODE ) { - if ( typeof( offset ) === 'number' ) { - offset = offset < node.childNodes.length ? - node.childNodes[ offset ] : null; - } - if ( node === stopNode ) { - return offset; - } - - // Clone node without children - parent = node.parentNode; - clone = node.cloneNode( false ); - - // Add right-hand siblings to the clone - while ( offset ) { - next = offset.nextSibling; - clone.appendChild( offset ); - offset = next; - } - - // DO NOT NORMALISE. This may undo the fixCursor() call - // of a node lower down the tree! - - // We need something in the element in order for the cursor to appear. - fixCursor( node ); - fixCursor( clone ); - - // Inject clone after original node - if ( next = node.nextSibling ) { - parent.insertBefore( clone, next ); - } else { - parent.appendChild( clone ); - } - - // Keep on splitting up the tree - return split( parent, clone, stopNode ); - } - return offset; -} - -function mergeInlines ( node, range ) { - if ( node.nodeType !== ELEMENT_NODE ) { - return; - } - var children = node.childNodes, - l = children.length, - frags = [], - child, prev, len; - while ( l-- ) { - child = children[l]; - prev = l && children[ l - 1 ]; - if ( l && isInline( child ) && areAlike( child, prev ) && - !leafNodeNames[ child.nodeName ] ) { - if ( range.startContainer === child ) { - range.startContainer = prev; - range.startOffset += getLength( prev ); - } - if ( range.endContainer === child ) { - range.endContainer = prev; - range.endOffset += getLength( prev ); - } - if ( range.startContainer === node ) { - if ( range.startOffset > l ) { - range.startOffset -= 1; - } - else if ( range.startOffset === l ) { - range.startContainer = prev; - range.startOffset = getLength( prev ); - } - } - if ( range.endContainer === node ) { - if ( range.endOffset > l ) { - range.endOffset -= 1; - } - else if ( range.endOffset === l ) { - range.endContainer = prev; - range.endOffset = getLength( prev ); - } - } - detach( child ); - if ( child.nodeType === TEXT_NODE ) { - prev.appendData( child.data ); - } - else { - frags.push( empty( child ) ); - } - } - else if ( child.nodeType === ELEMENT_NODE ) { - len = frags.length; - while ( len-- ) { - child.appendChild( frags.pop() ); - } - mergeInlines( child, range ); - } - } -} - -function mergeWithBlock ( block, next, range ) { - var container = next, - last, offset, _range; - while ( container.parentNode.childNodes.length === 1 ) { - container = container.parentNode; - } - detach( container ); - - offset = block.childNodes.length; - - // Remove extra
          fixer if present. - last = block.lastChild; - if ( last && last.nodeName === 'BR' ) { - block.removeChild( last ); - offset -= 1; - } - - _range = { - startContainer: block, - startOffset: offset, - endContainer: block, - endOffset: offset - }; - - block.appendChild( empty( next ) ); - mergeInlines( block, _range ); - - range.setStart( _range.startContainer, _range.startOffset ); - range.collapse( true ); - - // Opera inserts a BR if you delete the last piece of text - // in a block-level element. Unfortunately, it then gets - // confused when setting the selection subsequently and - // refuses to accept the range that finishes just before the - // BR. Removing the BR fixes the bug. - // Steps to reproduce bug: Type "a-b-c" (where - is return) - // then backspace twice. The cursor goes to the top instead - // of after "b". - if ( isOpera && ( last = block.lastChild ) && last.nodeName === 'BR' ) { - block.removeChild( last ); - } -} - -function mergeContainers ( node ) { - var prev = node.previousSibling, - first = node.firstChild, - doc = node.ownerDocument, - isListItem = ( node.nodeName === 'LI' ), - needsFix, block; - - // Do not merge LIs, unless it only contains a UL - if ( isListItem && ( !first || !/^[OU]L$/.test( first.nodeName ) ) ) { - return; - } - - if ( prev && areAlike( prev, node ) ) { - if ( !isContainer( prev ) ) { - if ( isListItem ) { - block = doc.createElement( 'DIV' ); - block.appendChild( empty( prev ) ); - prev.appendChild( block ); - } else { - return; - } - } - detach( node ); - needsFix = !isContainer( node ); - prev.appendChild( empty( node ) ); - if ( needsFix ) { - fixContainer( prev ); - } - if ( first ) { - mergeContainers( first ); - } - } else if ( isListItem ) { - prev = doc.createElement( 'DIV' ); - node.insertBefore( prev, first ); - fixCursor( prev ); - } -} - -// Recursively examine container nodes and wrap any inline children. -function fixContainer ( container ) { - var children = container.childNodes, - doc = container.ownerDocument, - wrapper = null, - i, l, child, isBR; - for ( i = 0, l = children.length; i < l; i += 1 ) { - child = children[i]; - isBR = child.nodeName === 'BR'; - if ( !isBR && isInline( child ) ) { - if ( !wrapper ) { wrapper = createElement( doc, 'DIV' ); } - wrapper.appendChild( child ); - i -= 1; - l -= 1; - } else if ( isBR || wrapper ) { - if ( !wrapper ) { wrapper = createElement( doc, 'DIV' ); } - fixCursor( wrapper ); - if ( isBR ) { - container.replaceChild( wrapper, child ); - } else { - container.insertBefore( wrapper, child ); - i += 1; - l += 1; - } - wrapper = null; - } - if ( isContainer( child ) ) { - fixContainer( child ); - } - } - if ( wrapper ) { - container.appendChild( fixCursor( wrapper ) ); - } - return container; -} - -function createElement ( doc, tag, props, children ) { - var el = doc.createElement( tag ), - attr, value, i, l; - if ( props instanceof Array ) { - children = props; - props = null; - } - if ( props ) { - for ( attr in props ) { - value = props[ attr ]; - if ( value !== undefined ) { - el.setAttribute( attr, props[ attr ] ); - } - } - } - if ( children ) { - for ( i = 0, l = children.length; i < l; i += 1 ) { - el.appendChild( children[i] ); - } - } - return el; -} diff --git a/source/Range.js b/source/Range.js deleted file mode 100644 index 3efdde1..0000000 --- a/source/Range.js +++ /dev/null @@ -1,511 +0,0 @@ -/*global - ELEMENT_NODE, - TEXT_NODE, - SHOW_TEXT, - FILTER_ACCEPT, - START_TO_START, - START_TO_END, - END_TO_END, - END_TO_START, - indexOf, - - TreeWalker, - - isLeaf, - isInline, - isBlock, - getPreviousBlock, - getNextBlock, - getLength, - fixCursor, - split, - mergeWithBlock, - mergeContainers -*/ -/*jshint strict:false */ - -var getNodeBefore = function ( node, offset ) { - var children = node.childNodes; - while ( offset && node.nodeType === ELEMENT_NODE ) { - node = children[ offset - 1 ]; - children = node.childNodes; - offset = children.length; - } - return node; -}; - -var getNodeAfter = function ( node, offset ) { - if ( node.nodeType === ELEMENT_NODE ) { - var children = node.childNodes; - if ( offset < children.length ) { - node = children[ offset ]; - } else { - while ( node && !node.nextSibling ) { - node = node.parentNode; - } - if ( node ) { node = node.nextSibling; } - } - } - return node; -}; - -// --- - -var forEachTextNodeInRange = function ( range, fn ) { - range = range.cloneRange(); - moveRangeBoundariesDownTree( range ); - - var startContainer = range.startContainer, - endContainer = range.endContainer, - root = range.commonAncestorContainer, - walker = new TreeWalker( - root, SHOW_TEXT, function ( node ) { - return FILTER_ACCEPT; - }, false ), - textnode = walker.currentNode = startContainer; - - while ( !fn( textnode, range ) && - textnode !== endContainer && - ( textnode = walker.nextNode() ) ) {} -}; - -var getTextContentInRange = function ( range ) { - var textContent = ''; - forEachTextNodeInRange( range, function ( textnode, range ) { - var value = textnode.data; - if ( value && ( /\S/.test( value ) ) ) { - if ( textnode === range.endContainer ) { - value = value.slice( 0, range.endOffset ); - } - if ( textnode === range.startContainer ) { - value = value.slice( range.startOffset ); - } - textContent += value; - } - }); - return textContent; -}; - -// --- - -var insertNodeInRange = function ( range, node ) { - // Insert at start. - var startContainer = range.startContainer, - startOffset = range.startOffset, - endContainer = range.endContainer, - endOffset = range.endOffset, - parent, children, childCount, afterSplit; - - // If part way through a text node, split it. - if ( startContainer.nodeType === TEXT_NODE ) { - parent = startContainer.parentNode; - children = parent.childNodes; - if ( startOffset === startContainer.length ) { - startOffset = indexOf.call( children, startContainer ) + 1; - if ( range.collapsed ) { - endContainer = parent; - endOffset = startOffset; - } - } else { - if ( startOffset ) { - afterSplit = startContainer.splitText( startOffset ); - if ( endContainer === startContainer ) { - endOffset -= startOffset; - endContainer = afterSplit; - } - else if ( endContainer === parent ) { - endOffset += 1; - } - startContainer = afterSplit; - } - startOffset = indexOf.call( children, startContainer ); - } - startContainer = parent; - } else { - children = startContainer.childNodes; - } - - childCount = children.length; - - if ( startOffset === childCount) { - startContainer.appendChild( node ); - } else { - startContainer.insertBefore( node, children[ startOffset ] ); - } - if ( startContainer === endContainer ) { - endOffset += children.length - childCount; - } - - range.setStart( startContainer, startOffset ); - range.setEnd( endContainer, endOffset ); -}; - -var extractContentsOfRange = function ( range, common ) { - var startContainer = range.startContainer, - startOffset = range.startOffset, - endContainer = range.endContainer, - endOffset = range.endOffset; - - if ( !common ) { - common = range.commonAncestorContainer; - } - - if ( common.nodeType === TEXT_NODE ) { - common = common.parentNode; - } - - var endNode = split( endContainer, endOffset, common ), - startNode = split( startContainer, startOffset, common ), - frag = common.ownerDocument.createDocumentFragment(), - next; - - // End node will be null if at end of child nodes list. - while ( startNode !== endNode ) { - next = startNode.nextSibling; - frag.appendChild( startNode ); - startNode = next; - } - - range.setStart( common, endNode ? - indexOf.call( common.childNodes, endNode ) : - common.childNodes.length ); - range.collapse( true ); - - fixCursor( common ); - - return frag; -}; - -var deleteContentsOfRange = function ( range ) { - // Move boundaries up as much as possible to reduce need to split. - moveRangeBoundariesUpTree( range ); - - // Remove selected range - extractContentsOfRange( range ); - - // If we split into two different blocks, merge the blocks. - var startBlock = getStartBlockOfRange( range ), - endBlock = getEndBlockOfRange( range ); - if ( startBlock && endBlock && startBlock !== endBlock ) { - mergeWithBlock( startBlock, endBlock, range ); - } - - // Ensure block has necessary children - if ( startBlock ) { - fixCursor( startBlock ); - } - - // Ensure body has a block-level element in it. - var body = range.endContainer.ownerDocument.body, - child = body.firstChild; - if ( !child || child.nodeName === 'BR' ) { - fixCursor( body ); - range.selectNodeContents( body.firstChild ); - } - - // Ensure valid range (must have only block or inline containers) - var isCollapsed = range.collapsed; - moveRangeBoundariesDownTree( range ); - if ( isCollapsed ) { - // Collapse - range.collapse( true ); - } -}; - -// --- - -var insertTreeFragmentIntoRange = function ( range, frag ) { - // Check if it's all inline content - var allInline = true, - children = frag.childNodes, - l = children.length; - while ( l-- ) { - if ( !isInline( children[l] ) ) { - allInline = false; - break; - } - } - - // Delete any selected content - if ( !range.collapsed ) { - deleteContentsOfRange( range ); - } - - // Move range down into text ndoes - moveRangeBoundariesDownTree( range ); - - // If inline, just insert at the current position. - if ( allInline ) { - insertNodeInRange( range, frag ); - range.collapse( false ); - } - // Otherwise, split up to body, insert inline before and after split - // and insert block in between split, then merge containers. - else { - var nodeAfterSplit = split( range.startContainer, range.startOffset, - range.startContainer.ownerDocument.body ), - nodeBeforeSplit = nodeAfterSplit.previousSibling, - startContainer = nodeBeforeSplit, - startOffset = startContainer.childNodes.length, - endContainer = nodeAfterSplit, - endOffset = 0, - parent = nodeAfterSplit.parentNode, - child, node; - - while ( ( child = startContainer.lastChild ) && - child.nodeType === ELEMENT_NODE && - child.nodeName !== 'BR' ) { - startContainer = child; - startOffset = startContainer.childNodes.length; - } - while ( ( child = endContainer.firstChild ) && - child.nodeType === ELEMENT_NODE && - child.nodeName !== 'BR' ) { - endContainer = child; - } - while ( ( child = frag.firstChild ) && isInline( child ) ) { - startContainer.appendChild( child ); - } - while ( ( child = frag.lastChild ) && isInline( child ) ) { - endContainer.insertBefore( child, endContainer.firstChild ); - endOffset += 1; - } - - // Fix cursor then insert block(s) - node = frag; - while ( node = getNextBlock( node ) ) { - fixCursor( node ); - } - parent.insertBefore( frag, nodeAfterSplit ); - - // Remove empty nodes created by split and merge inserted containers - // with edges of split - node = nodeAfterSplit.previousSibling; - if ( !nodeAfterSplit.textContent ) { - parent.removeChild( nodeAfterSplit ); - } else { - mergeContainers( nodeAfterSplit ); - } - if ( !nodeAfterSplit.parentNode ) { - endContainer = node; - endOffset = getLength( endContainer ); - } - - if ( !nodeBeforeSplit.textContent) { - startContainer = nodeBeforeSplit.nextSibling; - startOffset = 0; - parent.removeChild( nodeBeforeSplit ); - } else { - mergeContainers( nodeBeforeSplit ); - } - - range.setStart( startContainer, startOffset ); - range.setEnd( endContainer, endOffset ); - moveRangeBoundariesDownTree( range ); - } -}; - -// --- - -var isNodeContainedInRange = function ( range, node, partial ) { - var nodeRange = node.ownerDocument.createRange(); - - nodeRange.selectNode( node ); - - if ( partial ) { - // Node must not finish before range starts or start after range - // finishes. - var nodeEndBeforeStart = ( range.compareBoundaryPoints( - END_TO_START, nodeRange ) > -1 ), - nodeStartAfterEnd = ( range.compareBoundaryPoints( - START_TO_END, nodeRange ) < 1 ); - return ( !nodeEndBeforeStart && !nodeStartAfterEnd ); - } - else { - // Node must start after range starts and finish before range - // finishes - var nodeStartAfterStart = ( range.compareBoundaryPoints( - START_TO_START, nodeRange ) < 1 ), - nodeEndBeforeEnd = ( range.compareBoundaryPoints( - END_TO_END, nodeRange ) > -1 ); - return ( nodeStartAfterStart && nodeEndBeforeEnd ); - } -}; - -var moveRangeBoundariesDownTree = function ( range ) { - var startContainer = range.startContainer, - startOffset = range.startOffset, - endContainer = range.endContainer, - endOffset = range.endOffset, - child; - - while ( startContainer.nodeType !== TEXT_NODE ) { - child = startContainer.childNodes[ startOffset ]; - if ( !child || isLeaf( child ) ) { - break; - } - startContainer = child; - startOffset = 0; - } - if ( endOffset ) { - while ( endContainer.nodeType !== TEXT_NODE ) { - child = endContainer.childNodes[ endOffset - 1 ]; - if ( !child || isLeaf( child ) ) { - break; - } - endContainer = child; - endOffset = getLength( endContainer ); - } - } else { - while ( endContainer.nodeType !== TEXT_NODE ) { - child = endContainer.firstChild; - if ( !child || isLeaf( child ) ) { - break; - } - endContainer = child; - } - } - - // If collapsed, this algorithm finds the nearest text node positions - // *outside* the range rather than inside, but also it flips which is - // assigned to which. - if ( range.collapsed ) { - range.setStart( endContainer, endOffset ); - range.setEnd( startContainer, startOffset ); - } else { - range.setStart( startContainer, startOffset ); - range.setEnd( endContainer, endOffset ); - } -}; - -var moveRangeBoundariesUpTree = function ( range, common ) { - var startContainer = range.startContainer, - startOffset = range.startOffset, - endContainer = range.endContainer, - endOffset = range.endOffset, - parent; - - if ( !common ) { - common = range.commonAncestorContainer; - } - - while ( startContainer !== common && !startOffset ) { - parent = startContainer.parentNode; - startOffset = indexOf.call( parent.childNodes, startContainer ); - startContainer = parent; - } - - while ( endContainer !== common && - endOffset === getLength( endContainer ) ) { - parent = endContainer.parentNode; - endOffset = indexOf.call( parent.childNodes, endContainer ) + 1; - endContainer = parent; - } - - range.setStart( startContainer, startOffset ); - range.setEnd( endContainer, endOffset ); -}; - -// Returns the first block at least partially contained by the range, -// or null if no block is contained by the range. -var getStartBlockOfRange = function ( range ) { - var container = range.startContainer, - block; - - // If inline, get the containing block. - if ( isInline( container ) ) { - block = getPreviousBlock( container ); - } else if ( isBlock( container ) ) { - block = container; - } else { - block = getNodeBefore( container, range.startOffset ); - block = getNextBlock( block ); - } - // Check the block actually intersects the range - return block && isNodeContainedInRange( range, block, true ) ? block : null; -}; - -// Returns the last block at least partially contained by the range, -// or null if no block is contained by the range. -var getEndBlockOfRange = function ( range ) { - var container = range.endContainer, - block, child; - - // If inline, get the containing block. - if ( isInline( container ) ) { - block = getPreviousBlock( container ); - } else if ( isBlock( container ) ) { - block = container; - } else { - block = getNodeAfter( container, range.endOffset ); - if ( !block ) { - block = container.ownerDocument.body; - while ( child = block.lastChild ) { - block = child; - } - } - block = getPreviousBlock( block ); - - } - // Check the block actually intersects the range - return block && isNodeContainedInRange( range, block, true ) ? block : null; -}; - -var rangeDoesStartAtBlockBoundary = function ( range ) { - var startContainer = range.startContainer, - startOffset = range.startOffset, - parent, child; - - while ( isInline( startContainer ) ) { - if ( startOffset ) { - return false; - } - parent = startContainer.parentNode; - startOffset = indexOf.call( parent.childNodes, startContainer ); - startContainer = parent; - } - // Skip empty text nodes and
          s. - while ( startOffset && - ( child = startContainer.childNodes[ startOffset - 1 ] ) && - ( child.data === '' || child.nodeName === 'BR' ) ) { - startOffset -= 1; - } - return !startOffset; -}; - -var rangeDoesEndAtBlockBoundary = function ( range ) { - var endContainer = range.endContainer, - endOffset = range.endOffset, - length = getLength( endContainer ), - parent, child; - - while ( isInline( endContainer ) ) { - if ( endOffset !== length ) { - return false; - } - parent = endContainer.parentNode; - endOffset = indexOf.call( parent.childNodes, endContainer ) + 1; - endContainer = parent; - length = endContainer.childNodes.length; - } - // Skip empty text nodes and
          s. - while ( endOffset < length && - ( child = endContainer.childNodes[ endOffset ] ) && - ( child.data === '' || child.nodeName === 'BR' ) ) { - endOffset += 1; - } - return endOffset === length; -}; - -var expandRangeToBlockBoundaries = function ( range ) { - var start = getStartBlockOfRange( range ), - end = getEndBlockOfRange( range ), - parent; - - if ( start && end ) { - parent = start.parentNode; - range.setStart( parent, indexOf.call( parent.childNodes, start ) ); - parent = end.parentNode; - range.setEnd( parent, indexOf.call( parent.childNodes, end ) + 1 ); - } -}; diff --git a/source/Squire-UI.css b/source/Squire-UI.css deleted file mode 100644 index 1ca2907..0000000 --- a/source/Squire-UI.css +++ /dev/null @@ -1,148 +0,0 @@ -body { - margin: 0; - font-family: 'Lato', sans-serif !important; -} - -.header { - padding: 50px 0 30px; - color: #fff; - text-align: center; - background: #1d193d; - margin-bottom: 20px; -} - -.header h1 { - font-size: 8em; - line-height: 1em; - font-weight: 900; -} - -.header h2 { - margin-bottom: 1em; - font-size: 3em; - font-weight: 300; - text-transform: lowercase; - color: #afaedf; -} - - .col-centered{ - float: none; - margin: 0 auto; - width: 80%; - } - -.alignCenter { - text-align: center; -} - -iframe { - width: 100%; - border: 1px #919191 solid; - border-radius: 4px; - -webkit-border-radius: 4px; - padding: 7px 8px; - color: #333; - background-color: #fff; - background-repeat: no-repeat; - background-position: right center; - border: 1px solid #ccc; - border-radius: 3px; - outline: none; - box-shadow: inset 0 1px 2px rgba(0,0,0,0.075); -} - -.menu .item { - color:#000; - float:left; - background:#FFF; - padding:10px; - border-left:1px #EEE solid; - -webkit-font-smoothing:subpixel-antialiased -} - -.menu .group { - border-radius:3px; - display:inline-block; - border:1px #EEE solid; - margin:5px -} - -.menu .group .item .flip { - -ms-transform:rotateY(180deg); - -webkit-transform:rotateY(180deg); - -moz-transform:rotateY(180deg); - transform:rotateY(180deg) -} - -.btn { - background: #516066; - display: block; - position: relative; - padding: 10px 15px; - margin-top: 10px; - text-transform: uppercase; - font-size: 11px; - font-weight: 500; - color: #fff; - text-align: center; - overflow: hidden; - letter-spacing: 1px; - border-radius: 4px; -} - -input[type=text] { - background-color: #fff; - vertical-align: middle; - max-width: 100%; - border: 1px solid #a8afb2; - border-color: #a8afb2 #d4d7d9 #d4d7d9; - color: #516066; - -webkit-box-sizing: border-box; - box-sizing: border-box; - -webkit-transition: border linear 150ms; - -moz-transition: border linear 150ms; - -o-transition: border linear 150ms; - transition: border linear 150ms; - font-size: 14px; - padding: 5px; - width: 100%; -} - -.menu .group .item:hover, .menu .item:first-child:hover { - border-left: 3px #55ACEE solid; -} - -.menu .item:first-child { - border-left:none; -} -.menu { - text-align: center; - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} -.drop { - border: 1px solid #dbdbdb; - padding: 14px; - background: white; - box-shadow: 0 1px 0 rgba(255,255,255,0),0 0 10px rgba(0,0,0,0.1); - -webkit-border-radius: 4px; - border-radius: 4px; - margin-top: 5px; -} - -.hidden { - display: none; -} - -.quit { - float: right; - top:0; - right:0; - margin-bottom: 5px; -} - - \ No newline at end of file diff --git a/source/Squire-UI.html b/source/Squire-UI.html deleted file mode 100644 index 8956790..0000000 --- a/source/Squire-UI.html +++ /dev/null @@ -1,67 +0,0 @@ - - diff --git a/source/Squire-UI.js b/source/Squire-UI.js deleted file mode 100644 index 17fff00..0000000 --- a/source/Squire-UI.js +++ /dev/null @@ -1,167 +0,0 @@ -$(document).ready(function() { - Squire.prototype.testPresenceinSelection = function(name, action, format, - validation) { - var path = this.getPath(), - test = (validation.test(path) | this.hasFormat(format)); - if (name == action && test) { - return true; - } else { - return false; - } - }; - SquireUI = function(options) { - if (typeof options.buildPath == "undefined") { - options.buildPath = 'build/'; - } - // Create instance of iFrame - var container, editor; - if (options.replace) { - container = $(options.replace).parent(); - $(options.replace).remove(); - } else if (options.div) { - container = $(options.div); - } else { - throw new Error( - "No element was defined for the editor to inject to."); - } - var iframe = document.createElement('iframe'); - var div = document.createElement('div'); - div.className = 'Squire-UI'; - iframe.height = options.height; - - $(div).load(options.buildPath + 'Squire-UI.html', function() { - this.linkDrop = new Drop({ - target: $('#makeLink').first()[0], - content: $('#drop-link').html(), - position: 'bottom center', - openOn: 'click' - }); - - this.linkDrop.on('open', function () { - $('.quit').click(function () { - $(this).parent().parent().removeClass('drop-open'); - }); - - $('.submitLink').click(function () { - var editor = iframe.contentWindow.editor; - editor.makeLink($(this).parent().children('#url').first().val()); - $(this).parent().parent().removeClass('drop-open'); - $(this).parent().children('#url').attr('value', ''); - }); - }); - - this.imageDrop = new Drop({ - target: $('#insertImage').first()[0], - content: $('#drop-image').html(), - position: 'bottom center', - openOn: 'click' - }); - - this.imageDrop.on('open', function () { - $('.quit').unbind().click(function () { - $(this).parent().parent().removeClass('drop-open'); - }); - - $('.sumbitImageURL').unbind().click(function () { - console.log("Passed through .sumbitImageURL"); - var editor = iframe.contentWindow.editor; - url = $(this).parent().children('#imageUrl').first()[0]; - editor.insertImage(url.value); - $(this).parent().parent().removeClass('drop-open'); - $(this).parent().children('#imageUrl').attr('value', ''); - }); - - }); - - this.fontDrop = new Drop({ - target: $('#selectFont').first()[0], - content: $('#drop-font').html(), - position: 'bottom center', - openOn: 'click' - }); - - this.fontDrop.on('open', function () { - $('.quit').click(function () { - $(this).parent().parent().removeClass('drop-open'); - }); - - $('.submitFont').unbind().click(function () { - var editor = iframe.contentWindow.editor; - var selectedFonts = $('select#fontSelect option:selected').last().data('fonts'); - var fontSize = $('select#textSelector option:selected').last().data('size') + 'px'; - editor.setFontSize(fontSize); - - try { - editor.setFontFace(selectedFonts); - } catch (e) { - alert('Please make a selection of text.'); - } finally { - $(this).parent().parent().removeClass('drop-open'); - } - - }); - - - }); - - $('.item').click(function() { - var iframe = $(this).parents('.Squire-UI').next('iframe').first()[0]; - var editor = iframe.contentWindow.editor; - var action = $(this).data('action'); - - test = { - value: $(this).data('action'), - testBold: editor.testPresenceinSelection('bold', - action, 'B', (/>B\b/)), - testItalic: editor.testPresenceinSelection('italic', - action, 'I', (/>I\b/)), - testUnderline: editor.testPresenceinSelection( - 'underline', action, 'U', (/>U\b/)), - testOrderedList: editor.testPresenceinSelection( - 'makeOrderedList', action, 'OL', (/>OL\b/)), - testLink: editor.testPresenceinSelection('makeLink', - action, 'A', (/>A\b/)), - testQuote: editor.testPresenceinSelection( - 'increaseQuoteLevel', action, 'blockquote', ( - />blockquote\b/)), - isNotValue: function (a) {return (a == action && this.value !== ''); } - }; - - editor.alignRight = function () { editor.setTextAlignment('right'); }; - editor.alignCenter = function () { editor.setTextAlignment('center'); }; - editor.alignLeft = function () { editor.setTextAlignment('left'); }; - editor.alignJustify = function () { editor.setTextAlignment('justify'); }; - editor.makeHeading = function () { editor.setFontSize('2em'); editor.bold(); }; - - if (test.testBold | test.testItalic | test.testUnderline | test.testOrderedList | test.testLink | test.testQuote) { - if (test.testBold) editor.removeBold(); - if (test.testItalic) editor.removeItalic(); - if (test.testUnderline) editor.removeUnderline(); - if (test.testLink) editor.removeLink(); - if (test.testOrderedList) editor.removeList(); - if (test.testQuote) editor.decreaseQuoteLevel(); - } else if (test.isNotValue('makeLink') | test.isNotValue('insertImage') | test.isNotValue('selectFont')) { - // do nothing these are dropdowns. - } else { - if (editor.getSelectedText() === '' && !(action == 'insertImage' || action == 'makeOrderedList' || action == 'increaseQuoteLevel' || action == 'redo' || action == 'undo')) return; - editor[action](); - } - }); - }); - - $(container).append(div); - $(container).append(iframe); - - var style = document.createElement('style'); - style.innerHTML = 'blockquote { border-left: 3px green solid; padding-left: 5px; }'; - - - iframe.contentWindow.editor = new Squire(iframe.contentWindow.document); - iframe.addEventListener('load', function() { - iframe.contentWindow.editor = new Squire(iframe.contentWindow.document); - }); - - iframe.contentWindow.document.head.appendChild(style); - return iframe.contentWindow.editor; - }; -}); \ No newline at end of file diff --git a/source/TreeWalker.js b/source/TreeWalker.js deleted file mode 100644 index 8bf7ffc..0000000 --- a/source/TreeWalker.js +++ /dev/null @@ -1,91 +0,0 @@ -/*global FILTER_ACCEPT */ -/*jshint strict:false */ - -/* - Native TreeWalker is buggy in IE and Opera: - * IE9/10 sometimes throw errors when calling TreeWalker#nextNode or - TreeWalker#previousNode. No way to feature detect this. - * Some versions of Opera have a bug in TreeWalker#previousNode which makes - it skip to the wrong node. - - Rather than risk further bugs, it's easiest just to implement our own - (subset) of the spec in all browsers. -*/ - -var typeToBitArray = { - // ELEMENT_NODE - 1: 1, - // ATTRIBUTE_NODE - 2: 2, - // TEXT_NODE - 3: 4, - // COMMENT_NODE - 8: 128, - // DOCUMENT_NODE - 9: 256, - // DOCUMENT_FRAGMENT_NODE - 11: 1024 -}; - -function TreeWalker ( root, nodeType, filter ) { - this.root = this.currentNode = root; - this.nodeType = nodeType; - this.filter = filter; -} - -TreeWalker.prototype.nextNode = function () { - var current = this.currentNode, - root = this.root, - nodeType = this.nodeType, - filter = this.filter, - node; - while ( true ) { - node = current.firstChild; - while ( !node && current ) { - if ( current === root ) { - break; - } - node = current.nextSibling; - if ( !node ) { current = current.parentNode; } - } - if ( !node ) { - return null; - } - if ( ( typeToBitArray[ node.nodeType ] & nodeType ) && - filter( node ) === FILTER_ACCEPT ) { - this.currentNode = node; - return node; - } - current = node; - } -}; - -TreeWalker.prototype.previousNode = function () { - var current = this.currentNode, - root = this.root, - nodeType = this.nodeType, - filter = this.filter, - node; - while ( true ) { - if ( current === root ) { - return null; - } - node = current.previousSibling; - if ( node ) { - while ( current = node.lastChild ) { - node = current; - } - } else { - node = current.parentNode; - } - if ( !node ) { - return null; - } - if ( ( typeToBitArray[ node.nodeType ] & nodeType ) && - filter( node ) === FILTER_ACCEPT ) { - this.currentNode = node; - return node; - } - current = node; - } -}; diff --git a/source/document.html b/source/document.html deleted file mode 100644 index 428e909..0000000 --- a/source/document.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/source/ie8dom.js b/source/ie8dom.js deleted file mode 100644 index 0b6fd9e..0000000 --- a/source/ie8dom.js +++ /dev/null @@ -1,157 +0,0 @@ -/* Copyright © 2011-2012 by Neil Jenkins. Licensed under the MIT license. */ - -( function () { - -/*global window, document, Element, HTMLDocument */ -/*jshint strict: false */ - -var doc = document; - -// Add JS hook -window.ie = 8; - -// Add defaultView property to document -doc.defaultView = window; - -// Fake W3C events support -var translate = { - focus: 'focusin', - blur: 'focusout' -}; - -var returnTrue = function () { return true; }; -var returnFalse = function () { return false; }; - -var toCopy = 'altKey ctrlKey metaKey shiftKey clientX clientY charCode keyCode'.split( ' ' ); - -var DOMEvent = function ( event ) { - var type = event.type, - doc = document, - target = event.srcElement || doc, - html = ( target.ownerDocument || doc ).documentElement, - l = toCopy.length, - property; - - while ( l-- ) { - property = toCopy[l]; - this[ property ] = event[ property ]; - } - - if ( type === 'propertychange' ) { - type = ( target.nodeName === 'INPUT' && - target.type !== 'text' && target.type !== 'password' ) ? - 'change' : 'input'; - } - - this.type = Object.keyOf( translate, type ) || type; - this.target = target; - this.pageX = event.clientX + html.scrollLeft; - this.pageY = event.clientY + html.scrollTop; - - if ( event.button ) { - this.button = ( event.button & 4 ? 1 : - ( event.button & 2 ? 2 : 0 ) ); - this.which = this.button + 1; - } - - this.relatedTarget = event.fromElement === target ? - event.toElement : event.fromElement; - this._event = event; -}; - -DOMEvent.prototype = { - constructor: DOMEvent, - isEvent: true, - preventDefault: function () { - this.isDefaultPrevented = returnTrue; - this._event.returnValue = false; - }, - stopPropagation: function () { - this.isPropagationStopped = returnTrue; - this._event.cancelBubble = true; - }, - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse -}; - -// Add W3C event add/remove methods to elements and document. -[ doc, Element.prototype ].forEach( - function ( dom ) { - dom.addEventListener = function ( type, handler, capture ) { - var fn = handler._ie_handleEvent || ( handler._ie_handleEvent = - function () { - var event = new DOMEvent( window.event ); - if ( typeof handler === 'object' ) { - handler.handleEvent( event ); - } else { - handler.call( this, event ); - } - } - ), - node = /paste|cut/.test( type ) ? this.body || this : this; - - handler._ie_registeredCount = ( handler._ie_registeredCount || 0 ) + 1; - - node.attachEvent( 'on' + ( translate[ type ] || type ), fn ); - }; - dom.addEventListener.isFake = true; - - dom.removeEventListener = function ( type, handler, capture ) { - var fn = handler._ie_handleEvent, - node = /paste|cut/.test( type ) ? this.body || this : this; - if ( !( handler._ie_registeredCount -= 1 ) ) { - delete handler._ie_handleEvent; - } - if ( fn ) { - node.detachEvent( 'on' + ( translate[ type ] || type ), fn ); - } - }; - dom.removeEventListener.isFake = true; -}); - -// The events that we normally attach to the window object, IE8 wants on the -// body. -doc.defaultView.addEventListener = function ( type, handler, capture ) { - return doc.addEventListener( type, handler, capture ); -}; - -// Add textContent property to elements. -Object.defineProperty( Element.prototype, 'textContent', { - get: function () { - return this.innerText; - }, - - set: function ( text ) { - this.innerText = text; - } -}); - -// Add compareDocumentPosition method to elements. -Element.prototype.compareDocumentPosition = function ( b ) { - if ( b.nodeType !== 1 ) { b = b.parentNode; } - var a = this, - different = ( a !== b ), - aIndex = a.sourceIndex, - bIndex = b.sourceIndex; - - return ( different && a.contains( b ) ? 16 : 0 ) + - ( different && b.contains( a ) ? 8 : 0 ) + - ( aIndex < bIndex ? 4 : 0 ) + - ( bIndex < aIndex ? 2 : 0 ); -}; - -// Add normalize method to document fragments -HTMLDocument.prototype.normalize = function () { - var children = this.childNodes, - l = children.length, - child; - - while ( l-- ) { - child = children[l]; - if ( child.nodeType === 1 ) { - child.normalize(); - } - } -}; - -}() ); diff --git a/source/ie8range.js b/source/ie8range.js deleted file mode 100644 index 1c54e52..0000000 --- a/source/ie8range.js +++ /dev/null @@ -1,487 +0,0 @@ -/* Copyright © 2011-2012 by Neil Jenkins. Licensed under the MIT license. - - IE TextRange <-> W3C Range code adapted from Rangy: - http://code.google.com/p/rangy/ - Copyright 2012, Tim Down. Licensed under the MIT license. -*/ - -var Range; - -( function () { - -/*global window, document */ -/*jshint strict: false */ - -var indexOf = Array.prototype.indexOf; - -var START_TO_START = 0; -var START_TO_END = 1; -var END_TO_START = 3; - -var contains = function ( a, b ) { - while ( b = b.parentNode ) { - if ( a === b ) { return true; } - } - return false; -}; - -var getCommonAncestor = function ( a, b ) { - var commonAncestor, - aParents, bParents, - aL, bL; - - if ( a === b || contains( a, b ) ) { - commonAncestor = a; - } else if ( contains( b, a ) ) { - commonAncestor = b; - } else { - aParents = []; - bParents = []; - while ( a = a.parentNode ) { - aParents.push( a ); - } - while ( b = b.parentNode ) { - bParents.push( b ); - } - aL = aParents.length; - bL = bParents.length; - while ( aL-- && bL-- ) { - if ( aParents[ aL ] !== bParents[ bL ] ) { - commonAncestor = aParents[ aL + 1 ]; - break; - } - } - if ( !commonAncestor ) { - commonAncestor = ( aL === -1 ? aParents[0] : bParents[0] ); - } - } - return commonAncestor; -}; - -Range = function ( startContainer, startOffset, endContainer, endOffset ) { - startContainer = startContainer || document; - startOffset = startOffset || 0; - - this.startContainer = startContainer; - this.startOffset = startOffset; - this.endContainer = endContainer || startContainer; - this.endOffset = endOffset !== undefined ? endOffset : startOffset; - this._updateCollapsedAndAncestor(); -}; - -Range.prototype = { - constructor: Range, - - _updateCollapsedAndAncestor: function () { - this.collapsed = ( - this.startContainer === this.endContainer && - this.startOffset === this.endOffset - ); - this.commonAncestorContainer = - getCommonAncestor( this.startContainer, this.endContainer ); - }, - setStart: function ( node, offset ) { - this.startContainer = node; - this.startOffset = offset; - this._updateCollapsedAndAncestor(); - }, - setEnd: function ( node, offset ) { - this.endContainer = node; - this.endOffset = offset; - this._updateCollapsedAndAncestor(); - }, - setStartAfter: function ( node ) { - var parent = node.parentNode; - this.setStart( parent, indexOf.call( parent.childNodes, node ) + 1 ); - }, - setEndBefore: function ( node ) { - var parent = node.parentNode; - this.setEnd( parent, indexOf.call( parent.childNodes, node ) ); - }, - selectNode: function ( node ) { - var parent = node.parentNode, - offset = indexOf.call( parent.childNodes, node ); - this.setStart( parent, offset ); - this.setEnd( parent, offset + 1 ); - }, - selectNodeContents: function ( node ) { - this.setStart( node, 0 ); - this.setEnd( node, node.childNodes.length ); - }, - cloneRange: function () { - return new Range( - this.startContainer, - this.startOffset, - this.endContainer, - this.endOffset - ); - }, - collapse: function ( toStart ) { - if ( toStart ) { - this.setEnd( this.startContainer, this.startOffset ); - } else { - this.setStart( this.endContainer, this.endOffset ); - } - }, - compareBoundaryPoints: function ( how, sourceRange ) { - var aContainer, aOffset, bContainer, bOffset, node, parent; - if ( how === START_TO_START || how === END_TO_START ) { - aContainer = this.startContainer; - aOffset = this.startOffset; - } else { - aContainer = this.endContainer; - aOffset = this.endOffset; - } - if ( how === START_TO_START || how === START_TO_END ) { - bContainer = sourceRange.startContainer; - bOffset = sourceRange.startOffset; - } else { - bContainer = sourceRange.endContainer; - bOffset = sourceRange.endOffset; - } - if ( aContainer === bContainer ) { - return aOffset < bOffset ? -1 : - aOffset > bOffset ? 1 : 0; - } - - node = aContainer; - while ( parent = node.parentNode ) { - if ( parent === bContainer ) { - return indexOf.call( parent.childNodes, node ) < bOffset ? - -1 : 1; - } - node = parent; - } - node = bContainer; - while ( parent = node.parentNode ) { - if ( parent === aContainer ) { - return indexOf.call( parent.childNodes, node ) < aOffset ? - 1 : -1; - } - node = parent; - } - if ( aContainer.nodeType !== 1 ) { - aContainer = aContainer.parentNode; - } - if ( bContainer.nodeType !== 1 ) { - bContainer = bContainer.parentNode; - } - return aContainer.sourceIndex < bContainer.sourceIndex ? -1 : - aContainer.sourceIndex > bContainer.sourceIndex ? 1 : 0; - } -}; - -document.createRange = function () { - return new Range(); -}; - -// --- - -var isAncestorOf = function ( ancestor, descendant ) { - return ancestor === descendant || contains( ancestor, descendant ); -}; - -var isCharacterDataNode = function ( node ) { - var nodeType = node.nodeType; - // Text, CDataSection or Comment - return nodeType === 3 || nodeType === 4 || nodeType === 8; -}; - -var DomPosition = function ( node, offset ) { - this.node = node; - this.offset = offset; -}; - -var getTextRangeContainerElement = function ( textRange ) { - var parentEl = textRange.parentElement(), - range, startEl, endEl, startEndContainer; - - range = textRange.duplicate(); - range.collapse( true ); - startEl = range.parentElement(); - range = textRange.duplicate(); - range.collapse( false ); - endEl = range.parentElement(); - startEndContainer = ( startEl === endEl ) ? - startEl : getCommonAncestor( startEl, endEl ); - - return startEndContainer === parentEl ? - startEndContainer : getCommonAncestor( parentEl, startEndContainer ); -}; - -// Gets the boundary of a TextRange expressed as a node and an offset within -// that node. This function started out as an improved version of code found in -// Tim Cameron Ryan's IERange (http://code.google.com/p/ierange/) but has grown, -// fixing problems with line breaks in preformatted text, adding workaround for -// IE TextRange bugs, handling for inputs and images, plus optimizations. -var getTextRangeBoundaryPosition = function ( - textRange, wholeRangeContainerElement, isStart, isCollapsed ) { - var workingRange = textRange.duplicate(); - - workingRange.collapse( isStart ); - - var containerElement = workingRange.parentElement(); - - // Sometimes collapsing a TextRange that's at the start of a text node can - // move it into the previous node, so check for that TODO: Find out when. - // Workaround for wholeRangeContainerElement may break this - if ( !isAncestorOf( wholeRangeContainerElement, containerElement ) ) { - containerElement = wholeRangeContainerElement; - } - - // Deal with nodes that cannot "contain rich HTML markup". In practice, this - // means form inputs, images and similar. See - // http://msdn.microsoft.com/en-us/library/aa703950%28VS.85%29.aspx - if ( !containerElement.canHaveHTML ) { - return new DomPosition( - containerElement.parentNode, - indexOf.call( - containerElement.parentNode.childNodes, containerElement ) - ); - } - - var workingNode = document.createElement( 'span' ), - workingComparisonType = isStart ? 'StartToStart' : 'StartToEnd', - comparison, previousNode, nextNode, boundaryPosition, boundaryNode; - - // Move the working range through the container's children, starting at the - // end and working backwards, until the working range reaches or goes past - // the boundary we're interested in - do { - containerElement.insertBefore( - workingNode, workingNode.previousSibling ); - workingRange.moveToElementText( workingNode ); - comparison = - workingRange.compareEndPoints( workingComparisonType, textRange ); - } while ( comparison > 0 && workingNode.previousSibling ); - - // We've now reached or gone past the boundary of the text range we're - // interested in so have identified the node we want - boundaryNode = workingNode.nextSibling; - - if ( comparison === -1 && boundaryNode && - isCharacterDataNode( boundaryNode ) ) { - // This is a character data node (text, comment, cdata). The working - // range is collapsed at the start of the node containing the text - // range's boundary, so we move the end of the working range to the - // boundary point and measure the length of its text to get the - // boundary's offset within the node. - workingRange.setEndPoint( - isStart ? 'EndToStart' : 'EndToEnd', textRange ); - - var offset; - - if ( /[\r\n]/.test( boundaryNode.data ) || - /[\r\n]/.test( workingRange.text ) ) { - /* - For the particular case of a boundary within a text node containing - line breaks (within a
           element, for example), we need a
          -            slightly complicated approach to get the boundary's offset in IE.
          -            The facts:
          -
          -            - Each line break is represented as \r in the text node's
          -              data/nodeValue properties
          -            - Each line break is represented as \r\n in the TextRange's 'text'
          -              property
          -            - The 'text' property of the TextRange does not contain trailing
          -              line breaks
          -
          -            To get round the problem presented by the final fact above, we can
          -            use the fact that TextRange's moveStart() and moveEnd() methods
          -            return the actual number of characters moved, which is not
          -            necessarily the same as the number of characters it was instructed
          -            to move. The simplest approach is to use this to store the
          -            characters moved when moving both the start and end of the range to
          -            the start of the document body and subtracting the start offset from
          -            the end offset (the "move-negative-gazillion" method). However, this
          -            is extremely slow when the document is large and the range is near
          -            the end of it. Clearly doing the mirror image (i.e. moving the range
          -            boundaries to the end of the document) has the same problem.
          -
          -            Another approach that works is to use moveStart() to move the start
          -            boundary of the range up to the end boundary one character at a time
          -            and incrementing a counter with the value returned by the
          -            moveStart() call. However, the check for whether the start boundary
          -            has reached the end boundary is expensive, so this method is slow
          -            (although unlike "move-negative-gazillion" is largely unaffected by
          -            the location of the range within the document).
          -
          -            The method below is a hybrid of the two methods above. It uses the
          -            fact that a string containing the TextRange's 'text' property with
          -            each \r\n converted to a single \r character cannot be longer than
          -            the text of the TextRange, so the start of the range is moved that
          -            length initially and then a character at a time to make up for any
          -            trailing line breaks not contained in the 'text' property. This has
          -            good performance in most situations compared to the previous two
          -            methods.
          -            */
          -            var tempRange = workingRange.duplicate();
          -            var rangeLength = tempRange.text.replace( /\r\n/g, '\r' ).length;
          -
          -            offset = tempRange.moveStart( 'character', rangeLength);
          -            while ( ( comparison =
          -                    tempRange.compareEndPoints( 'StartToEnd', tempRange )
          -                    ) === -1 ) {
          -                offset += 1;
          -                tempRange.moveStart( 'character', 1 );
          -            }
          -        } else {
          -            offset = workingRange.text.length;
          -        }
          -        boundaryPosition = new DomPosition( boundaryNode, offset );
          -    }
          -    else {
          -        // If the boundary immediately follows a character data node and this is
          -        // the end boundary, we should favour a position within that, and
          -        // likewise for a start boundary preceding a character data node
          -        previousNode = ( isCollapsed || !isStart ) &&
          -            workingNode.previousSibling;
          -        nextNode = ( isCollapsed || isStart ) && workingNode.nextSibling;
          -
          -        if ( nextNode && isCharacterDataNode( nextNode ) ) {
          -            boundaryPosition = new DomPosition( nextNode, 0 );
          -        } else if ( previousNode && isCharacterDataNode( previousNode ) ) {
          -            // Strange bug: if we don't read the data property, the length
          -            // property is often returned incorrectly as 0. Don't ask me why.
          -            // Therefore get the length from the data property rather than
          -            // reading it directly from the node.
          -            boundaryPosition = new DomPosition(
          -                previousNode, previousNode.data.length );
          -        } else {
          -            boundaryPosition = new DomPosition(
          -                containerElement,
          -                indexOf.call( containerElement.childNodes, workingNode )
          -            );
          -        }
          -    }
          -
          -    // Clean up
          -    workingNode.parentNode.removeChild( workingNode );
          -
          -    return boundaryPosition;
          -};
          -
          -// Returns a TextRange representing the boundary of a TextRange expressed as a
          -// node and an offset within that node. This function started out as an
          -// optimized version of code found in Tim Cameron Ryan's IERange
          -// (http://code.google.com/p/ierange/)
          -var createBoundaryTextRange = function ( boundaryPosition, isStart ) {
          -    var boundaryNode, boundaryParent, boundaryOffset = boundaryPosition.offset;
          -    var doc = document;
          -    var workingNode, childNodes, workingRange = doc.body.createTextRange();
          -    var nodeIsDataNode = isCharacterDataNode( boundaryPosition.node );
          -
          -    if ( nodeIsDataNode ) {
          -        boundaryNode = boundaryPosition.node;
          -        boundaryParent = boundaryNode.parentNode;
          -    } else {
          -        childNodes = boundaryPosition.node.childNodes;
          -        boundaryNode = ( boundaryOffset < childNodes.length ) ?
          -            childNodes[ boundaryOffset ] : null;
          -        boundaryParent = boundaryPosition.node;
          -    }
          -
          -    // Position the range immediately before the node containing the boundary
          -    workingNode = doc.createElement( 'span' );
          -
          -    // Making the working element non-empty element persuades IE to consider the
          -    // TextRange boundary to be within the element rather than immediately
          -    // before or after it, which is what we want
          -    workingNode.innerHTML = '';
          -
          -    // insertBefore is supposed to work like appendChild if the second parameter
          -    // is null. However, a bug report for IERange suggests that it can crash the
          -    // browser: http://code.google.com/p/ierange/issues/detail?id=12
          -    if ( boundaryNode ) {
          -        boundaryParent.insertBefore( workingNode, boundaryNode );
          -    } else {
          -        boundaryParent.appendChild( workingNode );
          -    }
          -
          -    workingRange.moveToElementText( workingNode );
          -    workingRange.collapse( !isStart );
          -
          -    // Clean up
          -    boundaryParent.removeChild( workingNode );
          -
          -    // Move the working range to the text offset, if required
          -    if ( nodeIsDataNode ) {
          -        workingRange[ isStart ? 'moveStart' : 'moveEnd' ](
          -            'character', boundaryOffset );
          -    }
          -
          -    return workingRange;
          -};
          -
          -var toDOMRange = function ( textRange ) {
          -    var rangeContainerElement = getTextRangeContainerElement( textRange ),
          -        start, end;
          -
          -    if ( textRange.compareEndPoints( 'StartToEnd', textRange ) === 0 ) {
          -        start = end = getTextRangeBoundaryPosition(
          -            textRange, rangeContainerElement, true, true );
          -    } else {
          -        start = getTextRangeBoundaryPosition(
          -            textRange, rangeContainerElement, true, false );
          -        end = getTextRangeBoundaryPosition(
          -            textRange, rangeContainerElement, false, false );
          -    }
          -    return new Range(
          -        start.node,
          -        start.offset,
          -        end.node,
          -        end.offset
          -    );
          -};
          -
          -var toTextRange = function ( range ) {
          -    var textRange, startRange, endRange;
          -    if ( range.collapsed ) {
          -        textRange = createBoundaryTextRange(
          -            new DomPosition( range.startContainer, range.startOffset ), true);
          -    } else {
          -        startRange = createBoundaryTextRange(
          -            new DomPosition( range.startContainer, range.startOffset ), true);
          -        endRange = createBoundaryTextRange(
          -            new DomPosition( range.endContainer, range.endOffset ), false );
          -        textRange = document.body.createTextRange();
          -        textRange.setEndPoint( 'StartToStart', startRange);
          -        textRange.setEndPoint( 'EndToEnd', endRange);
          -    }
          -    return textRange;
          -};
          -
          -var selection = {
          -    rangeCount: 0,
          -    getRangeAt: function ( index ) {
          -        if ( index !== 0 ) { return undefined; }
          -        var sel = document.selection.createRange();
          -        // Check if we have a control range.
          -        if ( sel.add ) {
          -            var range = document.createRange();
          -            range.moveToElementText( sel.item( 0 ) );
          -            range.collapse( false );
          -            range.select();
          -            sel = range;
          -        }
          -        return toDOMRange( sel );
          -    },
          -    removeAllRanges: function () {},
          -    addRange: function ( range ) {
          -        toTextRange( range ).select();
          -    }
          -};
          -
          -document.attachEvent( 'onbeforeactivate', function () {
          -    selection.rangeCount = 1;
          -});
          -
          -document.attachEvent( 'ondeactivate', function () {
          -    selection.rangeCount = 0;
          -});
          -
          -window.getSelection = function () {
          -    return selection;
          -};
          -
          -}() );
          diff --git a/source/ie8types.js b/source/ie8types.js
          deleted file mode 100644
          index 7a3361c..0000000
          --- a/source/ie8types.js
          +++ /dev/null
          @@ -1,63 +0,0 @@
          -/* Copyright © 2011-2012 by Neil Jenkins. Licensed under the MIT license. */
          -
          -( function () {
          -
          -/*jshint strict: false */
          -
          -// Note: Does not inclue the `if ( i in this ) {}` check these function should
          -// have, as IE8 will return false if this[i] is undefined (at least if the array
          -// was defined with a literal, e.g. `[ undefined, undefined ]`).
          -
          -Array.prototype.indexOf = function ( item, from ) {
          -    var l = this.length;
          -    for ( var i = ( from < 0 ) ? Math.max( 0, l + from ) : from || 0;
          -            i < l; i += 1 ) {
          -        if ( this[i] === item ) {
          -            return i;
          -        }
          -    }
          -    return -1;
          -};
          -
          -Array.prototype.forEach = function ( fn, bind ) {
          -    var l = this.length >>> 0;
          -    if ( typeof fn !== 'function' ) {
          -        throw new TypeError();
          -    }
          -    for ( var i = 0; i < l; i += 1 ) {
          -        fn.call( bind, this[i], i, this );
          -    }
          -};
          -
          -Array.prototype.filter = function ( fn, bind ) {
          -    var results = [];
          -    for ( var i = 0, l = this.length; i < l; i += 1 ) {
          -        var value = this[i];
          -        if ( fn.call( bind, value, i, this ) ) {
          -            results.push( value );
          -        }
          -    }
          -    return results;
          -};
          -
          -Object.keyOf = function ( object, value ) {
          -    for ( var key in object ) {
          -        if ( object[ key ] === value ) {
          -            return key;
          -        }
          -    }
          -};
          -
          -Date.now = function () {
          -    return +( new Date() );
          -};
          -
          -String.prototype.trim = function () {
          -    var str = this.replace( /^\s\s*/, '' ),
          -        ws = /\s/,
          -        i = str.length;
          -    while ( ws.test( str.charAt( i -= 1 ) ) ) {/* Empty! */}
          -    return str.slice( 0, i + 1 );
          -};
          -
          -}() );
          diff --git a/source/intro.js b/source/intro.js
          deleted file mode 100644
          index f14b523..0000000
          --- a/source/intro.js
          +++ /dev/null
          @@ -1,5 +0,0 @@
          -/* Copyright © 2011-2013 by Neil Jenkins. MIT Licensed. */
          -
          -( function ( doc, undefined ) {
          -
          -"use strict";
          diff --git a/source/outro.js b/source/outro.js
          deleted file mode 100644
          index a8f1a62..0000000
          --- a/source/outro.js
          +++ /dev/null
          @@ -1,13 +0,0 @@
          -/*global top, win, doc, Squire */
          -
          -if ( top !== win ) {
          -    win.editor = new Squire( doc );
          -    if ( win.onEditorLoad ) {
          -        win.onEditorLoad( win.editor );
          -        win.onEditorLoad = null;
          -    }
          -} else {
          -    win.Squire = Squire;
          -}
          -
          -}( document ) );