jquery-ui实现一个多tab标签页可拖动编辑器记事本效果代码

代码语言:html

所属分类:其他

代码描述:jquery-ui实现一个多tab标签页可拖动编辑器效果代码

代码标签: jquery-ui tab 标签页 编辑器 记事本 拖动

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">



    <style>
    
    @font-face {
	font-family: 'icomoon';

	src:url('data:application/font-woff;base64,d09GRk9UVE8AAAdMAAoAAAAABwQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABDRkYgAAAA9AAAA8AAAAPAUtNf6E9TLzIAAAS0AAAAYAAAAGAIQvy+Y21hcAAABRQAAABEAAAAROYaAIdnYXNwAAAFWAAAAAgAAAAIAAAAEGhlYWQAAAVgAAAANgAAADb+9fzjaGhlYQAABZgAAAAkAAAAJAPiAexobXR4AAAFvAAAACwAAAAsEwABAG1heHAAAAXoAAAABgAAAAYAC1AAbmFtZQAABfAAAAE5AAABOUQYtNZwb3N0AAAHLAAAACAAAAAgAAMAAAEABAQAAQEBCGljb21vb24AAQIAAQA8+BwC+BsD+BgEHgoAGVMSX4uLHgoAGVMSX4uLDAeLa/iU+JQFHQAAAKIPHQAAAKcRHQAAAAkdAAADtxIADAEBCA8SFxwhJiswNTo/aWNvbW9vbmljb21vb251MjB1RTYwMHVFNjAxdUU2MDJ1RTYwM3VFNjA0dUU2MDV1RTYwNnVFNjA3dUU2MDgAAAIBiQAJAAsCAAEABAAHAF0AtAEPAXYBzAH4AmICkQL2/JQO+5QOi/gUFfiUiwWLawX8lIsFi2sV9/SLBYtrBfv0iwWLaxX4lIsFi2sF/JSLBYtrFff0iwWLawX79IsFi2sV+JSLBYtrBfyUiwWLaxX39IsFi2sF+/SLBQ6L+BQV+JSLBYtrBfyUiwXraxX31IsFi2sF+9SLBYsrFffUiwWLawX71IsFiysV99SLBYtrBfvUiwUr93QV+JSLBYtrBfyUiwWLKxX4lIsFi2sF/JSLBQ6L+BQV+JSLBYtrBfyUiwX3NGsV9/SLBYtrBfv0iwX7NGsV+JSLBYtrBfyUiwX3NGsV9/SLBYtrBfv0iwX7NGsV+JSLBYtrBfyUiwX3NGsV9/SLBYtrBfv0iwUO9/b3ghWeoZaoi6oIi9JSxESLCPs0iwWL/FQF91SLBdKLxMSL0giLuXK0ZqEI+zb3JhW+iwWni6Jui2gIi2h0bm+LCFiLBYv3FAXb+9QVPIsFi/cUBduLBaiLo26LaAiLaHNubosIDvf0+FQVy4sFi/tkBYs7Q0sziwgzi0PLi9sIi/dkBcuLBYv7ZAWLd5R4nHwInXqkgqaLCKaLpJSdnAicmpSei58Ii/dkBfuU/BQV99SLBYtLBfvUiwUO+FT4VBWLawVLiwX7NPwUBcuLBYtrBft0iwWLqwXLiwX3NPgUBUuLBYurBQ74hPe0FftEiwWL90QFi5SEkoKLCCuLBYKLhISLggiL+0QF+0SLBYKLhISLggiLKwWLgpKElIsI90SLBYv7RAWLgpKElIsI64sFlIuSkouUCIv3RAX3RIsFlIuSkouUCIvrBYuUhJKCiwgOi/ekFYsrBYuCkoSUiwj4dIsFlIuSkouUCIvrBYuUhJKCiwj8dIsFgouEhIuCCA73VPdPFYuL8cjEcwiXnZeflp8IU5lBf4uLCIuL6sTFeQiXoJaglp4IXY5ehIuLCIuLx6/BkQicppuhmZYI+6yL+zv71Ev7VAiriwXr9zQFi4ura+urCKKToqChpwhTmUB+i4sIDviUFPiUFYsMCgADAgABkAAFAAABTAFmAAAARwFMAWYAAAD1ABkAhAAAAAAAAAAAAAAAAAAAAAEQAAAAAAAAAAAAAAAAAAAAAEAAIOYIAeD/4P/gAeAAIAAAAAEAAAAAAAAAAAAAACAAAAAAAAIAAAADAAAAFAADAAEAAAAUAAQAMAAAAAgACAACAAAAIOYI//3//wAAACDmAP/9////4RoCAAMAAQAAAAAAAAAAAAEAAf//AA8AAQAAAAEAAEkagQJfDzz1AAsCAAAAAADO614TAAAAAM7rXhMAAP/gAgAB4AAAAAgAAgAAAAAAAAABAAAB4P/gAAACAAAAAAACAAABAAAAAAAAAAAAAAAAAAAACwAAAAABAAAAAgAAAAIAAAACAAAAAgAAYAIAAGACAABAAgAAAAIAAAACAAAAAABQAAALAAAAAAAOAK4AAQAAAAAAAQAOAAAAAQAAAAAAAgAOAEcAAQAAAAAAAwAOACQAAQAAAAAABAAOAFUAAQAAAAAABQAWAA4AAQAAAAAABgAHADIAAQAAAAAACgAoAGMAAwABBAkAAQAOAAAAAwABBAkAAgAOAEcAAwABBAkAAwAOACQAAwABBAkABAAOAFUAAwABBAkABQAWAA4AAwABBAkABgAOADkAAwABBAkACgAoAGMAaQBjAG8AbQBvAG8AbgBWAGUAcgBzAGkAbwBuACAAMQAuADAAaQBjAG8AbQBvAG8Abmljb21vb24AaQBjAG8AbQBvAG8AbgBSAGUAZwB1AGwAYQByAGkAYwBvAG0AbwBvAG4ARwBlAG4AZQByAGEAdABlAGQAIABiAHkAIABJAGMAbwBNAG8AbwBuAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=') format('woff'),
		url('') format('truetype'),
		url('') format('svg');
	font-weight: normal;
	font-style: normal;
}

[class^="icon-"], [class*=" icon-"] {
	font-family: 'icomoon';
	speak: none;
	font-style: normal;
	font-weight: normal;
	font-variant: normal;
	text-transform: none;
	line-height: 1;

	/* Better Font Rendering =========== */
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

.icon-paragraph-left:before {
	content: "\e600";
}
.icon-paragraph-center:before {
	content: "\e601";
}
.icon-paragraph-right:before {
	content: "\e602";
}
.icon-bold:before {
	content: "\e603";
}
.icon-underline:before {
	content: "\e604";
}
.icon-italic:before {
	content: "\e605";
}
.icon-plus:before {
	content: "\e606";
}
.icon-minus:before {
	content: "\e607";
}
.icon-quill:before {
	content: "\e608";
}
        @import url(https://fonts.googleapis.com/css?family=Telex);
        * {
          -webkit-box-sizing: border-box;
          -moz-box-sizing: border-box;
          box-sizing: border-box;
          -webkit-font-smoothing: antialiased;
        }
        
        body {
          background: #5deaba;
        }
        
        .launch {
          font-family: "Telex", sans-serif;
          text-decoration: none;
          width: 240px;
          height: 60px;
          display: block;
          text-align: center;
          line-height: 38px;
          border-radius: 3px;
          font-size: 60px;
          color: #fff;
          position: absolute;
          left: 50%;
          top: 50%;
          margin: -30px 0 0 -120px;
          display: none;
        }
        
        .texteditor {
          width: 550px;
          height: 450px;
          position: absolute;
          left: 50%;
          top: 50%;
          background: #fff;
          margin: -205px 0 0 -275px;
          border-radius: 0 0 10px 10px;
          box-shadow: 0px -12px 200px #20b080;
        }
        .texteditor .ops {
          height: 288px;
          width: 50px;
          background: #ebebeb;
          position: absolute;
          right: 0px;
          border-radius: 0 10px 10px 0;
          z-index: 1;
          display: none;
        }
        .texteditor .ops ul {
          list-style: none;
          margin: 0;
          padding: 0;
        }
        .texteditor .ops ul li {
          text-align: center;
          padding: 10px 0;
        }
        .texteditor .ops ul li:first-child {
          border-radius: 0 10px 0 0;
        }
        .texteditor .ops ul li:last-child {
          border-radius: 0 0 10px 0;
        }
        .texteditor .ops ul li:hover {
          background: #31d687;
        }
        .texteditor .ops ul li:hover a {
          color: #fff;
        }
        .texteditor .ops ul li a {
          text-decoration: none;
          color: grey;
          display: block;
        }
        .texteditor .textareawrap .fontsize {
          background: #5deaba;
          position: absolute;
          z-index: 400;
          bottom: 5px;
          left: 5px;
          padding: 5px 10px;
          font-family: "Telex", sans-serif;
          color: #fff;
          font-size: 16px;
          border-radius: 0 0 0 5px;
          display: none;
        }
        .texteditor .textareawrap .textarea {
          display: none;
          margin: 0;
          padding: 10px;
          width: 100%;
          height: 100%;
          position: absolute;
          border: 0;
          outline: none;
          resize: none;
          overflow: auto;
          border-radius: 0 0 10px 10px;
          font-family: "Telex", sans-serif;
          font-size: 13px;
          color: #666;
          background: #fff;
          z-index: 2;
        }
        .texteditor .top {
          font-family: "Telex", sans-serif;
          position: absolute;
          height: 45px;
          background: #ebebeb;
          top: -45px;
          width: 100%;
          border-radius: 10px 10px 0 0;
          border-bottom: 1px solid #bababa;
          cursor: move;
          overflow: hidden;
        }
        .texteditor .top .icon-quill {
          font-size: 25px;
          float: right;
          margin: 0 10px 0 0;
          color: grey;
          text-decoration: none;
        }
        .texteditor .top .icon-quill:hover {
          color: #31d687;
        }
        .texteditor ul.controls {
          padding: 0;
          margin: 16px 0 0 15px;
          list-style: none;
          float: left;
          width: 65px;
        }
        .texteditor ul.controls li {
          width: 15px;
          height: 15px;
          float: left;
          margin: 0 4%;
          border-radius: 50%;
          cursor: pointer;
        }
        .texteditor ul.controls li:after {
          content: "";
          background: linear-gradient(225deg, #ffffff 50%, transparent 50%);
          width: 15px;
          height: 15px;
          position: absolute;
          z-index: 2;
          border-radius: 50%;
          opacity: 0.4;
        }
        .texteditor ul.controls .close {
          background: #e57069;
        }
        .texteditor ul.controls .min {
          background: #f5ce56;
        }
        .texteditor ul.controls .expand {
          background: #31d687;
        }
        .texteditor ul.tabs {
          margin: 9px 0 0 100px;
          padding: 0;
          list-style: none;
        }
        .texteditor ul.tabs .addtab {
          font-size: 22px;
          z-index: 305;
          color: grey;
          position: relative;
          cursor: pointer;
          float: left;
          margin: 0 0 0 5px;
        }
        .texteditor ul.tabs .addtab:hover {
          color: #31d687;
        }
        .texteditor ul.tabs li {
          float: left;
          width: auto;
          margin: 0 -5px;
          padding: 0 15px;
          border-bottom: 35px solid #dad8d8;
          border-right: 20px solid transparent;
          border-left: 20px solid transparent;
          height: 0;
          line-height: 37px;
          position: relative;
          z-index: 2;
          cursor: pointer;
        }
        .texteditor ul.tabs li span {
          position: absolute;
          top: -12px;
          left: 2px;
          display: none;
          width: 17px;
          height: 17px;
          border-radius: 50%;
          top: -5px;
          left: -7px;
          text-align: center;
          line-height: 16px;
          background: #e57069;
          font-size: 20px;
          z-index: 3;
        }
        .texteditor ul.tabs li span:after {
          content: "";
          background: linear-gradient(225deg, #ffffff 50%, transparent 50%);
          width: 15px;
          height: 15px;
          position: absolute;
          z-index: 2;
          border-radius: 50%;
          opacity: 0.2;
          left: 2px;
        }
        .texteditor ul.tabs li a {
          text-decoration: none;
          color: #fff;
        }
        
        .ui-resizable-e {
          right: 0;
          position: absolute;
          width: 5px;
          height: 100%;
        }
        
        .ui-resizable-s {
          bottom: 0;
          height: 5px;
          width: 100%;
          position: absolute;
        }
        
        .ui-icon-gripsmall-diagonal-se {
          z-index: 90;
          bottom: 0;
          right: 0;
          position: absolute;
          width: 10px;
          height: 10px;
        }
        
        .ui-resizable-s:hover {
          cursor: ns-resize;
        }
        
        .ui-resizable-e:hover {
          cursor: ew-resize;
        }
        
        .ui-icon-gripsmall-diagonal-se:hover {
          cursor: nwse-resize;
        }
        
        .active {
          border-bottom-color: #bababa !important;
          z-index: 300 !important;
        }
        
        .bold {
          font-weight: bold;
        }
        
        .italic {
          font-style: italic;
        }
        
        .underline {
          text-decoration: underline;
        }
        
        .leftalign {
          text-align: left;
          display: inline;
        }
        
        .rightalign {
          text-align: right;
          display: block;
        }
        
        .centeralign {
          text-align: center;
          display: block;
        }
    </style>



</head>

<body>

    <a href="#" class="launch"> LAUNCH </a>
    <div class="texteditor">
        <div class="top">
            <ul class="controls">
                <li class="close">
                    <a href="#"></a>
                </li>
                <li class="min">
                    <a href="#"></a>
                </li>
                <li class="expand">
                    <a href="#"></a>
                </li>
            </ul>
            <ul class="tabs">
                <span class="tabwrap">
      </span>
            </ul>
            <a href="#" class="icon-quill"></a>
        </div>
        <div class="textareawrap">
            <span class="fontsize"></span>
        </div>
        <aside class="ops">
            <ul>
                <li>
                    <a href="#" class="icon-bold"></a>
                </li>
                <li>
                    <a class="icon-underline" href="#"></a>
                </li>
                <li>
                    <a class="icon-italic" href="#"></a>
                </li>
                <li>
                    <a class="icon-paragraph-left" href="#"></a>
                </li>
                <li>
                    <a class="icon-paragraph-right" href="#"></a>
                </li>
                <li>
                    <a class="icon-paragraph-center" href="#"></a>
                </li>
                <li>
                    <a class="icon-plus" href="#"></a>
                </li>
                <li>
                    <a class="icon-minus" href="#"></a>
                </li>
            </ul>
        </aside>
    </div>
    <script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/jquery.2.11.js"></script>
    <script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/jquery-ui-1.10.3.min.js"></script>
    <script>
        (function (factory, root) {
          if (typeof define == "function" && define.amd) {
            // AMD. Register as an anonymous module.
            define(factory);
          } else if (typeof module != "undefined" && typeof exports == "object") {
            // Node/CommonJS style
            module.exports = factory();
          } else {
            // No AMD or CommonJS support so we place Rangy in (probably) the global variable
            root.rangy = factory();
          }
        })(function () {
        
          var OBJECT = "object",FUNCTION = "function",UNDEFINED = "undefined";
        
          // Minimal set of properties required for DOM Level 2 Range compliance. Comparison constants such as START_TO_START
          // are omitted because ranges in KHTML do not have them but otherwise work perfectly well. See issue 113.
          var domRangeProperties = ["startContainer", "startOffset", "endContainer", "endOffset", "collapsed",
          "commonAncestorContainer"];
        
          // Minimal set of methods required for DOM Level 2 Range compliance
          var domRangeMethods = ["setStart", "setStartBefore", "setStartAfter", "setEnd", "setEndBefore",
          "setEndAfter", "collapse", "selectNode", "selectNodeContents", "compareBoundaryPoints", "deleteContents",
          "extractContents", "cloneContents", "insertNode", "surroundContents", "cloneRange", "toString", "detach"];
        
          var textRangeProperties = ["boundingHeight", "boundingLeft", "boundingTop", "boundingWidth", "htmlText", "text"];
        
          // Subset of TextRange's full set of methods that we're interested in
          var textRangeMethods = ["collapse", "compareEndPoints", "duplicate", "moveToElementText", "parentElement", "select",
          "setEndPoint", "getBoundingClientRect"];
        
          /*----------------------------------------------------------------------------------------------------------------*/
        
          // Trio of functions taken from Peter Michaux's article:
          // http://peter.michaux.ca/articles/feature-detection-state-of-the-art-browser-scripting
          function isHostMethod(o, p) {
            var t = typeof o[p];
            return t == FUNCTION || !!(t == OBJECT && o[p]) || t == "unknown";
          }
        
          function isHostObject(o, p) {
            return !!(typeof o[p] == OBJECT && o[p]);
          }
        
          function isHostProperty(o, p) {
            return typeof o[p] != UNDEFINED;
          }
        
          // Creates a convenience function to save verbose repeated calls to tests functions
          function createMultiplePropertyTest(testFunc) {
            return function (o, props) {
              var i = props.length;
              while (i--) {if (window.CP.shouldStopExecution(0)) break;
                if (!testFunc(o, props[i])) {
                  return false;
                }
              }window.CP.exitedLoop(0);
              return true;
            };
          }
        
          // Next trio of functions are a convenience to save verbose repeated calls to previous two functions
          var areHostMethods = createMultiplePropertyTest(isHostMethod);
          var areHostObjects = createMultiplePropertyTest(isHostObject);
          var areHostProperties = createMultiplePropertyTest(isHostProperty);
        
          function isTextRange(range) {
            return range && areHostMethods(range, textRangeMethods) && areHostProperties(range, textRangeProperties);
          }
        
          function getBody(doc) {
            return isHostObject(doc, "body") ? doc.body : doc.getElementsByTagName("body")[0];
          }
        
          var forEach = [].forEach ?
          function (arr, func) {
            arr.forEach(func);
          } :
          function (arr, func) {
            for (var i = 0, len = arr.length; i < len; ++i) {if (window.CP.shouldStopExecution(1)) break;
              func(arr[i], i);
            }window.CP.exitedLoop(1);
          };
        
          var modules = {};
        
          var isBrowser = typeof window != UNDEFINED && typeof document != UNDEFINED;
        
          var util = {
            isHostMethod: isHostMethod,
            isHostObject: isHostObject,
            isHostProperty: isHostProperty,
            areHostMethods: areHostMethods,
            areHostObjects: areHostObjects,
            areHostProperties: areHostProperties,
            isTextRange: isTextRange,
            getBody: getBody,
            forEach: forEach };
        
        
          var api = {
            version: "1.3.1-dev",
            initialized: false,
            isBrowser: isBrowser,
            supported: true,
            util: util,
            features: {},
            modules: modules,
            config: {
              alertOnFail: false,
              alertOnWarn: false,
              preferTextRange: false,
              autoInitialize: typeof rangyAutoInitialize == UNDEFINED ? true : rangyAutoInitialize } };
        
        
        
          function consoleLog(msg) {
            if (typeof console != UNDEFINED && isHostMethod(console, "log")) {
              console.log(msg);
            }
          }
        
          function alertOrLog(msg, shouldAlert) {
            if (isBrowser && shouldAlert) {
              alert(msg);
            } else {
              consoleLog(msg);
            }
          }
        
          function fail(reason) {
            api.initialized = true;
            api.supported = false;
            alertOrLog("Rangy is not supported in this environment. Reason: " + reason, api.config.alertOnFail);
          }
        
          api.fail = fail;
        
          function warn(msg) {
            alertOrLog("Rangy warning: " + msg, api.config.alertOnWarn);
          }
        
          api.warn = warn;
        
          // Add utility extend() method
          var extend;
          if ({}.hasOwnProperty) {
            util.extend = extend = function (obj, props, deep) {
              var o, p;
              for (var i in props) {
                if (props.hasOwnProperty(i)) {
                  o = obj[i];
                  p = props[i];
                  if (deep && o !== null && typeof o == "object" && p !== null && typeof p == "object") {
                    extend(o, p, true);
                  }
                  obj[i] = p;
                }
              }
              // Special case for toString, which does not show up in for...in loops in IE <= 8
              if (props.hasOwnProperty("toString")) {
                obj.toString = props.toString;
              }
              return obj;
            };
        
            util.createOptions = function (optionsParam, defaults) {
              var options = {};
              extend(options, defaults);
              if (optionsParam) {
                extend(options, optionsParam);
              }
              return options;
            };
          } else {
            fail("hasOwnProperty not supported");
          }
        
          // Test whether we're in a browser and bail out if not
          if (!isBrowser) {
            fail("Rangy can only run in a browser");
          }
        
          // Test whether Array.prototype.slice can be relied on for NodeLists and use an alternative toArray() if not
          (function () {
            var toArray;
        
            if (isBrowser) {
              var el = document.createElement("div");
              el.appendChild(document.createElement("span"));
              var slice = [].slice;
              try {
                if (slice.call(el.childNodes, 0)[0].nodeType == 1) {
                  toArray = function (arrayLike) {
                    return slice.call(arrayLike, 0);
                  };
                }
              } catch (e) {}
            }
        
            if (!toArray) {
              toArray = function (arrayLike) {
                var arr = [];
                for (var i = 0, len = arrayLike.length; i < len; ++i) {if (window.CP.shouldStopExecution(2)) break;
                  arr[i] = arrayLike[i];
                }window.CP.exitedLoop(2);
                return arr;
              };
            }
        
            util.toArray = toArray;
          })();
        
          // Very simple event handler wrapper function that doesn't attempt to solve issues such as "this" handling or
          // normalization of event properties
          var addListener;
          if (isBrowser) {
            if (isHostMethod(document, "addEventListener")) {
              addListener = function (obj, eventType, listener) {
                obj.addEventListener(eventType, listener, false);
              };
            } else if (isHostMethod(document, "attachEvent")) {
              addListener = function (obj, eventType, listener) {
                obj.attachEvent("on" + eventType, listener);
              };
            } else {
              fail("Document does not have required addEventListener or attachEvent method");
            }
        
            util.addListener = addListener;
          }
        
          var initListeners = [];
        
          function getErrorDesc(ex) {
            return ex.message || ex.description || String(ex);
          }
        
          // Initialization
          function init() {
            if (!isBrowser || api.initialized) {
              return;
            }
            var testRange;
            var implementsDomRange = false,implementsTextRange = false;
        
            // First, perform basic feature tests
        
            if (isHostMethod(document, "createRange")) {
              testRange = document.createRange();
              if (areHostMethods(testRange, domRangeMethods) && areHostProperties(testRange, domRangeProperties)) {
                implementsDomRange = true;
              }
            }
        
            var body = getBody(document);
            if (!body || body.nodeName.toLowerCase() != "body") {
              fail("No body element found");
              return;
            }
        
            if (body && isHostMethod(body, "createTextRange")) {
              testRange = body.createTextRange();
              if (isTextRange(testRange)) {
                implementsTextRange = true;
              }
            }
        
            if (!implementsDomRange && !implementsTextRange) {
              fail("Neither Range nor TextRange are available");
              return;
            }
        
            api.initialized = true;
            api.features = {
              implementsDomRange: implementsDomRange,
              implementsTextRange: implementsTextRange };
        
        
            // Initialize modules
            var module, errorMessage;
            for (var moduleName in modules) {
              if ((module = modules[moduleName]) instanceof Module) {
                module.init(module, api);
              }
            }
        
            // Call init listeners
            for (var i = 0, len = initListeners.length; i < len; ++i) {if (window.CP.shouldStopExecution(3)) break;
              try {
                initListeners[i](api);
              } catch (ex) {
                errorMessage = "Rangy init listener threw an exception. Continuing. Detail: " + getErrorDesc(ex);
                consoleLog(errorMessage);
              }
            }window.CP.exitedLoop(3);
          }
        
          function deprecationNotice(deprecated, replacement, module) {
            if (module) {
              deprecated += " in module " + module.name;
            }
            api.warn("DEPRECATED: " + deprecated + " is deprecated. Please use " +
            replacement + " instead.");
          }
        
          function createAliasForDeprecatedMethod(owner, deprecated, replacement, module) {
            owner[deprecated] = function () {
              deprecationNotice(deprecated, replacement, module);
              return owner[replacement].apply(owner, util.toArray(arguments));
            };
          }
        
          util.deprecationNotice = deprecationNotice;
          util.createAliasForDeprecatedMethod = createAliasForDeprecatedMethod;
        
          // Allow external scripts to initialize this library in case it's loaded after the document has loaded
          api.init = init;
        
          // Execute listener immediately if already initialized
          api.addInitListener = function (listener) {
            if (api.initialized) {
              listener(api);
            } else {
              initListeners.push(listener);
            }
          };
        
          var shimListeners = [];
        
          api.addShimListener = function (listener) {
            shimListeners.push(listener);
          };
        
          function shim(win) {
            win = win || window;
            init();
        
            // Notify listeners
            for (var i = 0, len = shimListeners.length; i < len; ++i) {if (window.CP.shouldStopExecution(4)) break;
              shimListeners[i](win);
            }window.CP.exitedLoop(4);
          }
        
          if (isBrowser) {
            api.shim = api.createMissingNativeApi = shim;
            createAliasForDeprecatedMethod(api, "createMissingNativeApi", "shim");
          }
        
          function Module(name, dependencies, initializer) {
            this.name = name;
            this.dependencies = dependencies;
            this.initialized = false;
            this.supported = false;
            this.initializer = initializer;
          }
        
          Module.prototype = {
            init: function () {
              var requiredModuleNames = this.dependencies || [];
              for (var i = 0, len = requiredModuleNames.length, requiredModule, moduleName; i < len; ++i) {if (window.CP.shouldStopExecution(5)) break;
                moduleName = requiredModuleNames[i];
        
                requiredModule = modules[moduleName];
                if (!requiredModule || !(requiredModule instanceof Module)) {
                  throw new Error("required module '" + moduleName + "' not found");
                }
        
                requiredModule.init();
        
                if (!requiredModule.supported) {
                  throw new Error("required module '" + moduleName + "' not supported");
                }
              }
        
              // Now run initializer
              window.CP.exitedLoop(5);this.initializer(this);
            },
        
            fail: function (reason) {
              this.initialized = true;
              this.supported = false;
              throw new Error(reason);
            },
        
            warn: function (msg) {
              api.warn("Module " + this.name + ": " + msg);
            },
        
            deprecationNotice: function (deprecated, replacement) {
              api.warn("DEPRECATED: " + deprecated + " in module " + this.name + " is deprecated. Please use " +
              replacement + " instead");
            },
        
            createError: function (msg) {
              return new Error("Error in Rangy " + this.name + " module: " + msg);
            } };
        
        
          function createModule(name, dependencies, initFunc) {
            var newModule = new Module(name, dependencies, function (module) {
              if (!module.initialized) {
                module.initialized = true;
                try {
                  initFunc(api, module);
                  module.supported = true;
                } catch (ex) {
                  var errorMessage = "Module '" + name + "' failed to load: " + getErrorDesc(ex);
                  consoleLog(errorMessage);
                  if (ex.stack) {
                    consoleLog(ex.stack);
                  }
                }
              }
            });
            modules[name] = newModule;
            return newModule;
          }
        
          api.createModule = function (name) {
            // Allow 2 or 3 arguments (second argument is an optional array of dependencies)
            var initFunc, dependencies;
            if (arguments.length == 2) {
              initFunc = arguments[1];
              dependencies = [];
            } else {
              initFunc = arguments[2];
              dependencies = arguments[1];
            }
        
            var module = createModule(name, dependencies, initFunc);
        
            // Initialize the module immediately if the core is already initialized
            if (api.initialized && api.supported) {
              module.init();
            }
          };
        
          api.createCoreModule = function (name, dependencies, initFunc) {
            createModule(name, dependencies, initFunc);
          };
        
          /*----------------------------------------------------------------------------------------------------------------*/
        
          // Ensure rangy.rangePrototype and rangy.selectionPrototype are available immediately
        
          function RangePrototype() {}
          api.RangePrototype = RangePrototype;
          api.rangePrototype = new RangePrototype();
        
          function SelectionPrototype() {}
          api.selectionPrototype = new SelectionPrototype();
        
          /*----------------------------------------------------------------------------------------------------------------*/
        
          // DOM utility methods used by Rangy
          api.createCoreModule("DomUtil", [], function (api, module) {
            var UNDEF = "undefined";
            var util = api.util;
            var getBody = util.getBody;
        
            // Perform feature tests
            if (!util.areHostMethods(document, ["createDocumentFragment", "createElement", "createTextNode"])) {
              module.fail("document missing a Node creation method");
            }
        
            if (!util.isHostMethod(document, "getElementsByTagName")) {
              module.fail("document missing getElementsByTagName method");
            }
        
            var el = document.createElement("div");
            if (!util.areHostMethods(el, ["insertBefore", "appendChild", "cloneNode"] ||
            !util.areHostObjects(el, ["previousSibling", "nextSibling", "childNodes", "parentNode"]))) {
              module.fail("Incomplete Element implementation");
            }
        
            // innerHTML is required for Range's createContextualFragment method
            if (!util.isHostProperty(el, "innerHTML")) {
              module.fail("Element is missing innerHTML property");
            }
        
            var textNode = document.createTextNode("test");
            if (!util.areHostMethods(textNode, ["splitText", "deleteData", "insertData", "appendData", "cloneNode"] ||
            !util.areHostObjects(el, ["previousSibling", "nextSibling", "childNodes", "parentNode"]) ||
            !util.areHostProperties(textNode, ["data"]))) {
              module.fail("Incomplete Text Node implementation");
            }
        
            /*----------------------------------------------------------------------------------------------------------------*/
        
            // Removed use of indexOf because of a bizarre bug in Opera that is thrown in one of the Acid3 tests. I haven't been
            // able to replicate it outside of the test. The bug is that indexOf returns -1 when called on an Array that
            // contains just the document as a single element and the value searched for is the document.
            var arrayContains = /*Array.prototype.indexOf ?
            function(arr, val) {
            return arr.indexOf(val) > -1;
            }:*/
        
            function (arr, val) {
              var i = arr.length;
              while (i--) {if (window.CP.shouldStopExecution(6)) break;
                if (arr[i] === val) {
                  return true;
                }
              }window.CP.exitedLoop(6);
              return false;
            };
        
            // Opera 11 puts HTML elements in the null namespace, it seems, and IE 7 has undefined namespaceURI
            function isHtmlNamespace(node) {
              var ns;
              return typeof node.namespaceURI == UNDEF || (ns = node.namespaceURI) === null || ns == "http://www.w3.org/1999/xhtml";
            }
        
            function parentElement(node) {
              var parent = node.parentNode;
              return parent.nodeType == 1 ? parent : null;
            }
        
            function getNodeIndex(node) {
              var i = 0;
              while (node = node.previousSibling) {if (window.CP.shouldStopExecution(7)) break;
                ++i;
              }window.CP.exitedLoop(7);
              return i;
            }
        
            function getNodeLength(node) {
              switch (node.nodeType) {
                case 7:
                case 10:
                  return 0;
                case 3:
                case 8:
                  return node.length;
                default:
                  return node.childNodes.length;}
        
            }
        
            function getCommonAncestor(node1, node2) {
              var ancestors = [],n;
              for (n = node1; n; n = n.parentNode) {if (window.CP.shouldStopExecution(8)) break;
                ancestors.push(n);
              }window.CP.exitedLoop(8);
        
              for (n = node2; n; n = n.parentNode) {if (window.CP.shouldStopExecution(9)) break;
                if (arrayContains(ancestors, n)) {
                  return n;
                }
              }window.CP.exitedLoop(9);
        
              return null;
            }
        
            function isAncestorOf(ancestor, descendant, selfIsAncestor) {
              var n = selfIsAncestor ? descendant : descendant.parentNode;
              while (n) {if (window.CP.shouldStopExecution(10)) break;
                if (n === ancestor) {
                  return true;
                } else {
                  n = n.parentNode;
                }
              }window.CP.exitedLoop(10);
              return false;
            }
        
            function isOrIsAncestorOf(ancestor, descendant) {
              return isAncestorOf(ancestor, descendant, true);
            }
        
            function getClosestAncestorIn(node, ancestor, selfIsAncestor) {
              var p,n = selfIsAncestor ? node : node.parentNode;
              while (n) {if (window.CP.shouldStopExecution(11)) break;
                p = n.parentNode;
                if (p === ancestor) {
                  return n;
                }
                n = p;
              }window.CP.exitedLoop(11);
              return null;
            }
        
            function isCharacterDataNode(node) {
              var t = node.nodeType;
              return t == 3 || t == 4 || t == 8; // Text, CDataSection or Comment
            }
        
            function isTextOrCommentNode(node) {
              if (!node) {
                return false;
              }
              var t = node.nodeType;
              return t == 3 || t == 8; // Text or Comment
            }
        
            function insertAfter(node, precedingNode) {
              var nextNode = precedingNode.nextSibling,parent = precedingNode.parentNode;
              if (nextNode) {
                parent.insertBefore(node, nextNode);
              } else {
                parent.appendChild(node);
              }
              return node;
            }
        
            // Note that we cannot use splitText() because it is bugridden in IE 9.
            function splitDataNode(node, index, positionsToPreserve) {
              var newNode = node.cloneNode(false);
              newNode.deleteData(0, index);
              node.deleteData(index, node.length - index);
              insertAfter(newNode, node);
        
              // Preserve positions
              if (positionsToPreserve) {
                for (var i = 0, position; position = positionsToPreserve[i++];) {if (window.CP.shouldStopExecution(12)) break;
                  // Handle case where position was inside the portion of node after the split point
                  if (position.node == node && position.offset > index) {
                    position.node = newNode;
                    position.offset -= index;
                  }
                  // Handle the case where the position is a node offset within node's parent
                  else if (position.node == node.parentNode && position.offset > getNodeIndex(node)) {
                      ++position.offset;
                    }
                }window.CP.exitedLoop(12);
              }
              return newNode;
            }
        
            function getDocument(node) {
              if (node.nodeType == 9) {
                return node;
              } else if (typeof node.ownerDocument != UNDEF) {
                return node.ownerDocument;
              } else if (typeof node.document != UNDEF) {
                return node.document;
              } else if (node.parentNode) {
                return getDocument(node.parentNode);
              } else {
                throw module.createError("getDocument: no document found for node");
              }
            }
        
            function getWindow(node) {
              var doc = getDocument(node);
              if (typeof doc.defaultView != UNDEF) {
                return doc.defaultView;
              } else if (typeof doc.parentWindow != UNDEF) {
                return doc.parentWindow;
              } else {
                throw module.createError("Cannot get a window object for node");
              }
            }
        
            function getIframeDocument(iframeEl) {
              if (typeof iframeEl.contentDocument != UNDEF) {
                return iframeEl.contentDocument;
              } else if (typeof iframeEl.contentWindow != UNDEF) {
                return iframeEl.contentWindow.document;
              } else {
                throw module.createError("getIframeDocument: No Document object found for iframe element");
              }
            }
        
            function getIframeWindow(iframeEl) {
              if (typeof iframeEl.contentWindow != UNDEF) {
                return iframeEl.contentWindow;
              } else if (typeof iframeEl.contentDocument != UNDEF) {
                return iframeEl.contentDocument.defaultView;
              } else {
                throw module.createError("getIframeWindow: No Window object found for iframe element");
              }
            }
        
            // This looks bad. Is it worth it?
            function isWindow(obj) {
              return obj && util.isHostMethod(obj, "setTimeout") && util.isHostObject(obj, "document");
            }
        
            function getContentDocument(obj, module, methodName) {
              var doc;
        
              if (!obj) {
                doc = document;
              }
        
              // Test if a DOM node has been passed and obtain a document object for it if so
              else if (util.isHostProperty(obj, "nodeType")) {
                  doc = obj.nodeType == 1 && obj.tagName.toLowerCase() == "iframe" ?
                  getIframeDocument(obj) : getDocument(obj);
                }
        
                // Test if the doc parameter appears to be a Window object
                else if (isWindow(obj)) {
                    doc = obj.document;
                  }
        
              if (!doc) {
                throw module.createError(methodName + "(): Parameter must be a Window object or DOM node");
              }
        
              return doc;
            }
        
            function getRootContainer(node) {
              var parent;
              while (parent = node.parentNode) {if (window.CP.shouldStopExecution(13)) break;
                node = parent;
              }window.CP.exitedLoop(13);
              return node;
            }
        
            function comparePoints(nodeA, offsetA, nodeB, offsetB) {
              // See http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Comparing
              var nodeC, root, childA, childB, n;
              if (nodeA == nodeB) {
                // Case 1: nodes are the same
                return offsetA === offsetB ? 0 : offsetA < offsetB ? -1 : 1;
              } else if (nodeC = getClosestAncestorIn(nodeB, nodeA, true)) {
                // Case 2: node C (container B or an ancestor) is a child node of A
                return offsetA <= getNodeIndex(nodeC) ? -1 : 1;
              } else if (nodeC = getClosestAncestorIn(nodeA, nodeB, true)) {
                // Case 3: node C (container A or an ancestor) is a child node of B
                return getNodeIndex(nodeC) < offsetB ? -1 : 1;
              } else {
                root = getCommonAncestor(nodeA, nodeB);
                if (!root) {
                  throw new Error("comparePoints error: nodes have no common ancestor");
                }
        
                // Case 4: containers are siblings or descendants of siblings
                childA = nodeA === root ? root : getClosestAncestorIn(nodeA, root, true);
                childB = nodeB === root ? root : getClosestAncestorIn(nodeB, root, true);
        
                if (childA === childB) {
                  // This shouldn't be possible
                  throw module.createError("comparePoints got to case 4 and childA and childB are the same!");
                } else {
                  n = root.firstChild;
                  while (n) {if (window.CP.shouldStopExecution(14)) break;
                    if (n === childA) {
                      return -1;
                    } else if (n === childB) {
                      return 1;
                    }
                    n = n.nextSibling;
                  }window.CP.exitedLoop(14);
                }
              }
            }
        
            /*----------------------------------------------------------------------------------------------------------------*/
        
            // Test for IE's crash (IE 6/7) or exception (IE >= 8) when a reference to garbage-collected text node is queried
            var crashyTextNodes = false;
        
            function isBrokenNode(node) {
              var n;
              try {
                n = node.parentNode;
                return false;
              } catch (e) {
                return true;
              }
            }
        
            (function () {
              var el = document.createElement("b");
              el.innerHTML = "1";
              var textNode = el.firstChild;
              el.innerHTML = "<br />";
              crashyTextNodes = isBrokenNode(textNode);
        
              api.features.crashyTextNodes = crashyTextNodes;
            })();
        
            /*----------------------------------------------------------------------------------------------------------------*/
        
            function inspectNode(node) {
              if (!node) {
                return "[No node]";
              }
              if (crashyTextNodes && isBrokenNode(node)) {
                return "[Broken node]";
              }
              if (isCharacterDataNode(node)) {
                return '"' + node.data + '"';
              }
              if (node.nodeType == 1) {
                var idAttr = node.id ? ' id="' + node.id + '"' : "";
                return "<" + node.nodeName + idAttr + ">[index:" + getNodeIndex(node) + ",length:" + node.childNodes.length + "][" + (node.innerHTML || "[innerHTML not supported]").slice(0, 25) + "]";
              }
              return node.nodeName;
            }
        
            function fragmentFromNodeChildren(node) {
              var fragment = getDocument(node).createDocumentFragment(),child;
              while (child = node.firstChild) {if (window.CP.shouldStopExecution(15)) break;
                fragment.appendChild(child);
              }window.CP.exitedLoop(15);
              return fragment;
            }
        
            var getComputedStyleProperty;
            if (typeof window.getComputedStyle != UNDEF) {
              getComputedStyleProperty = function (el, propName) {
                return getWindow(el).getComputedStyle(el, null)[propName];
              };
            } else if (typeof document.documentElement.currentStyle != UNDEF) {
              getComputedStyleProperty = function (el, propName) {
                return el.currentStyle ? el.currentStyle[propName] : "";
              };
            } else {
              module.fail("No means of obtaining computed style properties found");
            }
        
            function createTestElement(doc, html, contentEditable) {
              var body = getBody(doc);
              var el = doc.createElement("div");
              el.contentEditable = "" + !!contentEditable;
              if (html) {
                el.innerHTML = html;
              }
        
              // Insert the test element at the start of the body to prevent scrolling to the bottom in iOS (issue #292)
              var bodyFirstChild = body.firstChild;
              if (bodyFirstChild) {
                body.insertBefore(el, bodyFirstChild);
              } else {
                body.appendChild(el);
              }
        
              return el;
            }
        
            function removeNode(node) {
              return node.parentNode.removeChild(node);
            }
        
            function NodeIterator(root) {
              this.root = root;
              this._next = root;
            }
        
            NodeIterator.prototype = {
              _current: null,
        
              hasNext: function () {
                return !!this._next;
              },
        
              next: function () {
                var n = this._current = this._next;
                var child, next;
                if (this._current) {
                  child = n.firstChild;
                  if (child) {
                    this._next = child;
                  } else {
                    next = null;
                    while (n !== this.root && !(next = n.nextSibling)) {if (window.CP.shouldStopExecution(16)) break;
                      n = n.parentNode;
                    }window.CP.exitedLoop(16);
                    this._next = next;
                  }
                }
                return this._current;
              },
        
              detach: function () {
                this._current = this._next = this.root = null;
              } };
        
        
            function createIterator(root) {
              return new NodeIterator(root);
            }
        
            function DomPosition(node, offset) {
              this.node = node;
              this.offset = offset;
            }
        
            DomPosition.prototype = {
              equals: function (pos) {
                return !!pos && this.node === pos.node && this.offset == pos.offset;
              },
        
              inspect: function () {
                return "[DomPosition(" + inspectNode(this.node) + ":" + this.offset + ")]";
              },
        
              toString: function () {
                return this.inspect();
              } };
        
        
            function DOMException(codeName) {
              this.code = this[codeName];
              this.codeName = codeName;
              this.message = "DOMException: " + this.codeName;
            }
        
            DOMException.prototype = {
              INDEX_SIZE_ERR: 1,
              HIERARCHY_REQUEST_ERR: 3,
              WRONG_DOCUMENT_ERR: 4,
              NO_MODIFICATION_ALLOWED_ERR: 7,
              NOT_FOUND_ERR: 8,
              NOT_SUPPORTED_ERR: 9,
              INVALID_STATE_ERR: 11,
              INVALID_NODE_TYPE_ERR: 24 };
        
        
            DOMException.prototype.toString = function () {
              return this.message;
            };
        
            api.dom = {
              arrayContains: arrayContains,
              isHtmlNamespace: isHtmlNamespace,
              parentElement: parentElement,
              getNodeIndex: getNodeIndex,
              getNodeLength: getNodeLength,
              getCommonAncestor: getCommonAncestor,
              isAncestorOf: isAncestorOf,
              isOrIsAncestorOf: isOrIsAncestorOf,
              getClosestAncestorIn: getClosestAncestorIn,
              isCharacterDataNode: isCharacterDataNode,
              isTextOrCommentNode: isTextOrCommentNode,
              insertAfter: insertAfter,
              splitDataNode: splitDataNode,
              getDocument: getDocument,
              getWindow: getWindow,
              getIframeWindow: getIframeWindow,
              getIframeDocument: getIframeDocument,
              getBody: getBody,
              isWindow: isWindow,
              getContentDocument: getContentDocument,
              getRootContainer: getRootContainer,
              comparePoints: comparePoints,
              isBrokenNode: isBrokenNode,
              inspectNode: inspectNode,
              getComputedStyleProperty: getComputedStyleProperty,
              createTestElement: createTestElement,
              removeNode: removeNode,
              fragmentFromNodeChildren: fragmentFromNodeChildren,
              createIterator: createIterator,
              DomPosition: DomPosition };
        
        
            api.DOMException = DOMException;
          });
        
          /*----------------------------------------------------------------------------------------------------------------*/
        
          // Pure JavaScript implementation of DOM Range
          api.createCoreModule("DomRange", ["DomUtil"], function (api, module) {
            var dom = api.dom;
            var util = api.util;
            var DomPosition = dom.DomPosition;
            var DOMException = api.DOMException;
        
            var isCharacterDataNode = dom.isCharacterDataNode;
            var getNodeIndex = dom.getNodeIndex;
            var isOrIsAncestorOf = dom.isOrIsAncestorOf;
            var getDocument = dom.getDocument;
            var comparePoints = dom.comparePoints;
            var splitDataNode = dom.splitDataNode;
            var getClosestAncestorIn = dom.getClosestAncestorIn;
            var getNodeLength = dom.getNodeLength;
            var arrayContains = dom.arrayContains;
            var getRootContainer = dom.getRootContainer;
            var crashyTextNodes = api.features.crashyTextNodes;
        
            var removeNode = dom.removeNode;
        
            /*----------------------------------------------------------------------------------------------------------------*/
        
            // Utility functions
        
            function isNonTextPartiallySelected(node, range) {
              return node.nodeType != 3 && (
              isOrIsAncestorOf(node, range.startContainer) || isOrIsAncestorOf(node, range.endContainer));
            }
        
            function getRangeDocument(range) {
              return range.document || getDocument(range.startContainer);
            }
        
            function getRangeRoot(range) {
              return getRootContainer(range.startContainer);
            }
        
            function getBoundaryBeforeNode(node) {
              return new DomPosition(node.parentNode, getNodeIndex(node));
            }
        
            function getBoundaryAfterNode(node) {
              return new DomPosition(node.parentNode, getNodeIndex(node) + 1);
            }
        
            function insertNodeAtPosition(node, n, o) {
              var firstNodeInserted = node.nodeType == 11 ? node.firstChild : node;
              if (isCharacterDataNode(n)) {
                if (o == n.length) {
                  dom.insertAfter(node, n);
                } else {
                  n.parentNode.insertBefore(node, o == 0 ? n : splitDataNode(n, o));
                }
              } else if (o >= n.childNodes.length) {
                n.appendChild(node);
              } else {
                n.insertBefore(node, n.childNodes[o]);
              }
              return firstNodeInserted;
            }
        
            function rangesIntersect(rangeA, rangeB, touchingIsIntersecting) {
              assertRangeValid(rangeA);
              assertRangeValid(rangeB);
        
              if (getRangeDocument(rangeB) != getRangeDocument(rangeA)) {
                throw new DOMException("WRONG_DOCUMENT_ERR");
              }
        
              var startComparison = comparePoints(rangeA.startContainer, rangeA.startOffset, rangeB.endContainer, rangeB.endOffset),
              endComparison = comparePoints(rangeA.endContainer, rangeA.endOffset, rangeB.startContainer, rangeB.startOffset);
        
              return touchingIsIntersecting ? startComparison <= 0 && endComparison >= 0 : startComparison < 0 && endComparison > 0;
            }
        
            function cloneSubtree(iterator) {
              var partiallySelected;
              for (var node, frag = getRangeDocument(iterator.range).createDocumentFragment(), subIterator; node = iterator.next();) {if (window.CP.shouldStopExecution(17)) break;
                partiallySelected = iterator.isPartiallySelectedSubtree();
                node = node.cloneNode(!partiallySelected);
                if (partiallySelected) {
                  subIterator = iterator.getSubtreeIterator();
                  node.appendChild(cloneSubtree(subIterator));
                  subIterator.detach();
                }
        
                if (node.nodeType == 10) {// DocumentType
                  throw new DOMException("HIERARCHY_REQUEST_ERR");
                }
                frag.appendChild(node);
              }window.CP.exitedLoop(17);
              return frag;
            }
        
            function iterateSubtree(rangeIterator, func, iteratorState) {
              var it, n;
              iteratorState = iteratorState || { stop: false };
              for (var node, subRangeIterator; node = rangeIterator.next();) {if (window.CP.shouldStopExecution(18)) break;
                if (rangeIterator.isPartiallySelectedSubtree()) {
                  if (func(node) === false) {
                    iteratorState.stop = true;
                    return;
                  } else {
                    // The node is partially selected by the Range, so we can use a new RangeIterator on the portion of
                    // the node selected by the Range.
                    subRangeIterator = rangeIterator.getSubtreeIterator();
                    iterateSubtree(subRangeIterator, func, iteratorState);
                    subRangeIterator.detach();
                    if (iteratorState.stop) {
                      return;
                    }
                  }
                } else {
                  // The whole node is selected, so we can use efficient DOM iteration to iterate over the node and its
                  // descendants
                  it = dom.createIterator(node);
                  while (n = it.next()) {if (window.CP.shouldStopExecution(19)) break;
                    if (func(n) === false) {
                      iteratorState.stop = true;
                      return;
                    }
                  }window.CP.exitedLoop(19);
                }
              }window.CP.exitedLoop(18);
            }
        
            function deleteSubtree(iterator) {
              var subIterator;
              while (iterator.next()) {if (window.CP.shouldStopExecution(20)) break;
                if (iterator.isPartiallySelectedSubtree()) {
                  subIterator = iterator.getSubtreeIterator();
                  deleteSubtree(subIterator);
                  subIterator.detach();
                } else {
                  iterator.remove();
                }
              }window.CP.exitedLoop(20);
            }
        
            function extractSubtree(iterator) {
              for (var node, frag = getRangeDocument(iterator.range).createDocumentFragment(), subIterator; node = iterator.next();) {if (window.CP.shouldStopExecution(21)) break;
        
                if (iterator.isPartiallySelectedSubtree()) {
                  node = node.cloneNode(false);
                  subIterator = iterator.getSubtreeIterator();
                  node.appendChild(extractSubtree(subIterator));
                  subIterator.detach();
                } else {
                  iterator.remove();
                }
                if (node.nodeType == 10) {// DocumentType
                  throw new DOMException("HIERARCHY_REQUEST_ERR");
                }
                frag.appendChild(node);
              }window.CP.exitedLoop(21);
              return frag;
            }
        
            function getNodesInRange(range, nodeTypes, filter) {
              var filterNodeTypes = !!(nodeTypes && nodeTypes.length),regex;
              var filterExists = !!filter;
              if (filterNodeTypes) {
                regex = new RegExp("^(" + nodeTypes.join("|") + ")$");
              }
        
              var nodes = [];
              iterateSubtree(new RangeIterator(range, false), function (node) {
                if (filterNodeTypes && !regex.test(node.nodeType)) {
                  return;
                }
                if (filterExists && !filter(node)) {
                  return;
                }
                // Don't include a boundary container if it is a character data node and the range does not contain any
                // of its character data. See issue 190.
                var sc = range.startContainer;
                if (node == sc && isCharacterDataNode(sc) && range.startOffset == sc.length) {
                  return;
                }
        
                var ec = range.endContainer;
                if (node == ec && isCharacterDataNode(ec) && range.endOffset == 0) {
                  return;
                }
        
                nodes.push(node);
              });
              return nodes;
            }
        
            function inspect(range) {
              var name = typeof range.getName == "undefined" ? "Range" : range.getName();
              return "[" + name + "(" + dom.inspectNode(range.startContainer) + ":" + range.startOffset + ", " +
              dom.inspectNode(range.endContainer) + ":" + range.endOffset + ")]";
            }
        
            /*----------------------------------------------------------------------------------------------------------------*/
        
            // RangeIterator code partially borrows from IERange by Tim Ryan (https://github.com/timcameronryan/IERange)
        
            function RangeIterator(range, clonePartiallySelectedTextNodes) {
              this.range = range;
              this.clonePartiallySelectedTextNodes = clonePartiallySelectedTextNodes;
        
        
              if (!range.collapsed) {
                this.sc = range.startContainer;
                this.so = range.startOffset;
                this.ec = range.endContainer;
                this.eo = range.endOffset;
                var root = range.commonAncestorContainer;
        
                if (this.sc === this.ec && isCharacterDataNode(this.sc)) {
                  this.isSingleCharacterDataNode = true;
                  this._first = this._last = this._next = this.sc;
                } else {
                  this._first = this._next = this.sc === root && !isCharacterDataNode(this.sc) ?
                  this.sc.childNodes[this.so] : getClosestAncestorIn(this.sc, root, true);
                  this._last = this.ec === root && !isCharacterDataNode(this.ec) ?
                  this.ec.childNodes[this.eo - 1] : getClosestAncestorIn(this.ec, root, true);
                }
              }
            }
        
            RangeIterator.prototype = {
              _current: null,
              _next: null,
              _first: null,
              _last: null,
              isSingleCharacterDataNode: false,
        
              reset: function () {
                this._current = null;
                this._next = this._first;
              },
        
              hasNext: function () {
                return !!this._next;
              },
        
              next: function () {
                // Move to next node
                var current = this._current = this._next;
                if (current) {
                  this._next = current !== this._last ? current.nextSibling : null;
        
                  // Check for partially selected text nodes
                  if (isCharacterDataNode(current) && this.clonePartiallySelectedTextNodes) {
                    if (current === this.ec) {
                      (current = current.cloneNode(true)).deleteData(this.eo, current.length - this.eo);
                    }
                    if (this._current === this.sc) {
                      (current = current.cloneNode(true)).deleteData(0, this.so);
                    }
                  }
                }
        
                return current;
              },
        
              remove: function () {
                var current = this._current,start,end;
        
                if (isCharacterDataNode(current) && (current === this.sc || current === this.ec)) {
                  start = current === this.sc ? this.so : 0;
                  end = current === this.ec ? this.eo : current.length;
                  if (start != end) {
                    current.deleteData(start, end - start);
                  }
                } else {
                  if (current.parentNode) {
                    removeNode(current);
                  } else {
                  }
                }
              },
        
              // Checks if the current node is partially selected
              isPartiallySelectedSubtree: function () {
                var current = this._current;
                return isNonTextPartiallySelected(current, this.range);
              },
        
              getSubtreeIterator: function () {
                var subRange;
                if (this.isSingleCharacterDataNode) {
                  subRange = this.range.cloneRange();
                  subRange.collapse(false);
                } else {
                  subRange = new Range(getRangeDocument(this.range));
                  var current = this._current;
                  var startContainer = current,startOffset = 0,endContainer = current,endOffset = getNodeLength(current);
        
                  if (isOrIsAncestorOf(current, this.sc)) {
                    startContainer = this.sc;
                    startOffset = this.so;
                  }
                  if (isOrIsAncestorOf(current, this.ec)) {
                    endContainer = this.ec;
                    endOffset = this.eo;
                  }
        
                  updateBoundaries(subRange, startContainer, startOffset, endContainer, endOffset);
                }
                return new RangeIterator(subRange, this.clonePartiallySelectedTextNodes);
              },
        
              detach: function () {
                this.range = this._current = this._next = this._first = this._last = this.sc = this.so = this.ec = this.eo = null;
              } };
        
        
            /*----------------------------------------------------------------------------------------------------------------*/
        
            var beforeAfterNodeTypes = [1, 3, 4, 5, 7, 8, 10];
            var rootContainerNodeTypes = [2, 9, 11];
            var readonlyNodeTypes = [5, 6, 10, 12];
            var insertableNodeTypes = [1, 3, 4, 5, 7, 8, 10, 11];
            var surroundNodeTypes = [1, 3, 4, 5, 7, 8];
        
            function createAncestorFinder(nodeTypes) {
              return function (node, selfIsAncestor) {
                var t,n = selfIsAncestor ? node : node.parentNode;
                while (n) {if (window.CP.shouldStopExecution(22)) break;
                  t = n.nodeType;
                  if (arrayContains(nodeTypes, t)) {
                    return n;
                  }
                  n = n.parentNode;
                }window.CP.exitedLoop(22);
                return null;
              };
            }
        
            var getDocumentOrFragmentContainer = createAncestorFinder([9, 11]);
            var getReadonlyAncestor = createAncestorFinder(readonlyNodeTypes);
            var getDocTypeNotationEntityAncestor = createAncestorFinder([6, 10, 12]);
        
            function assertNoDocTypeNotationEntityAncestor(node, allowSelf) {
              if (getDocTypeNotationEntityAncestor(node, allowSelf)) {
                throw new DOMException("INVALID_NODE_TYPE_ERR");
              }
            }
        
            function assertValidNodeType(node, invalidTypes) {
              if (!arrayContains(invalidTypes, node.nodeType)) {
                throw new DOMException("INVALID_NODE_TYPE_ERR");
              }
            }
        
            function assertValidOffset(node, offset) {
              if (offset < 0 || offset > (isCharacterDataNode(node) ? node.length : node.childNodes.length)) {
                throw new DOMException("INDEX_SIZE_ERR");
              }
            }
        
            function assertSameDocumentOrFragment(node1, node2) {
              if (getDocumentOrFragmentContainer(node1, true) !== getDocumentOrFragmentContainer(node2, true)) {
                throw new DOMException("WRONG_DOCUMENT_ERR");
              }
            }
        
            function assertNodeNotReadOnly(node) {
              if (getReadonlyAncestor(node, true)) {
                throw new DOMException("NO_MODIFICATION_ALLOWED_ERR");
              }
            }
        
            function assertNode(node, codeName) {
              if (!node) {
                throw new DOMException(codeName);
              }
            }
        
            function isValidOffset(node, offset) {
              return offset <= (isCharacterDataNode(node) ? node.length : node.childNodes.length);
            }
        
            function isRangeValid(range) {
              return !!range.startContainer && !!range.endContainer &&
              !(crashyTextNodes && (dom.isBrokenNode(range.startContainer) || dom.isBrokenNode(range.endContainer))) &&
              getRootContainer(range.startContainer) == getRootContainer(range.endContainer) &&
              isValidOffset(range.startContainer, range.startOffset) &&
              isValidOffset(range.endContainer, range.endOffset);
            }
        
            function assertRangeValid(range) {
              if (!isRangeValid(range)) {
                throw new Error("Range error: Range is not valid. This usually happens after DOM mutation. Range: (" + range.inspect() + ")");
              }
            }
        
            /*----------------------------------------------------------------------------------------------------------------*/
        
            // Test the browser's innerHTML support to decide how to implement createContextualFragment
            var styleEl = document.createElement("style");
            var htmlParsingConforms = false;
            try {
              styleEl.innerHTML = "<b>x</b>";
              htmlParsingConforms = styleEl.firstChild.nodeType == 3; // Opera incorrectly creates an element node
            } catch (e) {
              // IE 6 and 7 throw
            }
        
            api.features.htmlParsingConforms = htmlParsingConforms;
        
            var createContextualFragment = htmlParsingConforms ?
        
            // Implementation as per HTML parsing spec, trusting in the browser's implementation of innerHTML. See
            // discussion and base code for this implementation at issue 67.
            // Spec: http://html5.org/specs/dom-parsing.html#extensions-to-the-range-interface
            // Thanks to Aleks Williams.
            function (fragmentStr) {
              // "Let node the context object's start's node."
              var node = this.startContainer;
              var doc = getDocument(node);
        
              // "If the context object's start's node is null, raise an INVALID_STATE_ERR
              // exception and abort these steps."
              if (!node) {
                throw new DOMException("INVALID_STATE_ERR");
              }
        
              // "Let element be as follows, depending on node's interface:"
              // Document, Document Fragment: null
              var el = null;
        
              // "Element: node"
              if (node.nodeType == 1) {
                el = node;
        
                // "Text, Comment: node's parentElement"
              } else if (isCharacterDataNode(node)) {
                el = dom.parentElement(node);
              }
        
              // "If either element is null or element's ownerDocument is an HTML document
              // and element's local name is "html" and element's namespace is the HTML
              // namespace"
              if (el === null ||
              el.nodeName == "HTML" &&
              dom.isHtmlNamespace(getDocument(el).documentElement) &&
              dom.isHtmlNamespace(el))
              {
        
                // "let element be a new Element with "body" as its local name and the HTML
                // namespace as its namespace.""
                el = doc.createElement("body");
              } else {
                el = el.cloneNode(false);
              }
        
              // "If the node's document is an HTML document: Invoke the HTML fragment parsing algorithm."
              // "If the node's document is an XML document: Invoke the XML fragment parsing algorithm."
              // "In either case, the algorithm must be invoked with fragment as the input
              // and element as the context element."
              el.innerHTML = fragmentStr;
        
              // "If this raises an exception, then abort these steps. Otherwise, let new
              // children be the nodes returned."
        
              // "Let fragment be a new DocumentFragment."
              // "Append all new children to fragment."
              // "Return fragment."
              return dom.fragmentFromNodeChildren(el);
            } :
        
            // In this case, innerHTML cannot be trusted, so fall back to a simpler, non-conformant implementation that
            // previous versions of Rangy used (with the exception of using a body element rather than a div)
            function (fragmentStr) {
              var doc = getRangeDocument(this);
              var el = doc.createElement("body");
              el.innerHTML = fragmentStr;
        
              return dom.fragmentFromNodeChildren(el);
            };
        
            function splitRangeBoundaries(range, positionsToPreserve) {
              assertRangeValid(range);
        
              var sc = range.startContainer,so = range.startOffset,ec = range.endContainer,eo = range.endOffset;
              var startEndSame = sc === ec;
        
              if (isCharacterDataNode(ec) && eo > 0 && eo < ec.length) {
                splitDataNode(ec, eo, positionsToPreserve);
              }
        
              if (isCharacterDataNode(sc) && so > 0 && so < sc.length) {
                sc = splitDataNode(sc, so, positionsToPreserve);
                if (startEndSame) {
                  eo -= so;
                  ec = sc;
                } else if (ec == sc.parentNode && eo >= getNodeIndex(sc)) {
                  eo++;
                }
                so = 0;
              }
              range.setStartAndEnd(sc, so, ec, eo);
            }
        
            function rangeToHtml(range) {
              assertRangeValid(range);
              var container = range.commonAncestorContainer.parentNode.cloneNode(false);
              container.appendChild(range.cloneContents());
              return container.innerHTML;
            }
        
            /*----------------------------------------------------------------------------------------------------------------*/
        
            var rangeProperties = ["startContainer", "startOffset", "endContainer", "endOffset", "collapsed",
            "commonAncestorContainer"];
        
            var s2s = 0,s2e = 1,e2e = 2,e2s = 3;
            var n_b = 0,n_a = 1,n_b_a = 2,n_i = 3;
        
            util.extend(api.rangePrototype, {
              compareBoundaryPoints: function (how, range) {
                assertRangeValid(this);
                assertSameDocumentOrFragment(this.startContainer, range.startContainer);
        
                var nodeA, offsetA, nodeB, offsetB;
                var prefixA = how == e2s || how == s2s ? "start" : "end";
                var prefixB = how == s2e || how == s2s ? "start" : "end";
                nodeA = this[prefixA + "Container"];
                offsetA = this[prefixA + "Offset"];
                nodeB = range[prefixB + "Container"];
                offsetB = range[prefixB + "Offset"];
                return comparePoints(nodeA, offsetA, nodeB, offsetB);
              },
        
              insertNode: function (node) {
                assertRangeValid(this);
                assertValidNodeType(node, insertableNodeTypes);
                assertNodeNotReadOnly(this.startContainer);
        
                if (isOrIsAncestorOf(node, this.startContainer)) {
                  throw new DOMException("HIERARCHY_REQUEST_ERR");
                }
        
                // No check for whether the container of the start of the Range is of a type that does not allow
                // children of the type of node: the browser's DOM implementation should do this for us when we attempt
                // to add the node
        
                var firstNodeInserted = insertNodeAtPosition(node, this.startContainer, this.startOffset);
                this.setStartBefore(firstNodeInserted);
              },
        
              cloneContents: function () {
                assertRangeValid(this);
        
                var clone, frag;
                if (this.collapsed) {
                  return getRangeDocument(this).createDocumentFragment();
                } else {
                  if (this.startContainer === this.endContainer && isCharacterDataNode(this.startContainer)) {
                    clone = this.startContainer.cloneNode(true);
                    clone.data = clone.data.slice(this.startOffset, this.endOffset);
                    frag = getRangeDocument(this).createDocumentFragment();
                    frag.appendChild(clone);
                    return frag;
                  } else {
                    var iterator = new RangeIterator(this, true);
                    clone = cloneSubtree(iterator);
                    iterator.detach();
                  }
                  return clone;
                }
              },
        
              canSurroundContents: function () {
                assertRangeValid(this);
                assertNodeNotReadOnly(this.startContainer);
                assertNodeNotReadOnly(this.endContainer);
        
                // Check if the contents can be surrounded. Specifically, this means whether the range partially selects
                // no non-text nodes.
                var iterator = new RangeIterator(this, true);
                var boundariesInvalid = iterator._first && isNonTextPartiallySelected(iterator._first, this) ||
                iterator._last && isNonTextPartiallySelected(iterator._last, this);
                iterator.detach();
                return !boundariesInvalid;
              },
        
              surroundContents: function (node) {
                assertValidNodeType(node, surroundNodeTypes);
        
                if (!this.canSurroundContents()) {
                  throw new DOMException("INVALID_STATE_ERR");
                }
        
                // Extract the contents
                var content = this.extractContents();
        
                // Clear the children of the node
                if (node.hasChildNodes()) {
                  while (node.lastChild) {if (window.CP.shouldStopExecution(23)) break;
                    node.removeChild(node.lastChild);
                  }window.CP.exitedLoop(23);
                }
        
                // Insert the new node and add the extracted contents
                insertNodeAtPosition(node, this.startContainer, this.startOffset);
                node.appendChild(content);
        
                this.selectNode(node);
              },
        
              cloneRange: function () {
                assertRangeValid(this);
                var range = new Range(getRangeDocument(this));
                var i = rangeProperties.length,prop;
                while (i--) {if (window.CP.shouldStopExecution(24)) break;
                  prop = rangeProperties[i];
                  range[prop] = this[prop];
                }window.CP.exitedLoop(24);
                return range;
              },
        
              toString: function () {
                assertRangeValid(this);
                var sc = this.startContainer;
                if (sc === this.endContainer && isCharacterDataNode(sc)) {
                  return sc.nodeType == 3 || sc.nodeType == 4 ? sc.data.slice(this.startOffset, this.endOffset) : "";
                } else {
                  var textParts = [],iterator = new RangeIterator(this, true);
                  iterateSubtree(iterator, function (node) {
                    // Accept only text or CDATA nodes, not comments
                    if (node.nodeType == 3 || node.nodeType == 4) {
                      textParts.push(node.data);
                    }
                  });
                  iterator.detach();
                  return textParts.join("");
                }
              },
        
              // The methods below are all non-standard. The following batch were introduced by Mozilla but have since
              // been removed from Mozilla.
        
              compareNode: function (node) {
                assertRangeValid(this);
        
                var parent = node.parentNode;
                var nodeIndex = getNodeIndex(node);
        
                if (!parent) {
                  throw new DOMException("NOT_FOUND_ERR");
                }
        
                var startComparison = this.comparePoint(parent, nodeIndex),
                endComparison = this.comparePoint(parent, nodeIndex + 1);
        
                if (startComparison < 0) {// Node starts before
                  return endComparison > 0 ? n_b_a : n_b;
                } else {
                  return endComparison > 0 ? n_a : n_i;
                }
              },
        
              comparePoint: function (node, offset) {
                assertRangeValid(this);
                assertNode(node, "HIERARCHY_REQUEST_ERR");
                assertSameDocumentOrFragment(node, this.startContainer);
        
                if (comparePoints(node, offset, this.startContainer, this.startOffset) < 0) {
                  return -1;
                } else if (comparePoints(node, offset, this.endContainer, this.endOffset) > 0) {
                  return 1;
                }
                return 0;
              },
        
              createContextualFragment: createContextualFragment,
        
              toHtml: function () {
                return rangeToHtml(this);
              },
        
              // touchingIsIntersecting determines whether this method considers a node that borders a range intersects
              // with it (as in WebKit) or not (as in Gecko pre-1.9, and the default)
              intersectsNode: function (node, touchingIsIntersecting) {
                assertRangeValid(this);
                if (getRootConta.........完整代码请登录后点击上方下载按钮下载查看

网友评论0