Paste should result in a clean Markdown code
* jquery.htmlClean.min.js: * in order to paste from any(?) document * rangy-selectionsaverestore.js: required by hallo html cleanup * App.master: * A link to awesome fonts * a hint, to engage to invoice :-) * rangy-core.js: using the bower installed one * to-markdown.js: using the new code, came from the `npm start` process * Edit.aspx: fixes copy/paste from LibreOffice writter colored text. * Web.csproj: using new javascript references to: rangy-core, rangy-selectionsaverestore jquery.htmlClean * mdd_help.htm: * mdd_styles.css: * rangyinputs-jquery-1.1.2.js: obsoletemain
parent
6bf32bf3c5
commit
f4329d6176
File diff suppressed because one or more lines are too long
@ -1,168 +0,0 @@
|
|||||||
<div class="panel">
|
|
||||||
<h2>Markdown Formatting</h2>
|
|
||||||
<p><a href="http://daringfireball.net/projects/markdown/" target="_blank">Markdown</a> turns plain text formatting into fancy HTML formatting.</p>
|
|
||||||
<h3>Phrase Emphasis</h3>
|
|
||||||
<pre><code>*italic* **bold**
|
|
||||||
_italic_ __bold__
|
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
<h3>Links</h3>
|
|
||||||
<p>Inline:</p>
|
|
||||||
<pre><code>An [example](http://url.com/ "Title")
|
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
<p>Reference-style labels (titles are optional):</p>
|
|
||||||
<pre><code>An [example][id]. Then, anywhere
|
|
||||||
else in the doc, define the link:
|
|
||||||
|
|
||||||
[id]: http://example.com/ "Title"
|
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
<h3>Images</h3>
|
|
||||||
<p>Inline (titles are optional):</p>
|
|
||||||
<pre><code>
|
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
<p>Reference-style:</p>
|
|
||||||
<pre><code>![alt text][id]
|
|
||||||
|
|
||||||
[id]: /url/to/img.jpg "Title"
|
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
<h3>Headers</h3>
|
|
||||||
<p>Setext-style:</p>
|
|
||||||
<pre><code>Header 1
|
|
||||||
========
|
|
||||||
|
|
||||||
Header 2
|
|
||||||
--------
|
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
<p>atx-style (closing #'s are optional):</p>
|
|
||||||
<pre><code># Header 1 #
|
|
||||||
|
|
||||||
## Header 2 ##
|
|
||||||
|
|
||||||
###### Header 6
|
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
<h3>Lists</h3>
|
|
||||||
<p>Ordered, without paragraphs:</p>
|
|
||||||
<pre><code>1. Foo
|
|
||||||
2. Bar
|
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
<p>Unordered, with paragraphs:</p>
|
|
||||||
<pre><code>* A list item.
|
|
||||||
|
|
||||||
With multiple paragraphs.
|
|
||||||
|
|
||||||
* Bar
|
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
<p>You can nest them:</p>
|
|
||||||
<pre><code>* Abacus
|
|
||||||
* answer
|
|
||||||
* Bubbles
|
|
||||||
1. bunk
|
|
||||||
2. bupkis
|
|
||||||
* BELITTLER
|
|
||||||
3. burper
|
|
||||||
* Cunning
|
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
<h3>Blockquotes</h3>
|
|
||||||
<pre><code>> Email-style angle brackets
|
|
||||||
> are used for blockquotes.
|
|
||||||
|
|
||||||
> > And, they can be nested.
|
|
||||||
|
|
||||||
> #### Headers in blockquotes
|
|
||||||
>
|
|
||||||
> * You can quote a list.
|
|
||||||
> * Etc.
|
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
<h3>Code Spans</h3>
|
|
||||||
<pre><code>`<code>` spans are delimited
|
|
||||||
by backticks.
|
|
||||||
|
|
||||||
You can include literal backticks
|
|
||||||
like `` `this` ``.
|
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
<h3>Preformatted Code Blocks</h3>
|
|
||||||
<p>Indent every line of a code block by at least 4 spaces or 1 tab.</p>
|
|
||||||
<pre><code>This is a normal paragraph.
|
|
||||||
|
|
||||||
This is a preformatted
|
|
||||||
code block.
|
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
<h3>Horizontal Rules</h3>
|
|
||||||
<p>Three or more dashes or asterisks:</p>
|
|
||||||
<pre><code>---
|
|
||||||
|
|
||||||
* * *
|
|
||||||
|
|
||||||
- - - -
|
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
<h3>Manual Line Breaks</h3>
|
|
||||||
<p>End a line with two or more spaces:</p>
|
|
||||||
<pre><code>Roses are red,
|
|
||||||
Violets are blue.
|
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
<h2>Extra Mode</h2>
|
|
||||||
|
|
||||||
These formatting features are only available when Extra Mode is enabled.
|
|
||||||
|
|
||||||
<h3>Markdown In Html</h3>
|
|
||||||
<p>Enable markdown in HTML block level elements:</p>
|
|
||||||
<pre><code><div markdown="1">
|
|
||||||
Markdown **still** works.
|
|
||||||
</div>
|
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
<h3>Fenced Code Blocks</h3>
|
|
||||||
<p>Code blocks delimited by 3 or more tildas:</p>
|
|
||||||
<pre><code>~~~
|
|
||||||
This is a preformatted
|
|
||||||
code block
|
|
||||||
~~~
|
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
<h3>Header IDs</h3>
|
|
||||||
<p>Set the id of headings with <code>{#<id>}</code> at end of heading line:</p>
|
|
||||||
<pre><code>## My Heading {#myheading}
|
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
<h3>Tables</h3>
|
|
||||||
|
|
||||||
<pre><code>Fruit |Color
|
|
||||||
---------|----------
|
|
||||||
Apples |Red
|
|
||||||
Pears |Green
|
|
||||||
Bananas |Yellow</code></pre>
|
|
||||||
<h3>Definition Lists</h3>
|
|
||||||
<pre><code>Term 1
|
|
||||||
: Definition 1
|
|
||||||
|
|
||||||
Term 2
|
|
||||||
: Definition 2</code></pre>
|
|
||||||
|
|
||||||
<h3>Footnotes</h3>
|
|
||||||
<pre><code>Body text with a footnote [^1]
|
|
||||||
|
|
||||||
[^1]: Footnote text here
|
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
<h3>Abbreviations</h3>
|
|
||||||
<pre><code>MDD <- will have title
|
|
||||||
|
|
||||||
*[MDD]: MarkdownDeep
|
|
||||||
</code></pre>
|
|
||||||
<p> </p>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
@ -1,211 +0,0 @@
|
|||||||
div.mdd_modal
|
|
||||||
{
|
|
||||||
position:absolute;
|
|
||||||
top:0;
|
|
||||||
left:0;
|
|
||||||
padding:0;
|
|
||||||
margin:0;
|
|
||||||
width:100%;
|
|
||||||
height:100%;
|
|
||||||
z-index:1000;
|
|
||||||
display:none;
|
|
||||||
font-size:10pt;
|
|
||||||
background-image:url(mdd_modal_background.png);
|
|
||||||
}
|
|
||||||
div.mdd_modal_frame
|
|
||||||
{
|
|
||||||
background-color:black;
|
|
||||||
color:white;
|
|
||||||
z-index:2000;
|
|
||||||
margin:0 auto;
|
|
||||||
margin-top:60px;
|
|
||||||
border:solid 5px #808080;
|
|
||||||
position:relative;
|
|
||||||
border-radius:5px;
|
|
||||||
-moz-border-radius:5px;
|
|
||||||
-webkit-border-radius:5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.mdd_modal_button
|
|
||||||
{
|
|
||||||
position:absolute;
|
|
||||||
top:-33px;
|
|
||||||
right:-5px;
|
|
||||||
padding-left:10px;
|
|
||||||
padding-right:10px;
|
|
||||||
padding-top:4px;
|
|
||||||
padding-bottom:0px;
|
|
||||||
height:20px;
|
|
||||||
background-color:#202020;
|
|
||||||
z-index:1999;
|
|
||||||
border-radius:4px;
|
|
||||||
-moz-border-radius:4px;
|
|
||||||
-webkit-border-radius:4px;
|
|
||||||
line-height:1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.mdd_modal_button a
|
|
||||||
{
|
|
||||||
color:Yellow;
|
|
||||||
text-decoration:none;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.mdd_modal_button a:hover
|
|
||||||
{
|
|
||||||
color:Orange;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.mdd_modal_content
|
|
||||||
{
|
|
||||||
overflow:scroll;
|
|
||||||
overflow-x:hidden;
|
|
||||||
position:relative;
|
|
||||||
width:100%;
|
|
||||||
height:100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.mdd_ajax_loader
|
|
||||||
{
|
|
||||||
background-position: center center;
|
|
||||||
background-image: url(mdd_ajax_loader.gif);
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
width:100%;
|
|
||||||
height:200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.mdd_syntax
|
|
||||||
{
|
|
||||||
font-size:12pt;
|
|
||||||
padding:10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.mdd_syntax h2
|
|
||||||
{
|
|
||||||
font-size:14pt;
|
|
||||||
}
|
|
||||||
div.mdd_syntax h3
|
|
||||||
{
|
|
||||||
font-size:12pt;
|
|
||||||
}
|
|
||||||
div.mdd_syntax pre
|
|
||||||
{
|
|
||||||
font-size:10pt;
|
|
||||||
border:solid 1px silver;
|
|
||||||
padding:4px;
|
|
||||||
background-color:#282828;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.mdd_toolbar_wrap
|
|
||||||
{
|
|
||||||
width:100%;
|
|
||||||
}
|
|
||||||
div.mdd_toolbar
|
|
||||||
{
|
|
||||||
padding:5px;
|
|
||||||
height:20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.mdd_toolbar ul
|
|
||||||
{
|
|
||||||
margin:0;
|
|
||||||
padding:0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.mdd_toolbar li
|
|
||||||
{
|
|
||||||
float:left;
|
|
||||||
margin:0;
|
|
||||||
padding:0;
|
|
||||||
list-style:none;
|
|
||||||
background-color: #888;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.mdd_toolbar a.mdd_button
|
|
||||||
{
|
|
||||||
background-image:url(mdd_toolbar.png);
|
|
||||||
width:20px;
|
|
||||||
height:20px;
|
|
||||||
display:block;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.mdd_sep
|
|
||||||
{
|
|
||||||
width:5px;
|
|
||||||
height:20px;
|
|
||||||
display:block;
|
|
||||||
border-left:solid 1px #808080;
|
|
||||||
margin-left:5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#mdd_bold { background-position:-1px -1px;}
|
|
||||||
#mdd_bold:hover { background-position:-1px -23px; }
|
|
||||||
#mdd_italic { background-position:-23px -1px; }
|
|
||||||
#mdd_italic:hover { background-position:-23px -23px; }
|
|
||||||
#mdd_ullist { background-position:-177px -1px; }
|
|
||||||
#mdd_ullist:hover { background-position:-177px -23px; }
|
|
||||||
#mdd_ollist { background-position:-155px -1px; }
|
|
||||||
#mdd_ollist:hover { background-position:-155px -23px; }
|
|
||||||
#mdd_indent { background-position:-67px -1px; }
|
|
||||||
#mdd_indent:hover { background-position:-67px -23px; }
|
|
||||||
#mdd_outdent { background-position:-89px -1px; }
|
|
||||||
#mdd_outdent:hover { background-position:-89px -23px; }
|
|
||||||
#mdd_link { background-position:-45px -1px; }
|
|
||||||
#mdd_link:hover { background-position:-45px -23px; }
|
|
||||||
#mdd_img { background-position:-133px -1px; }
|
|
||||||
#mdd_img:hover { background-position:-133px -23px; }
|
|
||||||
#mdd_hr { background-position:-221px -1px; }
|
|
||||||
#mdd_hr:hover { background-position:-221px -23px; }
|
|
||||||
#mdd_code { background-position:-111px -1px; }
|
|
||||||
#mdd_code:hover { background-position:-111px -23px; }
|
|
||||||
#mdd_heading { background-position:-199px -1px; }
|
|
||||||
#mdd_heading:hover { background-position:-199px -23px; }
|
|
||||||
#mdd_undo { background-position:-243px -1px; }
|
|
||||||
#mdd_undo:hover { background-position:-243px -23px; }
|
|
||||||
#mdd_redo { background-position:-265px -1px; }
|
|
||||||
#mdd_redo:hover { background-position:-265px -23px; }
|
|
||||||
|
|
||||||
div.mdd_links
|
|
||||||
{
|
|
||||||
float:right;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.mdd_links a
|
|
||||||
{
|
|
||||||
text-decoration:none;
|
|
||||||
color:#a0a0a0;
|
|
||||||
font-size:smaller;
|
|
||||||
}
|
|
||||||
div.mdd_links a:hover
|
|
||||||
{
|
|
||||||
color:white;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.mdd_editor_wrap
|
|
||||||
{
|
|
||||||
padding-right: 8px;
|
|
||||||
}
|
|
||||||
textarea.mdd_editor
|
|
||||||
{
|
|
||||||
width:100%;
|
|
||||||
resize:none;
|
|
||||||
margin:0;padding: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.mdd_resizer_wrap
|
|
||||||
{
|
|
||||||
width:100%;
|
|
||||||
}
|
|
||||||
div.mdd_resizer
|
|
||||||
{
|
|
||||||
background:#181818;
|
|
||||||
background-image:url("mdd_gripper.png");
|
|
||||||
background-position:center center;
|
|
||||||
background-repeat:no-repeat;
|
|
||||||
padding-left:2px;
|
|
||||||
padding-right:2px;
|
|
||||||
height:9px;
|
|
||||||
border:solid 1px #303030;
|
|
||||||
margin-top:-1px;
|
|
||||||
cursor:n-resize;
|
|
||||||
}
|
|
||||||
|
|
||||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,252 @@
|
|||||||
|
/**
|
||||||
|
* Selection save and restore module for Rangy.
|
||||||
|
* Saves and restores user selections using marker invisible elements in the DOM.
|
||||||
|
*
|
||||||
|
* Part of Rangy, a cross-browser JavaScript range and selection library
|
||||||
|
* https://github.com/timdown/rangy
|
||||||
|
*
|
||||||
|
* Depends on Rangy core.
|
||||||
|
*
|
||||||
|
* Copyright 2015, Tim Down
|
||||||
|
* Licensed under the MIT license.
|
||||||
|
* Version: 1.3.0
|
||||||
|
* Build date: 10 May 2015
|
||||||
|
*/
|
||||||
|
(function(factory, root) {
|
||||||
|
if (typeof define == "function" && define.amd) {
|
||||||
|
// AMD. Register as an anonymous module with a dependency on Rangy.
|
||||||
|
define(["./rangy-core"], factory);
|
||||||
|
} else if (typeof module != "undefined" && typeof exports == "object") {
|
||||||
|
// Node/CommonJS style
|
||||||
|
module.exports = factory( require("rangy") );
|
||||||
|
} else {
|
||||||
|
// No AMD or CommonJS support so we use the rangy property of root (probably the global variable)
|
||||||
|
factory(root.rangy);
|
||||||
|
}
|
||||||
|
})(function(rangy) {
|
||||||
|
rangy.createModule("SaveRestore", ["WrappedRange"], function(api, module) {
|
||||||
|
var dom = api.dom;
|
||||||
|
var removeNode = dom.removeNode;
|
||||||
|
var isDirectionBackward = api.Selection.isDirectionBackward;
|
||||||
|
var markerTextChar = "\ufeff";
|
||||||
|
|
||||||
|
function gEBI(id, doc) {
|
||||||
|
return (doc || document).getElementById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function insertRangeBoundaryMarker(range, atStart) {
|
||||||
|
var markerId = "selectionBoundary_" + (+new Date()) + "_" + ("" + Math.random()).slice(2);
|
||||||
|
var markerEl;
|
||||||
|
var doc = dom.getDocument(range.startContainer);
|
||||||
|
|
||||||
|
// Clone the Range and collapse to the appropriate boundary point
|
||||||
|
var boundaryRange = range.cloneRange();
|
||||||
|
boundaryRange.collapse(atStart);
|
||||||
|
|
||||||
|
// Create the marker element containing a single invisible character using DOM methods and insert it
|
||||||
|
markerEl = doc.createElement("span");
|
||||||
|
markerEl.id = markerId;
|
||||||
|
markerEl.style.lineHeight = "0";
|
||||||
|
markerEl.style.display = "none";
|
||||||
|
markerEl.className = "rangySelectionBoundary";
|
||||||
|
markerEl.appendChild(doc.createTextNode(markerTextChar));
|
||||||
|
|
||||||
|
boundaryRange.insertNode(markerEl);
|
||||||
|
return markerEl;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setRangeBoundary(doc, range, markerId, atStart) {
|
||||||
|
var markerEl = gEBI(markerId, doc);
|
||||||
|
if (markerEl) {
|
||||||
|
range[atStart ? "setStartBefore" : "setEndBefore"](markerEl);
|
||||||
|
removeNode(markerEl);
|
||||||
|
} else {
|
||||||
|
module.warn("Marker element has been removed. Cannot restore selection.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareRanges(r1, r2) {
|
||||||
|
return r2.compareBoundaryPoints(r1.START_TO_START, r1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveRange(range, direction) {
|
||||||
|
var startEl, endEl, doc = api.DomRange.getRangeDocument(range), text = range.toString();
|
||||||
|
var backward = isDirectionBackward(direction);
|
||||||
|
|
||||||
|
if (range.collapsed) {
|
||||||
|
endEl = insertRangeBoundaryMarker(range, false);
|
||||||
|
return {
|
||||||
|
document: doc,
|
||||||
|
markerId: endEl.id,
|
||||||
|
collapsed: true
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
endEl = insertRangeBoundaryMarker(range, false);
|
||||||
|
startEl = insertRangeBoundaryMarker(range, true);
|
||||||
|
|
||||||
|
return {
|
||||||
|
document: doc,
|
||||||
|
startMarkerId: startEl.id,
|
||||||
|
endMarkerId: endEl.id,
|
||||||
|
collapsed: false,
|
||||||
|
backward: backward,
|
||||||
|
toString: function() {
|
||||||
|
return "original text: '" + text + "', new text: '" + range.toString() + "'";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreRange(rangeInfo, normalize) {
|
||||||
|
var doc = rangeInfo.document;
|
||||||
|
if (typeof normalize == "undefined") {
|
||||||
|
normalize = true;
|
||||||
|
}
|
||||||
|
var range = api.createRange(doc);
|
||||||
|
if (rangeInfo.collapsed) {
|
||||||
|
var markerEl = gEBI(rangeInfo.markerId, doc);
|
||||||
|
if (markerEl) {
|
||||||
|
markerEl.style.display = "inline";
|
||||||
|
var previousNode = markerEl.previousSibling;
|
||||||
|
|
||||||
|
// Workaround for issue 17
|
||||||
|
if (previousNode && previousNode.nodeType == 3) {
|
||||||
|
removeNode(markerEl);
|
||||||
|
range.collapseToPoint(previousNode, previousNode.length);
|
||||||
|
} else {
|
||||||
|
range.collapseBefore(markerEl);
|
||||||
|
removeNode(markerEl);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
module.warn("Marker element has been removed. Cannot restore selection.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setRangeBoundary(doc, range, rangeInfo.startMarkerId, true);
|
||||||
|
setRangeBoundary(doc, range, rangeInfo.endMarkerId, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalize) {
|
||||||
|
range.normalizeBoundaries();
|
||||||
|
}
|
||||||
|
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveRanges(ranges, direction) {
|
||||||
|
var rangeInfos = [], range, doc;
|
||||||
|
var backward = isDirectionBackward(direction);
|
||||||
|
|
||||||
|
// Order the ranges by position within the DOM, latest first, cloning the array to leave the original untouched
|
||||||
|
ranges = ranges.slice(0);
|
||||||
|
ranges.sort(compareRanges);
|
||||||
|
|
||||||
|
for (var i = 0, len = ranges.length; i < len; ++i) {
|
||||||
|
rangeInfos[i] = saveRange(ranges[i], backward);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that all the markers are in place and DOM manipulation over, adjust each range's boundaries to lie
|
||||||
|
// between its markers
|
||||||
|
for (i = len - 1; i >= 0; --i) {
|
||||||
|
range = ranges[i];
|
||||||
|
doc = api.DomRange.getRangeDocument(range);
|
||||||
|
if (range.collapsed) {
|
||||||
|
range.collapseAfter(gEBI(rangeInfos[i].markerId, doc));
|
||||||
|
} else {
|
||||||
|
range.setEndBefore(gEBI(rangeInfos[i].endMarkerId, doc));
|
||||||
|
range.setStartAfter(gEBI(rangeInfos[i].startMarkerId, doc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rangeInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveSelection(win) {
|
||||||
|
if (!api.isSelectionValid(win)) {
|
||||||
|
module.warn("Cannot save selection. This usually happens when the selection is collapsed and the selection document has lost focus.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var sel = api.getSelection(win);
|
||||||
|
var ranges = sel.getAllRanges();
|
||||||
|
var backward = (ranges.length == 1 && sel.isBackward());
|
||||||
|
|
||||||
|
var rangeInfos = saveRanges(ranges, backward);
|
||||||
|
|
||||||
|
// Ensure current selection is unaffected
|
||||||
|
if (backward) {
|
||||||
|
sel.setSingleRange(ranges[0], backward);
|
||||||
|
} else {
|
||||||
|
sel.setRanges(ranges);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
win: win,
|
||||||
|
rangeInfos: rangeInfos,
|
||||||
|
restored: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreRanges(rangeInfos) {
|
||||||
|
var ranges = [];
|
||||||
|
|
||||||
|
// Ranges are in reverse order of appearance in the DOM. We want to restore earliest first to avoid
|
||||||
|
// normalization affecting previously restored ranges.
|
||||||
|
var rangeCount = rangeInfos.length;
|
||||||
|
|
||||||
|
for (var i = rangeCount - 1; i >= 0; i--) {
|
||||||
|
ranges[i] = restoreRange(rangeInfos[i], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ranges;
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreSelection(savedSelection, preserveDirection) {
|
||||||
|
if (!savedSelection.restored) {
|
||||||
|
var rangeInfos = savedSelection.rangeInfos;
|
||||||
|
var sel = api.getSelection(savedSelection.win);
|
||||||
|
var ranges = restoreRanges(rangeInfos), rangeCount = rangeInfos.length;
|
||||||
|
|
||||||
|
if (rangeCount == 1 && preserveDirection && api.features.selectionHasExtend && rangeInfos[0].backward) {
|
||||||
|
sel.removeAllRanges();
|
||||||
|
sel.addRange(ranges[0], true);
|
||||||
|
} else {
|
||||||
|
sel.setRanges(ranges);
|
||||||
|
}
|
||||||
|
|
||||||
|
savedSelection.restored = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeMarkerElement(doc, markerId) {
|
||||||
|
var markerEl = gEBI(markerId, doc);
|
||||||
|
if (markerEl) {
|
||||||
|
removeNode(markerEl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeMarkers(savedSelection) {
|
||||||
|
var rangeInfos = savedSelection.rangeInfos;
|
||||||
|
for (var i = 0, len = rangeInfos.length, rangeInfo; i < len; ++i) {
|
||||||
|
rangeInfo = rangeInfos[i];
|
||||||
|
if (rangeInfo.collapsed) {
|
||||||
|
removeMarkerElement(savedSelection.doc, rangeInfo.markerId);
|
||||||
|
} else {
|
||||||
|
removeMarkerElement(savedSelection.doc, rangeInfo.startMarkerId);
|
||||||
|
removeMarkerElement(savedSelection.doc, rangeInfo.endMarkerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
api.util.extend(api, {
|
||||||
|
saveRange: saveRange,
|
||||||
|
restoreRange: restoreRange,
|
||||||
|
saveRanges: saveRanges,
|
||||||
|
restoreRanges: restoreRanges,
|
||||||
|
saveSelection: saveSelection,
|
||||||
|
restoreSelection: restoreSelection,
|
||||||
|
removeMarkerElement: removeMarkerElement,
|
||||||
|
removeMarkers: removeMarkers
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return rangy;
|
||||||
|
}, this);
|
||||||
@ -1,274 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license Rangy Inputs, a jQuery plug-in for selection and caret manipulation within textareas and text inputs.
|
|
||||||
*
|
|
||||||
* https://github.com/timdown/rangyinputs
|
|
||||||
*
|
|
||||||
* For range and selection features for contenteditable, see Rangy.
|
|
||||||
|
|
||||||
* http://code.google.com/p/rangy/
|
|
||||||
*
|
|
||||||
* Depends on jQuery 1.0 or later.
|
|
||||||
*
|
|
||||||
* Copyright 2013, Tim Down
|
|
||||||
* Licensed under the MIT license.
|
|
||||||
* Version: 1.1.2
|
|
||||||
* Build date: 6 September 2013
|
|
||||||
*/
|
|
||||||
(function($) {
|
|
||||||
var UNDEF = "undefined";
|
|
||||||
var getSelection, setSelection, deleteSelectedText, deleteText, insertText;
|
|
||||||
var replaceSelectedText, surroundSelectedText, extractSelectedText, collapseSelection;
|
|
||||||
|
|
||||||
// Trio of isHost* functions taken from Peter Michaux's article:
|
|
||||||
// http://peter.michaux.ca/articles/feature-detection-state-of-the-art-browser-scripting
|
|
||||||
function isHostMethod(object, property) {
|
|
||||||
var t = typeof object[property];
|
|
||||||
return t === "function" || (!!(t == "object" && object[property])) || t == "unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
function isHostProperty(object, property) {
|
|
||||||
return typeof(object[property]) != UNDEF;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isHostObject(object, property) {
|
|
||||||
return !!(typeof(object[property]) == "object" && object[property]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function fail(reason) {
|
|
||||||
if (window.console && window.console.log) {
|
|
||||||
window.console.log("RangyInputs not supported in your browser. Reason: " + reason);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function adjustOffsets(el, start, end) {
|
|
||||||
if (start < 0) {
|
|
||||||
start += el.value.length;
|
|
||||||
}
|
|
||||||
if (typeof end == UNDEF) {
|
|
||||||
end = start;
|
|
||||||
}
|
|
||||||
if (end < 0) {
|
|
||||||
end += el.value.length;
|
|
||||||
}
|
|
||||||
return { start: start, end: end };
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeSelection(el, start, end) {
|
|
||||||
return {
|
|
||||||
start: start,
|
|
||||||
end: end,
|
|
||||||
length: end - start,
|
|
||||||
text: el.value.slice(start, end)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBody() {
|
|
||||||
return isHostObject(document, "body") ? document.body : document.getElementsByTagName("body")[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
|
||||||
var testTextArea = document.createElement("textarea");
|
|
||||||
|
|
||||||
getBody().appendChild(testTextArea);
|
|
||||||
|
|
||||||
if (isHostProperty(testTextArea, "selectionStart") && isHostProperty(testTextArea, "selectionEnd")) {
|
|
||||||
getSelection = function(el) {
|
|
||||||
var start = el.selectionStart, end = el.selectionEnd;
|
|
||||||
return makeSelection(el, start, end);
|
|
||||||
};
|
|
||||||
|
|
||||||
setSelection = function(el, startOffset, endOffset) {
|
|
||||||
var offsets = adjustOffsets(el, startOffset, endOffset);
|
|
||||||
el.selectionStart = offsets.start;
|
|
||||||
el.selectionEnd = offsets.end;
|
|
||||||
};
|
|
||||||
|
|
||||||
collapseSelection = function(el, toStart) {
|
|
||||||
if (toStart) {
|
|
||||||
el.selectionEnd = el.selectionStart;
|
|
||||||
} else {
|
|
||||||
el.selectionStart = el.selectionEnd;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else if (isHostMethod(testTextArea, "createTextRange") && isHostObject(document, "selection") &&
|
|
||||||
isHostMethod(document.selection, "createRange")) {
|
|
||||||
|
|
||||||
getSelection = function(el) {
|
|
||||||
var start = 0, end = 0, normalizedValue, textInputRange, len, endRange;
|
|
||||||
var range = document.selection.createRange();
|
|
||||||
|
|
||||||
if (range && range.parentElement() == el) {
|
|
||||||
len = el.value.length;
|
|
||||||
|
|
||||||
normalizedValue = el.value.replace(/\r\n/g, "\n");
|
|
||||||
textInputRange = el.createTextRange();
|
|
||||||
textInputRange.moveToBookmark(range.getBookmark());
|
|
||||||
endRange = el.createTextRange();
|
|
||||||
endRange.collapse(false);
|
|
||||||
if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
|
|
||||||
start = end = len;
|
|
||||||
} else {
|
|
||||||
start = -textInputRange.moveStart("character", -len);
|
|
||||||
start += normalizedValue.slice(0, start).split("\n").length - 1;
|
|
||||||
if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) {
|
|
||||||
end = len;
|
|
||||||
} else {
|
|
||||||
end = -textInputRange.moveEnd("character", -len);
|
|
||||||
end += normalizedValue.slice(0, end).split("\n").length - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return makeSelection(el, start, end);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Moving across a line break only counts as moving one character in a TextRange, whereas a line break in
|
|
||||||
// the textarea value is two characters. This function corrects for that by converting a text offset into a
|
|
||||||
// range character offset by subtracting one character for every line break in the textarea prior to the
|
|
||||||
// offset
|
|
||||||
var offsetToRangeCharacterMove = function(el, offset) {
|
|
||||||
return offset - (el.value.slice(0, offset).split("\r\n").length - 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
setSelection = function(el, startOffset, endOffset) {
|
|
||||||
var offsets = adjustOffsets(el, startOffset, endOffset);
|
|
||||||
var range = el.createTextRange();
|
|
||||||
var startCharMove = offsetToRangeCharacterMove(el, offsets.start);
|
|
||||||
range.collapse(true);
|
|
||||||
if (offsets.start == offsets.end) {
|
|
||||||
range.move("character", startCharMove);
|
|
||||||
} else {
|
|
||||||
range.moveEnd("character", offsetToRangeCharacterMove(el, offsets.end));
|
|
||||||
range.moveStart("character", startCharMove);
|
|
||||||
}
|
|
||||||
range.select();
|
|
||||||
};
|
|
||||||
|
|
||||||
collapseSelection = function(el, toStart) {
|
|
||||||
var range = document.selection.createRange();
|
|
||||||
range.collapse(toStart);
|
|
||||||
range.select();
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
getBody().removeChild(testTextArea);
|
|
||||||
fail("No means of finding text input caret position");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up
|
|
||||||
getBody().removeChild(testTextArea);
|
|
||||||
|
|
||||||
deleteText = function(el, start, end, moveSelection) {
|
|
||||||
var val;
|
|
||||||
if (start != end) {
|
|
||||||
val = el.value;
|
|
||||||
el.value = val.slice(0, start) + val.slice(end);
|
|
||||||
}
|
|
||||||
if (moveSelection) {
|
|
||||||
setSelection(el, start, start);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
deleteSelectedText = function(el) {
|
|
||||||
var sel = getSelection(el);
|
|
||||||
deleteText(el, sel.start, sel.end, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
extractSelectedText = function(el) {
|
|
||||||
var sel = getSelection(el), val;
|
|
||||||
if (sel.start != sel.end) {
|
|
||||||
val = el.value;
|
|
||||||
el.value = val.slice(0, sel.start) + val.slice(sel.end);
|
|
||||||
}
|
|
||||||
setSelection(el, sel.start, sel.start);
|
|
||||||
return sel.text;
|
|
||||||
};
|
|
||||||
|
|
||||||
var updateSelectionAfterInsert = function(el, startIndex, text, selectionBehaviour) {
|
|
||||||
var endIndex = startIndex + text.length;
|
|
||||||
|
|
||||||
selectionBehaviour = (typeof selectionBehaviour == "string") ?
|
|
||||||
selectionBehaviour.toLowerCase() : "";
|
|
||||||
|
|
||||||
if ((selectionBehaviour == "collapsetoend" || selectionBehaviour == "select") && /[\r\n]/.test(text)) {
|
|
||||||
// Find the length of the actual text inserted, which could vary
|
|
||||||
// depending on how the browser deals with line breaks
|
|
||||||
var normalizedText = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
||||||
endIndex = startIndex + normalizedText.length;
|
|
||||||
var firstLineBreakIndex = startIndex + normalizedText.indexOf("\n");
|
|
||||||
|
|
||||||
if (el.value.slice(firstLineBreakIndex, firstLineBreakIndex + 2) == "\r\n") {
|
|
||||||
// Browser uses \r\n, so we need to account for extra \r characters
|
|
||||||
endIndex += normalizedText.match(/\n/g).length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (selectionBehaviour) {
|
|
||||||
case "collapsetostart":
|
|
||||||
setSelection(el, startIndex, startIndex);
|
|
||||||
break;
|
|
||||||
case "collapsetoend":
|
|
||||||
setSelection(el, endIndex, endIndex);
|
|
||||||
break;
|
|
||||||
case "select":
|
|
||||||
setSelection(el, startIndex, endIndex);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
insertText = function(el, text, index, selectionBehaviour) {
|
|
||||||
var val = el.value;
|
|
||||||
el.value = val.slice(0, index) + text + val.slice(index);
|
|
||||||
if (typeof selectionBehaviour == "boolean") {
|
|
||||||
selectionBehaviour = selectionBehaviour ? "collapseToEnd" : "";
|
|
||||||
}
|
|
||||||
updateSelectionAfterInsert(el, index, text, selectionBehaviour);
|
|
||||||
};
|
|
||||||
|
|
||||||
replaceSelectedText = function(el, text, selectionBehaviour) {
|
|
||||||
var sel = getSelection(el), val = el.value;
|
|
||||||
el.value = val.slice(0, sel.start) + text + val.slice(sel.end);
|
|
||||||
updateSelectionAfterInsert(el, sel.start, text, selectionBehaviour || "collapseToEnd");
|
|
||||||
};
|
|
||||||
|
|
||||||
surroundSelectedText = function(el, before, after, selectionBehaviour) {
|
|
||||||
if (typeof after == UNDEF) {
|
|
||||||
after = before;
|
|
||||||
}
|
|
||||||
var sel = getSelection(el), val = el.value;
|
|
||||||
el.value = val.slice(0, sel.start) + before + sel.text + after + val.slice(sel.end);
|
|
||||||
var startIndex = sel.start + before.length;
|
|
||||||
updateSelectionAfterInsert(el, startIndex, sel.text, selectionBehaviour || "select");
|
|
||||||
};
|
|
||||||
|
|
||||||
function jQuerify(func, returnThis) {
|
|
||||||
return function() {
|
|
||||||
var el = this.jquery ? this[0] : this;
|
|
||||||
var nodeName = el.nodeName.toLowerCase();
|
|
||||||
|
|
||||||
if (el.nodeType == 1 && (nodeName == "textarea" || (nodeName == "input" && el.type == "text"))) {
|
|
||||||
var args = [el].concat(Array.prototype.slice.call(arguments));
|
|
||||||
var result = func.apply(this, args);
|
|
||||||
if (!returnThis) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (returnThis) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
$.fn.extend({
|
|
||||||
getSelection: jQuerify(getSelection, false),
|
|
||||||
setSelection: jQuerify(setSelection, true),
|
|
||||||
collapseSelection: jQuerify(collapseSelection, true),
|
|
||||||
deleteSelectedText: jQuerify(deleteSelectedText, true),
|
|
||||||
deleteText: jQuerify(deleteText, true),
|
|
||||||
extractSelectedText: jQuerify(extractSelectedText, false),
|
|
||||||
insertText: jQuerify(insertText, true),
|
|
||||||
replaceSelectedText: jQuerify(replaceSelectedText, true),
|
|
||||||
surroundSelectedText: jQuerify(surroundSelectedText, true)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})(jQuery);
|
|
||||||
Loading…
Reference in New Issue