How to use getSelectionListState method in wpt

Best JavaScript code snippet using wpt

engine.js

Source:engine.js Github

copy

Full Screen

...3607 movePreservingRanges(sublist, sublist.previousSibling, sublist.previousSibling.childNodes.length, range);3608 }3609 });3610}3611function getSelectionListState() {3612 // "Block-extend the active range, and let new range be the result."3613 var newRange = blockExtend(getActiveRange());3614 // "Let node list be a list of nodes, initially empty."3615 //3616 // "For each node contained in new range, append node to node list if the3617 // last member of node list (if any) is not an ancestor of node; node is3618 // editable; node is not an indentation element; and node is either an ol3619 // or ul, or the child of an ol or ul, or an allowed child of "li"."3620 var nodeList = getContainedNodes(newRange, function(node) {3621 return isEditable(node)3622 && !isIndentationElement(node)3623 && (isHtmlElement(node, ["ol", "ul"])3624 || isHtmlElement(node.parentNode, ["ol", "ul"])3625 || isAllowedChild(node, "li"));3626 });3627 // "If node list is empty, return "none"."3628 if (!nodeList.length) {3629 return "none";3630 }3631 // "If every member of node list is either an ol or the child of an ol or3632 // the child of an li child of an ol, and none is a ul or an ancestor of a3633 // ul, return "ol"."3634 if ($_(nodeList).every(function(node) {3635 return isHtmlElement(node, "ol")3636 || isHtmlElement(node.parentNode, "ol")3637 || (isHtmlElement(node.parentNode, "li") && isHtmlElement(node.parentNode.parentNode, "ol"));3638 })3639 && !$_( nodeList ).some(function(node) { return isHtmlElement(node, "ul") || ("querySelector" in node && node.querySelector("ul")) })) {3640 return "ol";3641 }3642 // "If every member of node list is either a ul or the child of a ul or the3643 // child of an li child of a ul, and none is an ol or an ancestor of an ol,3644 // return "ul"."3645 if ($_(nodeList).every(function(node) {3646 return isHtmlElement(node, "ul")3647 || isHtmlElement(node.parentNode, "ul")3648 || (isHtmlElement(node.parentNode, "li") && isHtmlElement(node.parentNode.parentNode, "ul"));3649 })3650 && !$_( nodeList ).some(function(node) { return isHtmlElement(node, "ol") || ("querySelector" in node && node.querySelector("ol")) })) {3651 return "ul";3652 }3653 var hasOl = $_( nodeList ).some(function(node) {3654 return isHtmlElement(node, "ol")3655 || isHtmlElement(node.parentNode, "ol")3656 || ("querySelector" in node && node.querySelector("ol"))3657 || (isHtmlElement(node.parentNode, "li") && isHtmlElement(node.parentNode.parentNode, "ol"));3658 });3659 var hasUl = $_( nodeList ).some(function(node) {3660 return isHtmlElement(node, "ul")3661 || isHtmlElement(node.parentNode, "ul")3662 || ("querySelector" in node && node.querySelector("ul"))3663 || (isHtmlElement(node.parentNode, "li") && isHtmlElement(node.parentNode.parentNode, "ul"));3664 });3665 // "If some member of node list is either an ol or the child or ancestor of3666 // an ol or the child of an li child of an ol, and some member of node list3667 // is either a ul or the child or ancestor of a ul or the child of an li3668 // child of a ul, return "mixed"."3669 if (hasOl && hasUl) {3670 return "mixed";3671 }3672 // "If some member of node list is either an ol or the child or ancestor of3673 // an ol or the child of an li child of an ol, return "mixed ol"."3674 if (hasOl) {3675 return "mixed ol";3676 }3677 // "If some member of node list is either a ul or the child or ancestor of3678 // a ul or the child of an li child of a ul, return "mixed ul"."3679 if (hasUl) {3680 return "mixed ul";3681 }3682 // "Return "none"."3683 return "none";3684}3685function getAlignmentValue(node) {3686 // "While node is neither null nor an Element, or it is an Element but its3687 // "display" property has resolved value "inline" or "none", set node to3688 // its parent."3689 while ((node && node.nodeType != $_.Node.ELEMENT_NODE)3690 || (node.nodeType == $_.Node.ELEMENT_NODE3691 && $_(["inline", "none"]).indexOf($_.getComputedStyle(node).display) != -1)) {3692 node = node.parentNode;3693 }3694 // "If node is not an Element, return "left"."3695 if (!node || node.nodeType != $_.Node.ELEMENT_NODE) {3696 return "left";3697 }3698 var resolvedValue = $_.getComputedStyle(node).textAlign3699 // Hack around browser non-standardness3700 .replace(/^-(moz|webkit)-/, "")3701 .replace(/^auto$/, "start");3702 // "If node's "text-align" property has resolved value "start", return3703 // "left" if the directionality of node is "ltr", "right" if it is "rtl"."3704 if (resolvedValue == "start") {3705 return getDirectionality(node) == "ltr" ? "left" : "right";3706 }3707 // "If node's "text-align" property has resolved value "end", return3708 // "right" if the directionality of node is "ltr", "left" if it is "rtl"."3709 if (resolvedValue == "end") {3710 return getDirectionality(node) == "ltr" ? "right" : "left";3711 }3712 // "If node's "text-align" property has resolved value "center", "justify",3713 // "left", or "right", return that value."3714 if ($_(["center", "justify", "left", "right"]).indexOf(resolvedValue) != -1) {3715 return resolvedValue;3716 }3717 // "Return "left"."3718 return "left";3719}3720//@}3721///// Block-extending a range /////3722//@{3723// "A boundary point (node, offset) is a block start point if either node's3724// parent is null and offset is zero; or node has a child with index offset −3725// 1, and that child is either a visible block node or a visible br."3726function isBlockStartPoint(node, offset) {3727 return (!node.parentNode && offset == 0)3728 || (0 <= offset - 13729 && offset - 1 < node.childNodes.length3730 && isVisible(node.childNodes[offset - 1])3731 && (isBlockNode(node.childNodes[offset - 1])3732 || isHtmlElement(node.childNodes[offset - 1], "br")));3733}3734// "A boundary point (node, offset) is a block end point if either node's3735// parent is null and offset is node's length; or node has a child with index3736// offset, and that child is a visible block node."3737function isBlockEndPoint(node, offset) {3738 return (!node.parentNode && offset == getNodeLength(node))3739 || (offset < node.childNodes.length3740 && isVisible(node.childNodes[offset])3741 && isBlockNode(node.childNodes[offset]));3742}3743// "A boundary point is a block boundary point if it is either a block start3744// point or a block end point."3745function isBlockBoundaryPoint(node, offset) {3746 return isBlockStartPoint(node, offset)3747 || isBlockEndPoint(node, offset);3748}3749function blockExtend(range) {3750 // "Let start node, start offset, end node, and end offset be the start3751 // and end nodes and offsets of the range."3752 var startNode = range.startContainer;3753 var startOffset = range.startOffset;3754 var endNode = range.endContainer;3755 var endOffset = range.endOffset;3756 // "If some ancestor container of start node is an li, set start offset to3757 // the index of the last such li in tree order, and set start node to that3758 // li's parent."3759 var liAncestors = $_( getAncestors(startNode).concat(startNode) )3760 .filter(function(ancestor) { return isHtmlElement(ancestor, "li") })3761 .slice(-1);3762 if (liAncestors.length) {3763 startOffset = getNodeIndex(liAncestors[0]);3764 startNode = liAncestors[0].parentNode;3765 }3766 // "If (start node, start offset) is not a block start point, repeat the3767 // following steps:"3768 if (!isBlockStartPoint(startNode, startOffset)) do {3769 // "If start offset is zero, set it to start node's index, then set3770 // start node to its parent."3771 if (startOffset == 0) {3772 startOffset = getNodeIndex(startNode);3773 startNode = startNode.parentNode;3774 // "Otherwise, subtract one from start offset."3775 } else {3776 startOffset--;3777 }3778 // "If (start node, start offset) is a block boundary point, break from3779 // this loop."3780 } while (!isBlockBoundaryPoint(startNode, startOffset));3781 // "While start offset is zero and start node's parent is not null, set3782 // start offset to start node's index, then set start node to its parent."3783 while (startOffset == 03784 && startNode.parentNode) {3785 startOffset = getNodeIndex(startNode);3786 startNode = startNode.parentNode;3787 }3788 // "If some ancestor container of end node is an li, set end offset to one3789 // plus the index of the last such li in tree order, and set end node to3790 // that li's parent."3791 var liAncestors = $_( getAncestors(endNode).concat(endNode) )3792 .filter(function(ancestor) { return isHtmlElement(ancestor, "li") })3793 .slice(-1);3794 if (liAncestors.length) {3795 endOffset = 1 + getNodeIndex(liAncestors[0]);3796 endNode = liAncestors[0].parentNode;3797 }3798 // "If (end node, end offset) is not a block end point, repeat the3799 // following steps:"3800 if (!isBlockEndPoint(endNode, endOffset)) do {3801 // "If end offset is end node's length, set it to one plus end node's3802 // index, then set end node to its parent."3803 if (endOffset == getNodeLength(endNode)) {3804 endOffset = 1 + getNodeIndex(endNode);3805 endNode = endNode.parentNode;3806 // "Otherwise, add one to end offset.3807 } else {3808 endOffset++;3809 }3810 // "If (end node, end offset) is a block boundary point, break from3811 // this loop."3812 } while (!isBlockBoundaryPoint(endNode, endOffset));3813 // "While end offset is end node's length and end node's parent is not3814 // null, set end offset to one plus end node's index, then set end node to3815 // its parent."3816 while (endOffset == getNodeLength(endNode)3817 && endNode.parentNode) {3818 endOffset = 1 + getNodeIndex(endNode);3819 endNode = endNode.parentNode;3820 }3821 // "Let new range be a new range whose start and end nodes and offsets3822 // are start node, start offset, end node, and end offset."3823 var newRange = Aloha.createRange();3824 newRange.setStart(startNode, startOffset);3825 newRange.setEnd(endNode, endOffset);3826 // "Return new range."3827 return newRange;3828}3829function followsLineBreak(node) {3830 // "Let offset be zero."3831 var offset = 0;3832 // "While (node, offset) is not a block boundary point:"3833 while (!isBlockBoundaryPoint(node, offset)) {3834 // "If node has a visible child with index offset minus one, return3835 // false."3836 if (0 <= offset - 13837 && offset - 1 < node.childNodes.length3838 && isVisible(node.childNodes[offset - 1])) {3839 return false;3840 }3841 // "If offset is zero or node has no children, set offset to node's3842 // index, then set node to its parent."3843 if (offset == 03844 || !node.hasChildNodes()) {3845 offset = getNodeIndex(node);3846 node = node.parentNode;3847 // "Otherwise, set node to its child with index offset minus one, then3848 // set offset to node's length."3849 } else {3850 node = node.childNodes[offset - 1];3851 offset = getNodeLength(node);3852 }3853 }3854 // "Return true."3855 return true;3856}3857function precedesLineBreak(node) {3858 // "Let offset be node's length."3859 var offset = getNodeLength(node);3860 // "While (node, offset) is not a block boundary point:"3861 while (!isBlockBoundaryPoint(node, offset)) {3862 // "If node has a visible child with index offset, return false."3863 if (offset < node.childNodes.length3864 && isVisible(node.childNodes[offset])) {3865 return false;3866 }3867 // "If offset is node's length or node has no children, set offset to3868 // one plus node's index, then set node to its parent."3869 if (offset == getNodeLength(node)3870 || !node.hasChildNodes()) {3871 offset = 1 + getNodeIndex(node);3872 node = node.parentNode;3873 // "Otherwise, set node to its child with index offset and set offset3874 // to zero."3875 } else {3876 node = node.childNodes[offset];3877 offset = 0;3878 }3879 }3880 // "Return true."3881 return true;3882}3883//@}3884///// Recording and restoring overrides /////3885//@{3886function recordCurrentOverrides( range ) {3887 // "Let overrides be a list of (string, string or boolean) ordered pairs,3888 // initially empty."3889 var overrides = [];3890 // "If there is a value override for "createLink", add ("createLink", value3891 // override for "createLink") to overrides."3892 if (getValueOverride("createlink" ,range) !== undefined) {3893 overrides.push(["createlink", getValueOverride("createlink", range)]);3894 }3895 // "For each command in the list "bold", "italic", "strikethrough",3896 // "subscript", "superscript", "underline", in order: if there is a state3897 // override for command, add (command, command's state override) to3898 // overrides."3899 $_( ["bold", "italic", "strikethrough", "subscript", "superscript",3900 "underline"] ).forEach(function(command) {3901 if (getStateOverride(command, range) !== undefined) {3902 overrides.push([command, getStateOverride(command, range)]);3903 }3904 });3905 // "For each command in the list "fontName", "fontSize", "foreColor",3906 // "hiliteColor", in order: if there is a value override for command, add3907 // (command, command's value override) to overrides."3908 $_( ["fontname", "fontsize", "forecolor",3909 "hilitecolor"] ).forEach(function(command) {3910 if (getValueOverride(command, range) !== undefined) {3911 overrides.push([command, getValueOverride(command, range)]);3912 }3913 });3914 // "Return overrides."3915 return overrides;3916}3917function recordCurrentStatesAndValues(range) {3918 // "Let overrides be a list of (string, string or boolean) ordered pairs,3919 // initially empty."3920 var overrides = [];3921 // "Let node be the first editable Text node effectively contained in the3922 // active range, or null if there is none."3923 var node = $_( getAllEffectivelyContainedNodes(range) )3924 .filter(function(node) { return isEditable(node) && node.nodeType == $_.Node.TEXT_NODE })[0];3925 // "If node is null, return overrides."3926 if (!node) {3927 return overrides;3928 }3929 // "Add ("createLink", value for "createLink") to overrides."3930 overrides.push(["createlink", commands.createlink.value(range)]);3931 // "For each command in the list "bold", "italic", "strikethrough",3932 // "subscript", "superscript", "underline", in order: if node's effective3933 // command value for command is one of its inline command activated values,3934 // add (command, true) to overrides, and otherwise add (command, false) to3935 // overrides."3936 $_( ["bold", "italic", "strikethrough", "subscript", "superscript",3937 "underline"] ).forEach(function(command) {3938 if ($_(commands[command].inlineCommandActivatedValues)3939 .indexOf(getEffectiveCommandValue(node, command)) != -1) {3940 overrides.push([command, true]);3941 } else {3942 overrides.push([command, false]);3943 }3944 });3945 // "For each command in the list "fontName", "foreColor", "hiliteColor", in3946 // order: add (command, command's value) to overrides."3947 $_( ["fontname", "fontsize", "forecolor", "hilitecolor"] ).forEach(function(command) {3948 overrides.push([command, commands[command].value(range)]);3949 });3950 // "Add ("fontSize", node's effective command value for "fontSize") to3951 // overrides."3952 overrides.push("fontsize", getEffectiveCommandValue(node, "fontsize"));3953 // "Return overrides."3954 return overrides;3955}3956function restoreStatesAndValues(overrides, range) {3957 // "Let node be the first editable Text node effectively contained in the3958 // active range, or null if there is none."3959 var node = $_( getAllEffectivelyContainedNodes(range) )3960 .filter(function(node) { return isEditable(node) && node.nodeType == $_.Node.TEXT_NODE })[0];3961 // "If node is not null, then for each (command, override) pair in3962 // overrides, in order:"3963 if (node) {3964 for (var i = 0; i < overrides.length; i++) {3965 var command = overrides[i][0];3966 var override = overrides[i][1];3967 // "If override is a boolean, and queryCommandState(command)3968 // returns something different from override, call3969 // execCommand(command)."3970 if (typeof override == "boolean"3971 && myQueryCommandState(command) != override) {3972 myExecCommand(command);3973 // "Otherwise, if override is a string, and command is not3974 // "fontSize", and queryCommandValue(command) returns something not3975 // equivalent to override, call execCommand(command, false,3976 // override)."3977 } else if (typeof override == "string"3978 && command != "fontsize"3979 && !areEquivalentValues(command, myQueryCommandValue(command), override)) {3980 myExecCommand(command, false, override);3981 // "Otherwise, if override is a string; and command is "fontSize";3982 // and either there is a value override for "fontSize" that is not3983 // equal to override, or there is no value override for "fontSize"3984 // and node's effective command value for "fontSize" is not loosely3985 // equivalent to override: call execCommand("fontSize", false,3986 // override)."3987 } else if (typeof override == "string"3988 && command == "fontsize"3989 && (3990 (3991 getValueOverride("fontsize", range) !== undefined3992 && getValueOverride("fontsize", range) !== override3993 ) || (3994 getValueOverride("fontsize", range) === undefined3995 && !areLooselyEquivalentValues(command, getEffectiveCommandValue(node, "fontsize"), override)3996 )3997 )) {3998 myExecCommand("fontsize", false, override);3999 // "Otherwise, continue this loop from the beginning."4000 } else {4001 continue;4002 }4003 // "Set node to the first editable Text node effectively contained4004 // in the active range, if there is one."4005 node = $_( getAllEffectivelyContainedNodes(range) )4006 .filter(function(node) { return isEditable(node) && node.nodeType == $_.Node.TEXT_NODE })[0]4007 || node;4008 }4009 // "Otherwise, for each (command, override) pair in overrides, in order:"4010 } else {4011 for (var i = 0; i < overrides.length; i++) {4012 var command = overrides[i][0];4013 var override = overrides[i][1];4014 // "If override is a boolean, set the state override for command to4015 // override."4016 if (typeof override == "boolean") {4017 setStateOverride(command, override, range);4018 }4019 // "If override is a string, set the value override for command to4020 // override."4021 if (typeof override == "string") {4022 setValueOverride(command, override, range);4023 }4024 }4025 }4026}4027//@}4028///// Deleting the contents of a range /////4029//@{4030function deleteContents() {4031 // We accept several different calling conventions:4032 //4033 // 1) A single argument, which is a range.4034 //4035 // 2) Two arguments, the first being a range and the second flags.4036 //4037 // 3) Four arguments, the start and end of a range.4038 //4039 // 4) Five arguments, the start and end of a range plus flags.4040 //4041 // The flags argument is a dictionary that can have up to two keys,4042 // blockMerging and stripWrappers, whose corresponding values are4043 // interpreted as boolean. E.g., {stripWrappers: false}.4044 var range;4045 var flags = {};4046 if (arguments.length < 3) {4047 range = arguments[0];4048 } else {4049 range = Aloha.createRange();4050 range.setStart(arguments[0], arguments[1]);4051 range.setEnd(arguments[2], arguments[3]);4052 }4053 if (arguments.length == 2) {4054 flags = arguments[1];4055 }4056 if (arguments.length == 5) {4057 flags = arguments[4];4058 }4059 var blockMerging = "blockMerging" in flags ? !!flags.blockMerging : true;4060 var stripWrappers = "stripWrappers" in flags ? !!flags.stripWrappers : true;4061 // "If range is null, abort these steps and do nothing."4062 if (!range) {4063 return;4064 }4065 // "Let start node, start offset, end node, and end offset be range's start4066 // and end nodes and offsets."4067 var startNode = range.startContainer;4068 var startOffset = range.startOffset;4069 var endNode = range.endContainer;4070 var endOffset = range.endOffset;4071 // "While start node has at least one child:"4072 while (startNode.hasChildNodes()) {4073 // "If start offset is start node's length, and start node's parent is4074 // in the same editing host, and start node is an inline node, set4075 // start offset to one plus the index of start node, then set start4076 // node to its parent and continue this loop from the beginning."4077 if (startOffset == getNodeLength(startNode)4078 && inSameEditingHost(startNode, startNode.parentNode)4079 && isInlineNode(startNode)) {4080 startOffset = 1 + getNodeIndex(startNode);4081 startNode = startNode.parentNode;4082 continue;4083 }4084 // "If start offset is start node's length, break from this loop."4085 if (startOffset == getNodeLength(startNode)) {4086 break;4087 }4088 // "Let reference node be the child of start node with index equal to4089 // start offset."4090 var referenceNode = startNode.childNodes[startOffset];4091 // "If reference node is a block node or an Element with no children,4092 // or is neither an Element nor a Text node, break from this loop."4093 if (isBlockNode(referenceNode)4094 || (referenceNode.nodeType == $_.Node.ELEMENT_NODE4095 && !referenceNode.hasChildNodes())4096 || (referenceNode.nodeType != $_.Node.ELEMENT_NODE4097 && referenceNode.nodeType != $_.Node.TEXT_NODE)) {4098 break;4099 }4100 // "Set start node to reference node and start offset to 0."4101 startNode = referenceNode;4102 startOffset = 0;4103 }4104 // "While end node has at least one child:"4105 while (endNode.hasChildNodes()) {4106 // "If end offset is 0, and end node's parent is in the same editing4107 // host, and end node is an inline node, set end offset to the index of4108 // end node, then set end node to its parent and continue this loop4109 // from the beginning."4110 if (endOffset == 04111 && inSameEditingHost(endNode, endNode.parentNode)4112 && isInlineNode(endNode)) {4113 endOffset = getNodeIndex(endNode);4114 endNode = endNode.parentNode;4115 continue;4116 }4117 // "If end offset is 0, break from this loop."4118 if (endOffset == 0) {4119 break;4120 }4121 // "Let reference node be the child of end node with index equal to end4122 // offset minus one."4123 var referenceNode = endNode.childNodes[endOffset - 1];4124 // "If reference node is a block node or an Element with no children,4125 // or is neither an Element nor a Text node, break from this loop."4126 if (isBlockNode(referenceNode)4127 || (referenceNode.nodeType == $_.Node.ELEMENT_NODE4128 && !referenceNode.hasChildNodes())4129 || (referenceNode.nodeType != $_.Node.ELEMENT_NODE4130 && referenceNode.nodeType != $_.Node.TEXT_NODE)) {4131 break;4132 }4133 // "Set end node to reference node and end offset to the length of4134 // reference node."4135 endNode = referenceNode;4136 endOffset = getNodeLength(referenceNode);4137 }4138 // "If (end node, end offset) is not after (start node, start offset), set4139 // range's end to its start and abort these steps."4140 if (getPosition(endNode, endOffset, startNode, startOffset) !== "after") {4141 range.setEnd(range.startContainer, range.startOffset);4142 return;4143 }4144 // "If start node is a Text node and start offset is 0, set start offset to4145 // the index of start node, then set start node to its parent."4146 if (startNode.nodeType == $_.Node.TEXT_NODE4147 && startOffset == 04148 && startNode != endNode) {4149// startOffset = getNodeIndex(startNode);4150// startNode = startNode.parentNode;4151 }4152 // "If end node is a Text node and end offset is its length, set end offset4153 // to one plus the index of end node, then set end node to its parent."4154 if (endNode.nodeType == $_.Node.TEXT_NODE4155 && endOffset == getNodeLength(endNode)4156 && startNode != endNode) {4157 endOffset = 1 + getNodeIndex(endNode);4158 endNode = endNode.parentNode;4159 }4160 // "Set range's start to (start node, start offset) and its end to (end4161 // node, end offset)."4162 range.setStart(startNode, startOffset);4163 range.setEnd(endNode, endOffset);4164 // "Let start block be the start node of range."4165 var startBlock = range.startContainer;4166 // "While start block's parent is in the same editing host and start block4167 // is an inline node, set start block to its parent."4168 while (inSameEditingHost(startBlock, startBlock.parentNode)4169 && isInlineNode(startBlock)) {4170 startBlock = startBlock.parentNode;4171 }4172 // "If start block is neither a block node nor an editing host, or "span"4173 // is not an allowed child of start block, or start block is a td or th,4174 // set start block to null."4175 if ((!isBlockNode(startBlock) && !isEditingHost(startBlock))4176 || !isAllowedChild("span", startBlock)4177 || isHtmlElement(startBlock, ["td", "th"])) {4178 startBlock = null;4179 }4180 // "Let end block be the end node of range."4181 var endBlock = range.endContainer;4182 4183 // "While end block's parent is in the same editing host and end block is4184 // an inline node, set end block to its parent."4185 while (inSameEditingHost(endBlock, endBlock.parentNode)4186 && isInlineNode(endBlock)) {4187 endBlock = endBlock.parentNode;4188 }4189 4190 // "If end block is neither a block node nor an editing host, or "span" is4191 // not an allowed child of end block, or end block is a td or th, set end4192 // block to null."4193 if ((!isBlockNode(endBlock) && !isEditingHost(endBlock))4194 || !isAllowedChild("span", endBlock)4195 || isHtmlElement(endBlock, ["td", "th"])) {4196 endBlock = null;4197 }4198 // "Record current states and values, and let overrides be the result."4199 var overrides = recordCurrentStatesAndValues(range);4200 // "If start node and end node are the same, and start node is an editable4201 // Text node:"4202 if (startNode == endNode4203 && isEditable(startNode)4204 && startNode.nodeType == $_.Node.TEXT_NODE) {4205 // "Let parent be the parent of node."4206 var parent_ = startNode.parentNode;4207 // "Call deleteData(start offset, end offset − start offset) on start4208 // node."4209 startNode.deleteData(startOffset, endOffset - startOffset);4210 // "Canonicalize whitespace at (start node, start offset)."4211 canonicalizeWhitespace(startNode, startOffset);4212 // "Set range's end to its start."4213 range.setEnd(range.startContainer, range.startOffset);4214 // "Restore states and values from overrides."4215 restoreStatesAndValues(overrides, range);4216 // "If parent is editable or an editing host, is not an inline node,4217 // and has no children, call createElement("br") on the context object4218 // and append the result as the last child of parent."4219 // only do this, if the offsetHeight is 04220 if ((isEditable(parent_) || isEditingHost(parent_))4221 && !isInlineNode(parent_)4222 && parent_.offsetHeight === 0) {4223 parent_.appendChild(createEndBreak());4224 }4225 // "Abort these steps."4226 return;4227 }4228 // "If start node is an editable Text node, call deleteData() on it, with4229 // start offset as the first argument and (length of start node − start4230 // offset) as the second argument."4231 if (isEditable(startNode)4232 && startNode.nodeType == $_.Node.TEXT_NODE) {4233 startNode.deleteData(startOffset, getNodeLength(startNode) - startOffset);4234 }4235 // "Let node list be a list of nodes, initially empty."4236 //4237 // "For each node contained in range, append node to node list if the last4238 // member of node list (if any) is not an ancestor of node; node is4239 // editable; and node is not a thead, tbody, tfoot, tr, th, or td."4240 var nodeList = getContainedNodes(range,4241 function(node) {4242 return isEditable(node)4243 && !isHtmlElement(node, ["thead", "tbody", "tfoot", "tr", "th", "td"]);4244 }4245 );4246 // "For each node in node list:"4247 for (var i = 0; i < nodeList.length; i++) {4248 var node = nodeList[i];4249 // "Let parent be the parent of node."4250 var parent_ = node.parentNode;4251 // "Remove node from parent."4252 parent_.removeChild(node);4253 // "If strip wrappers is true or parent is not an ancestor container of4254 // start node, while parent is an editable inline node with length 0,4255 // let grandparent be the parent of parent, then remove parent from4256 // grandparent, then set parent to grandparent."4257 if (stripWrappers4258 || (!isAncestor(parent_, startNode) && parent_ != startNode)) {4259 while (isEditable(parent_)4260 && isInlineNode(parent_)4261 && getNodeLength(parent_) == 0) {4262 var grandparent = parent_.parentNode;4263 grandparent.removeChild(parent_);4264 parent_ = grandparent;4265 }4266 }4267 // "If parent is editable or an editing host, is not an inline node,4268 // and has no children, call createElement("br") on the context object4269 // and append the result as the last child of parent."4270 // only do this, if the offsetHeight is 04271 if ((isEditable(parent_) || isEditingHost(parent_))4272 && !isInlineNode(parent_)4273 && !parent_.hasChildNodes()4274 && parent_.offsetHeight === 0) {4275 parent_.appendChild(createEndBreak());4276 }4277 }4278 // "If end node is an editable Text node, call deleteData(0, end offset) on4279 // it."4280 if (isEditable(endNode)4281 && endNode.nodeType == $_.Node.TEXT_NODE) {4282 endNode.deleteData(0, endOffset);4283 }4284 // "Canonicalize whitespace at range's start."4285 canonicalizeWhitespace(range.startContainer, range.startOffset);4286 // "Canonicalize whitespace at range's end."4287 canonicalizeWhitespace(range.endContainer, range.endOffset);4288 // "If block merging is false, or start block or end block is null, or4289 // start block is not in the same editing host as end block, or start block4290 // and end block are the same:"4291 if (!blockMerging4292 || !startBlock4293 || !endBlock4294 || !inSameEditingHost(startBlock, endBlock)4295 || startBlock == endBlock) {4296 // "Set range's end to its start."4297 range.setEnd(range.startContainer, range.startOffset);4298 // "Restore states and values from overrides."4299 restoreStatesAndValues(overrides, range);4300 // "Abort these steps."4301 return;4302 }4303 // "If start block has one child, which is a collapsed block prop, remove4304 // its child from it."4305 if (startBlock.children.length == 14306 && isCollapsedBlockProp(startBlock.firstChild)) {4307 startBlock.removeChild(startBlock.firstChild);4308 }4309 // "If end block has one child, which is a collapsed block prop, remove its4310 // child from it."4311 if (endBlock.children.length == 14312 && isCollapsedBlockProp(endBlock.firstChild)) {4313 endBlock.removeChild(endBlock.firstChild);4314 }4315 // "If start block is an ancestor of end block:"4316 if (isAncestor(startBlock, endBlock)) {4317 // "Let reference node be end block."4318 var referenceNode = endBlock;4319 // "While reference node is not a child of start block, set reference4320 // node to its parent."4321 while (referenceNode.parentNode != startBlock) {4322 referenceNode = referenceNode.parentNode;4323 }4324 // "Set the start and end of range to (start block, index of reference4325 // node)."4326 range.setStart(startBlock, getNodeIndex(referenceNode));4327 range.setEnd(startBlock, getNodeIndex(referenceNode));4328 // "If end block has no children:"4329 if (!endBlock.hasChildNodes()) {4330 // "While end block is editable and is the only child of its parent4331 // and is not a child of start block, let parent equal end block,4332 // then remove end block from parent, then set end block to4333 // parent."4334 while (isEditable(endBlock)4335 && endBlock.parentNode.childNodes.length == 14336 && endBlock.parentNode != startBlock) {4337 var parent_ = endBlock;4338 parent_.removeChild(endBlock);4339 endBlock = parent_;4340 }4341 // "If end block is editable and is not an inline node, and its4342 // previousSibling and nextSibling are both inline nodes, call4343 // createElement("br") on the context object and insert it into end4344 // block's parent immediately after end block."4345 if (isEditable(endBlock)4346 && !isInlineNode(endBlock)4347 && isInlineNode(endBlock.previousSibling)4348 && isInlineNode(endBlock.nextSibling)) {4349 endBlock.parentNode.insertBefore(document.createElement("br"), endBlock.nextSibling);4350 }4351 // "If end block is editable, remove it from its parent."4352 if (isEditable(endBlock)) {4353 endBlock.parentNode.removeChild(endBlock);4354 }4355 // "Restore states and values from overrides."4356 restoreStatesAndValues(overrides, range);4357 // "Abort these steps."4358 return;4359 }4360 // "If end block's firstChild is not an inline node, restore states and4361 // values from overrides, then abort these steps."4362 if (!isInlineNode(endBlock.firstChild)) {4363 restoreStatesAndValues(overrides, range);4364 return;4365 }4366 // "Let children be a list of nodes, initially empty."4367 var children = [];4368 // "Append the first child of end block to children."4369 children.push(endBlock.firstChild);4370 // "While children's last member is not a br, and children's last4371 // member's nextSibling is an inline node, append children's last4372 // member's nextSibling to children."4373 while (!isHtmlElement(children[children.length - 1], "br")4374 && isInlineNode(children[children.length - 1].nextSibling)) {4375 children.push(children[children.length - 1].nextSibling);4376 }4377 // "Record the values of children, and let values be the result."4378 var values = recordValues(children);4379 // "While children's first member's parent is not start block, split4380 // the parent of children."4381 while (children[0].parentNode != startBlock) {4382 splitParent(children, range);4383 }4384 // "If children's first member's previousSibling is an editable br,4385 // remove that br from its parent."4386 if (isEditable(children[0].previousSibling)4387 && isHtmlElement(children[0].previousSibling, "br")) {4388 children[0].parentNode.removeChild(children[0].previousSibling);4389 }4390 // "Otherwise, if start block is a descendant of end block:"4391 } else if (isDescendant(startBlock, endBlock)) {4392 // "Set the start and end of range to (start block, length of start4393 // block)."4394 range.setStart(startBlock, getNodeLength(startBlock));4395 range.setEnd(startBlock, getNodeLength(startBlock));4396 // "Let reference node be start block."4397 var referenceNode = startBlock;4398 // "While reference node is not a child of end block, set reference4399 // node to its parent."4400 while (referenceNode.parentNode != endBlock) {4401 referenceNode = referenceNode.parentNode;4402 }4403 // "If reference node's nextSibling is an inline node and start block's4404 // lastChild is a br, remove start block's lastChild from it."4405 if (isInlineNode(referenceNode.nextSibling)4406 && isHtmlElement(startBlock.lastChild, "br")) {4407 startBlock.removeChild(startBlock.lastChild);4408 }4409 // "Let nodes to move be a list of nodes, initially empty."4410 var nodesToMove = [];4411 // "If reference node's nextSibling is neither null nor a br nor a4412 // block node, append it to nodes to move."4413 if (referenceNode.nextSibling4414 && !isHtmlElement(referenceNode.nextSibling, "br")4415 && !isBlockNode(referenceNode.nextSibling)) {4416 nodesToMove.push(referenceNode.nextSibling);4417 }4418 // "While nodes to move is nonempty and its last member's nextSibling4419 // is neither null nor a br nor a block node, append it to nodes to4420 // move."4421 if (nodesToMove.length4422 && nodesToMove[nodesToMove.length - 1].nextSibling4423 && !isHtmlElement(nodesToMove[nodesToMove.length - 1].nextSibling, "br")4424 && !isBlockNode(nodesToMove[nodesToMove.length - 1].nextSibling)) {4425 nodesToMove.push(nodesToMove[nodesToMove.length - 1].nextSibling);4426 }4427 // "Record the values of nodes to move, and let values be the result."4428 var values = recordValues(nodesToMove);4429 // "For each node in nodes to move, append node as the last child of4430 // start block, preserving ranges."4431 $_( nodesToMove ).forEach(function(node) {4432 movePreservingRanges(node, startBlock, -1, range);4433 });4434 // "If the nextSibling of reference node is a br, remove it from its4435 // parent."4436 if (isHtmlElement(referenceNode.nextSibling, "br")) {4437 referenceNode.parentNode.removeChild(referenceNode.nextSibling);4438 }4439 // "Otherwise:"4440 } else {4441 // "Set the start and end of range to (start block, length of start4442 // block)."4443 range.setStart(startBlock, getNodeLength(startBlock));4444 range.setEnd(startBlock, getNodeLength(startBlock));4445 // "If end block's firstChild is an inline node and start block's4446 // lastChild is a br, remove start block's lastChild from it."4447 if (isInlineNode(endBlock.firstChild)4448 && isHtmlElement(startBlock.lastChild, "br")) {4449 startBlock.removeChild(startBlock.lastChild);4450 }4451 // "Record the values of end block's children, and let values be the4452 // result."4453 var values = recordValues([].slice.call(toArray(endBlock.childNodes)));4454 // "While end block has children, append the first child of end block4455 // to start block, preserving ranges."4456 while (endBlock.hasChildNodes()) {4457 movePreservingRanges(endBlock.firstChild, startBlock, -1, range);4458 }4459 // "While end block has no children, let parent be the parent of end4460 // block, then remove end block from parent, then set end block to4461 // parent."4462 while (!endBlock.hasChildNodes()) {4463 var parent_ = endBlock.parentNode;4464 parent_.removeChild(endBlock);4465 endBlock = parent_;4466 }4467 }4468 // "Restore the values from values."4469 restoreValues(values, range);4470 // "If start block has no children, call createElement("br") on the context4471 // object and append the result as the last child of start block."4472 if (!startBlock.hasChildNodes() && startBlock.offsetHeight == 0) {4473 startBlock.appendChild(createEndBreak());4474 }4475 // "Restore states and values from overrides."4476 restoreStatesAndValues(overrides, range);4477}4478//@}4479///// Splitting a node list's parent /////4480//@{4481function splitParent(nodeList, range) {4482 // "Let original parent be the parent of the first member of node list."4483 var originalParent = nodeList[0].parentNode;4484 // "If original parent is not editable or its parent is null, do nothing4485 // and abort these steps."4486 if (!isEditable(originalParent)4487 || !originalParent.parentNode) {4488 return;4489 }4490 // "If the first child of original parent is in node list, remove4491 // extraneous line breaks before original parent."4492 if ($_(nodeList).indexOf(originalParent.firstChild) != -1) {4493 removeExtraneousLineBreaksBefore(originalParent);4494 }4495 // "If the first child of original parent is in node list, and original4496 // parent follows a line break, set follows line break to true. Otherwise,4497 // set follows line break to false."4498 var followsLineBreak_ = $_(nodeList).indexOf(originalParent.firstChild) != -14499 && followsLineBreak(originalParent);4500 // "If the last child of original parent is in node list, and original4501 // parent precedes a line break, set precedes line break to true.4502 // Otherwise, set precedes line break to false."4503 var precedesLineBreak_ = $_(nodeList).indexOf(originalParent.lastChild) != -14504 && precedesLineBreak(originalParent);4505 // "If the first child of original parent is not in node list, but its last4506 // child is:"4507 if ($_(nodeList).indexOf(originalParent.firstChild) == -14508 && $_(nodeList).indexOf(originalParent.lastChild) != -1) {4509 // "For each node in node list, in reverse order, insert node into the4510 // parent of original parent immediately after original parent,4511 // preserving ranges."4512 for (var i = nodeList.length - 1; i >= 0; i--) {4513 movePreservingRanges(nodeList[i], originalParent.parentNode, 1 + getNodeIndex(originalParent), range);4514 }4515 // "If precedes line break is true, and the last member of node list4516 // does not precede a line break, call createElement("br") on the4517 // context object and insert the result immediately after the last4518 // member of node list."4519 if (precedesLineBreak_4520 && !precedesLineBreak(nodeList[nodeList.length - 1])) {4521 nodeList[nodeList.length - 1].parentNode.insertBefore(document.createElement("br"), nodeList[nodeList.length - 1].nextSibling);4522 }4523 // "Remove extraneous line breaks at the end of original parent."4524 removeExtraneousLineBreaksAtTheEndOf(originalParent);4525 // "Abort these steps."4526 return;4527 }4528 // "If the first child of original parent is not in node list:"4529 if ($_(nodeList).indexOf(originalParent.firstChild) == -1) {4530 // "Let cloned parent be the result of calling cloneNode(false) on4531 // original parent."4532 var clonedParent = originalParent.cloneNode(false);4533 // "If original parent has an id attribute, unset it."4534 originalParent.removeAttribute("id");4535 // "Insert cloned parent into the parent of original parent immediately4536 // before original parent."4537 originalParent.parentNode.insertBefore(clonedParent, originalParent);4538 // "While the previousSibling of the first member of node list is not4539 // null, append the first child of original parent as the last child of4540 // cloned parent, preserving ranges."4541 while (nodeList[0].previousSibling) {4542 movePreservingRanges(originalParent.firstChild, clonedParent, clonedParent.childNodes.length, range);4543 }4544 }4545 // "For each node in node list, insert node into the parent of original4546 // parent immediately before original parent, preserving ranges."4547 for (var i = 0; i < nodeList.length; i++) {4548 movePreservingRanges(nodeList[i], originalParent.parentNode, getNodeIndex(originalParent), range);4549 }4550 // "If follows line break is true, and the first member of node list does4551 // not follow a line break, call createElement("br") on the context object4552 // and insert the result immediately before the first member of node list."4553 if (followsLineBreak_4554 && !followsLineBreak(nodeList[0])) {4555 nodeList[0].parentNode.insertBefore(document.createElement("br"), nodeList[0]);4556 }4557 // "If the last member of node list is an inline node other than a br, and4558 // the first child of original parent is a br, and original parent is not4559 // an inline node, remove the first child of original parent from original4560 // parent."4561 if (isInlineNode(nodeList[nodeList.length - 1])4562 && !isHtmlElement(nodeList[nodeList.length - 1], "br")4563 && isHtmlElement(originalParent.firstChild, "br")4564 && !isInlineNode(originalParent)) {4565 originalParent.removeChild(originalParent.firstChild);4566 }4567 // "If original parent has no children:"4568 if (!originalParent.hasChildNodes()) {4569 // if the current range is collapsed and at the end of the originalParent.parentNode4570 // the offset will not be available anymore after the next step (remove child)4571 // that's why we need to fix the range to prevent a bogus offset4572 if (originalParent.parentNode === range.startContainer4573 && originalParent.parentNode === range.endContainer4574 && range.startContainer === range.endContainer4575 && range.startOffset === range.endOffset4576 && originalParent.parentNode.childNodes.length === range.startOffset) {4577 range.startOffset = originalParent.parentNode.childNodes.length - 1;4578 range.endOffset = range.startOffset;4579 }4580 // "Remove original parent from its parent."4581 originalParent.parentNode.removeChild(originalParent);4582 // "If precedes line break is true, and the last member of node list4583 // does not precede a line break, call createElement("br") on the4584 // context object and insert the result immediately after the last4585 // member of node list."4586 if (precedesLineBreak_4587 && !precedesLineBreak(nodeList[nodeList.length - 1])) {4588 nodeList[nodeList.length - 1].parentNode.insertBefore(document.createElement("br"), nodeList[nodeList.length - 1].nextSibling);4589 }4590 // "Otherwise, remove extraneous line breaks before original parent."4591 } else {4592 removeExtraneousLineBreaksBefore(originalParent);4593 }4594 // "If node list's last member's nextSibling is null, but its parent is not4595 // null, remove extraneous line breaks at the end of node list's last4596 // member's parent."4597 if (!nodeList[nodeList.length - 1].nextSibling4598 && nodeList[nodeList.length - 1].parentNode) {4599 removeExtraneousLineBreaksAtTheEndOf(nodeList[nodeList.length - 1].parentNode);4600 }4601}4602// "To remove a node node while preserving its descendants, split the parent of4603// node's children if it has any. If it has no children, instead remove it from4604// its parent."4605function removePreservingDescendants(node, range) {4606 if (node.hasChildNodes()) {4607 splitParent([].slice.call(toArray(node.childNodes)), range);4608 } else {4609 node.parentNode.removeChild(node);4610 }4611}4612//@}4613///// Canonical space sequences /////4614//@{4615function canonicalSpaceSequence(n, nonBreakingStart, nonBreakingEnd) {4616 // "If n is zero, return the empty string."4617 if (n == 0) {4618 return "";4619 }4620 // "If n is one and both non-breaking start and non-breaking end are false,4621 // return a single space (U+0020)."4622 if (n == 1 && !nonBreakingStart && !nonBreakingEnd) {4623 return " ";4624 }4625 // "If n is one, return a single non-breaking space (U+00A0)."4626 if (n == 1) {4627 return "\xa0";4628 }4629 // "Let buffer be the empty string."4630 var buffer = "";4631 // "If non-breaking start is true, let repeated pair be U+00A0 U+0020.4632 // Otherwise, let it be U+0020 U+00A0."4633 var repeatedPair;4634 if (nonBreakingStart) {4635 repeatedPair = "\xa0 ";4636 } else {4637 repeatedPair = " \xa0";4638 }4639 // "While n is greater than three, append repeated pair to buffer and4640 // subtract two from n."4641 while (n > 3) {4642 buffer += repeatedPair;4643 n -= 2;4644 }4645 // "If n is three, append a three-element string to buffer depending on4646 // non-breaking start and non-breaking end:"4647 if (n == 3) {4648 buffer +=4649 !nonBreakingStart && !nonBreakingEnd ? " \xa0 "4650 : nonBreakingStart && !nonBreakingEnd ? "\xa0\xa0 "4651 : !nonBreakingStart && nonBreakingEnd ? " \xa0\xa0"4652 : nonBreakingStart && nonBreakingEnd ? "\xa0 \xa0"4653 : "impossible";4654 // "Otherwise, append a two-element string to buffer depending on4655 // non-breaking start and non-breaking end:"4656 } else {4657 buffer +=4658 !nonBreakingStart && !nonBreakingEnd ? "\xa0 "4659 : nonBreakingStart && !nonBreakingEnd ? "\xa0 "4660 : !nonBreakingStart && nonBreakingEnd ? " \xa0"4661 : nonBreakingStart && nonBreakingEnd ? "\xa0\xa0"4662 : "impossible";4663 }4664 // "Return buffer."4665 return buffer;4666}4667function canonicalizeWhitespace(node, offset) {4668 // "If node is neither editable nor an editing host, abort these steps."4669 if (!isEditable(node) && !isEditingHost(node)) {4670 return;4671 }4672 // "Let start node equal node and let start offset equal offset."4673 var startNode = node;4674 var startOffset = offset;4675 // "Repeat the following steps:"4676 while (true) {4677 // "If start node has a child in the same editing host with index start4678 // offset minus one, set start node to that child, then set start4679 // offset to start node's length."4680 if (0 <= startOffset - 14681 && inSameEditingHost(startNode, startNode.childNodes[startOffset - 1])) {4682 startNode = startNode.childNodes[startOffset - 1];4683 startOffset = getNodeLength(startNode);4684 // "Otherwise, if start offset is zero and start node does not follow a4685 // line break and start node's parent is in the same editing host, set4686 // start offset to start node's index, then set start node to its4687 // parent."4688 } else if (startOffset == 04689 && !followsLineBreak(startNode)4690 && inSameEditingHost(startNode, startNode.parentNode)) {4691 startOffset = getNodeIndex(startNode);4692 startNode = startNode.parentNode;4693 // "Otherwise, if start node is a Text node and its parent's resolved4694 // value for "white-space" is neither "pre" nor "pre-wrap" and start4695 // offset is not zero and the (start offset − 1)st element of start4696 // node's data is a space (0x0020) or non-breaking space (0x00A0),4697 // subtract one from start offset."4698 } else if (startNode.nodeType == $_.Node.TEXT_NODE4699 && $_(["pre", "pre-wrap"]).indexOf($_.getComputedStyle(startNode.parentNode).whiteSpace) == -14700 && startOffset != 04701 && /[ \xa0]/.test(startNode.data[startOffset - 1])) {4702 startOffset--;4703 // "Otherwise, break from this loop."4704 } else {4705 break;4706 }4707 }4708 // "Let end node equal start node and end offset equal start offset."4709 var endNode = startNode;4710 var endOffset = startOffset;4711 // "Let length equal zero."4712 var length = 0;4713 // "Let follows space be false."4714 var followsSpace = false;4715 // "Repeat the following steps:"4716 while (true) {4717 // "If end node has a child in the same editing host with index end4718 // offset, set end node to that child, then set end offset to zero."4719 if (endOffset < endNode.childNodes.length4720 && inSameEditingHost(endNode, endNode.childNodes[endOffset])) {4721 endNode = endNode.childNodes[endOffset];4722 endOffset = 0;4723 // "Otherwise, if end offset is end node's length and end node does not4724 // precede a line break and end node's parent is in the same editing4725 // host, set end offset to one plus end node's index, then set end node4726 // to its parent."4727 } else if (endOffset == getNodeLength(endNode)4728 && !precedesLineBreak(endNode)4729 && inSameEditingHost(endNode, endNode.parentNode)) {4730 endOffset = 1 + getNodeIndex(endNode);4731 endNode = endNode.parentNode;4732 // "Otherwise, if end node is a Text node and its parent's resolved4733 // value for "white-space" is neither "pre" nor "pre-wrap" and end4734 // offset is not end node's length and the end offsetth element of4735 // end node's data is a space (0x0020) or non-breaking space (0x00A0):"4736 } else if (endNode.nodeType == $_.Node.TEXT_NODE4737 && $_(["pre", "pre-wrap"]).indexOf($_.getComputedStyle(endNode.parentNode).whiteSpace) == -14738 && endOffset != getNodeLength(endNode)4739 && /[ \xa0]/.test(endNode.data[endOffset])) {4740 // "If follows space is true and the end offsetth element of end4741 // node's data is a space (0x0020), call deleteData(end offset, 1)4742 // on end node, then continue this loop from the beginning."4743 if (followsSpace4744 && " " == endNode.data[endOffset]) {4745 endNode.deleteData(endOffset, 1);4746 continue;4747 }4748 // "Set follows space to true if the end offsetth element of end4749 // node's data is a space (0x0020), false otherwise."4750 followsSpace = " " == endNode.data[endOffset];4751 // "Add one to end offset."4752 endOffset++;4753 // "Add one to length."4754 length++;4755 // "Otherwise, break from this loop."4756 } else {4757 break;4758 }4759 }4760 // "Let replacement whitespace be the canonical space sequence of length4761 // length. non-breaking start is true if start offset is zero and start4762 // node follows a line break, and false otherwise. non-breaking end is true4763 // if end offset is end node's length and end node precedes a line break,4764 // and false otherwise."4765 var replacementWhitespace = canonicalSpaceSequence(length,4766 startOffset == 0 && followsLineBreak(startNode),4767 endOffset == getNodeLength(endNode) && precedesLineBreak(endNode));4768 // "While (start node, start offset) is before (end node, end offset):"4769 while (getPosition(startNode, startOffset, endNode, endOffset) == "before") {4770 // "If start node has a child with index start offset, set start node4771 // to that child, then set start offset to zero."4772 if (startOffset < startNode.childNodes.length) {4773 startNode = startNode.childNodes[startOffset];4774 startOffset = 0;4775 // "Otherwise, if start node is not a Text node or if start offset is4776 // start node's length, set start offset to one plus start node's4777 // index, then set start node to its parent."4778 } else if (startNode.nodeType != $_.Node.TEXT_NODE4779 || startOffset == getNodeLength(startNode)) {4780 startOffset = 1 + getNodeIndex(startNode);4781 startNode = startNode.parentNode;4782 // "Otherwise:"4783 } else {4784 // "Remove the first element from replacement whitespace, and let4785 // element be that element."4786 var element = replacementWhitespace[0];4787 replacementWhitespace = replacementWhitespace.slice(1);4788 // "If element is not the same as the start offsetth element of4789 // start node's data:"4790 if (element != startNode.data[startOffset]) {4791 // "Call insertData(start offset, element) on start node."4792 startNode.insertData(startOffset, element);4793 // "Call deleteData(start offset + 1, 1) on start node."4794 startNode.deleteData(startOffset + 1, 1);4795 }4796 // "Add one to start offset."4797 startOffset++;4798 }4799 }4800}4801//@}4802///// Indenting and outdenting /////4803//@{4804function indentNodes(nodeList, range) {4805 // "If node list is empty, do nothing and abort these steps."4806 if (!nodeList.length) {4807 return;4808 }4809 // "Let first node be the first member of node list."4810 var firstNode = nodeList[0];4811 // "If first node's parent is an ol or ul:"4812 if (isHtmlElement(firstNode.parentNode, ["OL", "UL"])) {4813 // "Let tag be the local name of the parent of first node."4814 var tag = firstNode.parentNode.tagName;4815 // "Wrap node list, with sibling criteria returning true for an HTML4816 // element with local name tag and false otherwise, and new parent4817 // instructions returning the result of calling createElement(tag) on4818 // the ownerDocument of first node."4819 wrap(nodeList,4820 function(node) { return isHtmlElement(node, tag) },4821 function() { return firstNode.ownerDocument.createElement(tag) },4822 range4823 );4824 // "Abort these steps."4825 return;4826 }4827 // "Wrap node list, with sibling criteria returning true for a simple4828 // indentation element and false otherwise, and new parent instructions4829 // returning the result of calling createElement("blockquote") on the4830 // ownerDocument of first node. Let new parent be the result."4831 var newParent = wrap(nodeList,4832 function(node) { return isSimpleIndentationElement(node) },4833 function() { return firstNode.ownerDocument.createElement("blockquote") },4834 range4835 );4836 // "Fix disallowed ancestors of new parent."4837 fixDisallowedAncestors(newParent, range);4838}4839function outdentNode(node, range) {4840 // "If node is not editable, abort these steps."4841 if (!isEditable(node)) {4842 return;4843 }4844 // "If node is a simple indentation element, remove node, preserving its4845 // descendants. Then abort these steps."4846 if (isSimpleIndentationElement(node)) {4847 removePreservingDescendants(node, range);4848 return;4849 }4850 // "If node is an indentation element:"4851 if (isIndentationElement(node)) {4852 // "Unset the class and dir attributes of node, if any."4853 node.removeAttribute("class");4854 node.removeAttribute("dir");4855 // "Unset the margin, padding, and border CSS properties of node."4856 node.style.margin = "";4857 node.style.padding = "";4858 node.style.border = "";4859 if (node.getAttribute("style") == "") {4860 node.removeAttribute("style");4861 }4862 // "Set the tag name of node to "div"."4863 setTagName(node, "div", range);4864 // "Abort these steps."4865 return;4866 }4867 // "Let current ancestor be node's parent."4868 var currentAncestor = node.parentNode;4869 // "Let ancestor list be a list of nodes, initially empty."4870 var ancestorList = [];4871 // "While current ancestor is an editable Element that is neither a simple4872 // indentation element nor an ol nor a ul, append current ancestor to4873 // ancestor list and then set current ancestor to its parent."4874 while (isEditable(currentAncestor)4875 && currentAncestor.nodeType == $_.Node.ELEMENT_NODE4876 && !isSimpleIndentationElement(currentAncestor)4877 && !isHtmlElement(currentAncestor, ["ol", "ul"])) {4878 ancestorList.push(currentAncestor);4879 currentAncestor = currentAncestor.parentNode;4880 }4881 // "If current ancestor is not an editable simple indentation element:"4882 if (!isEditable(currentAncestor)4883 || !isSimpleIndentationElement(currentAncestor)) {4884 // "Let current ancestor be node's parent."4885 currentAncestor = node.parentNode;4886 // "Let ancestor list be the empty list."4887 ancestorList = [];4888 // "While current ancestor is an editable Element that is neither an4889 // indentation element nor an ol nor a ul, append current ancestor to4890 // ancestor list and then set current ancestor to its parent."4891 while (isEditable(currentAncestor)4892 && currentAncestor.nodeType == $_.Node.ELEMENT_NODE4893 && !isIndentationElement(currentAncestor)4894 && !isHtmlElement(currentAncestor, ["ol", "ul"])) {4895 ancestorList.push(currentAncestor);4896 currentAncestor = currentAncestor.parentNode;4897 }4898 }4899 // "If node is an ol or ul and current ancestor is not an editable4900 // indentation element:"4901 if (isHtmlElement(node, ["OL", "UL"])4902 && (!isEditable(currentAncestor)4903 || !isIndentationElement(currentAncestor))) {4904 // "Unset the reversed, start, and type attributes of node, if any are4905 // set."4906 node.removeAttribute("reversed");4907 node.removeAttribute("start");4908 node.removeAttribute("type");4909 // "Let children be the children of node."4910 var children = [].slice.call(toArray(node.childNodes));4911 // "If node has attributes, and its parent is not an ol or ul, set the4912 // tag name of node to "div"."4913 if (node.attributes.length4914 && !isHtmlElement(node.parentNode, ["OL", "UL"])) {4915 setTagName(node, "div", range);4916 // "Otherwise:"4917 } else {4918 // "Record the values of node's children, and let values be the4919 // result."4920 var values = recordValues([].slice.call(toArray(node.childNodes)));4921 // "Remove node, preserving its descendants."4922 removePreservingDescendants(node, range);4923 // "Restore the values from values."4924 restoreValues(values, range);4925 }4926 // "Fix disallowed ancestors of each member of children."4927 for (var i = 0; i < children.length; i++) {4928 fixDisallowedAncestors(children[i], range);4929 }4930 // "Abort these steps."4931 return;4932 }4933 // "If current ancestor is not an editable indentation element, abort these4934 // steps."4935 if (!isEditable(currentAncestor)4936 || !isIndentationElement(currentAncestor)) {4937 return;4938 }4939 // "Append current ancestor to ancestor list."4940 ancestorList.push(currentAncestor);4941 // "Let original ancestor be current ancestor."4942 var originalAncestor = currentAncestor;4943 // "While ancestor list is not empty:"4944 while (ancestorList.length) {4945 // "Let current ancestor be the last member of ancestor list."4946 //4947 // "Remove the last member of ancestor list."4948 currentAncestor = ancestorList.pop();4949 // "Let target be the child of current ancestor that is equal to either4950 // node or the last member of ancestor list."4951 var target = node.parentNode == currentAncestor4952 ? node4953 : ancestorList[ancestorList.length - 1];4954 // "If target is an inline node that is not a br, and its nextSibling4955 // is a br, remove target's nextSibling from its parent."4956 if (isInlineNode(target)4957 && !isHtmlElement(target, "BR")4958 && isHtmlElement(target.nextSibling, "BR")) {4959 target.parentNode.removeChild(target.nextSibling);4960 }4961 // "Let preceding siblings be the preceding siblings of target, and let4962 // following siblings be the following siblings of target."4963 var precedingSiblings = [].slice.call(toArray(currentAncestor.childNodes), 0, getNodeIndex(target));4964 var followingSiblings = [].slice.call(toArray(currentAncestor.childNodes), 1 + getNodeIndex(target));4965 // "Indent preceding siblings."4966 indentNodes(precedingSiblings, range);4967 // "Indent following siblings."4968 indentNodes(followingSiblings, range);4969 }4970 // "Outdent original ancestor."4971 outdentNode(originalAncestor, range);4972}4973//@}4974///// Toggling lists /////4975//@{4976function toggleLists(tagName, range) {4977 // "Let mode be "disable" if the selection's list state is tag name, and4978 // "enable" otherwise."4979 var mode = getSelectionListState() == tagName ? "disable" : "enable";4980 tagName = tagName.toUpperCase();4981 // "Let other tag name be "ol" if tag name is "ul", and "ul" if tag name is4982 // "ol"."4983 var otherTagName = tagName == "OL" ? "UL" : "OL";4984 // "Let items be a list of all lis that are ancestor containers of the4985 // range's start and/or end node."4986 //4987 // It's annoying to get this in tree order using functional stuff without4988 // doing getDescendants(document), which is slow, so I do it imperatively.4989 var items = [];4990 (function(){4991 for (4992 var ancestorContainer = range.endContainer;4993 ancestorContainer != range.commonAncestorContainer;4994 ancestorContainer = ancestorContainer.parentNode4995 ) {4996 if (isHtmlElement(ancestorContainer, "li")) {4997 items.unshift(ancestorContainer);4998 }4999 }5000 for (5001 var ancestorContainer = range.startContainer;5002 ancestorContainer;5003 ancestorContainer = ancestorContainer.parentNode5004 ) {5005 if (isHtmlElement(ancestorContainer, "li")) {5006 items.unshift(ancestorContainer);5007 }5008 }5009 })();5010 // "For each item in items, normalize sublists of item."5011 $_( items ).forEach( function( thisArg ) {5012 normalizeSublists( thisArg, range);5013 });5014 // "Block-extend the range, and let new range be the result."5015 var newRange = blockExtend(range);5016 // "If mode is "enable", then let lists to convert consist of every5017 // editable HTML element with local name other tag name that is contained5018 // in new range, and for every list in lists to convert:"5019 if (mode == "enable") {5020 $_( getAllContainedNodes(newRange, function(node) {5021 return isEditable(node)5022 && isHtmlElement(node, otherTagName);5023 }) ).forEach(function(list) {5024 // "If list's previousSibling or nextSibling is an editable HTML5025 // element with local name tag name:"5026 if ((isEditable(list.previousSibling) && isHtmlElement(list.previousSibling, tagName))5027 || (isEditable(list.nextSibling) && isHtmlElement(list.nextSibling, tagName))) {5028 // "Let children be list's children."5029 var children = [].slice.call(toArray(list.childNodes));5030 // "Record the values of children, and let values be the5031 // result."5032 var values = recordValues(children);5033 // "Split the parent of children."5034 splitParent(children, range);5035 // "Wrap children, with sibling criteria returning true for an5036 // HTML element with local name tag name and false otherwise."5037 wrap(children, 5038 function(node) { return isHtmlElement(node, tagName) },5039 function() {return null },5040 range5041 );5042 // "Restore the values from values."5043 restoreValues(values, range);5044 // "Otherwise, set the tag name of list to tag name."5045 } else {5046 setTagName(list, tagName, range);5047 }5048 });5049 }5050 // "Let node list be a list of nodes, initially empty."5051 //5052 // "For each node node contained in new range, if node is editable; the5053 // last member of node list (if any) is not an ancestor of node; node5054 // is not an indentation element; and either node is an ol or ul, or its5055 // parent is an ol or ul, or it is an allowed child of "li"; then append5056 // node to node list."5057 var nodeList = getContainedNodes(newRange, function(node) {5058 return isEditable(node)5059 && !isIndentationElement(node)5060 && (isHtmlElement(node, ["OL", "UL"])5061 || isHtmlElement(node.parentNode, ["OL", "UL"])5062 || isAllowedChild(node, "li"));5063 });5064 // "If mode is "enable", remove from node list any ol or ul whose parent is5065 // not also an ol or ul."5066 if (mode == "enable") {5067 nodeList = $_( nodeList ).filter(function(node) {5068 return !isHtmlElement(node, ["ol", "ul"])5069 || isHtmlElement(node.parentNode, ["ol", "ul"]);5070 });5071 }5072 // "If mode is "disable", then while node list is not empty:"5073 if (mode == "disable") {5074 while (nodeList.length) {5075 // "Let sublist be an empty list of nodes."5076 var sublist = [];5077 // "Remove the first member from node list and append it to5078 // sublist."5079 sublist.push(nodeList.shift());5080 // "If the first member of sublist is an HTML element with local5081 // name tag name, outdent it and continue this loop from the5082 // beginning."5083 if (isHtmlElement(sublist[0], tagName)) {5084 outdentNode(sublist[0], range);5085 continue;5086 }5087 // "While node list is not empty, and the first member of node list5088 // is the nextSibling of the last member of sublist and is not an5089 // HTML element with local name tag name, remove the first member5090 // from node list and append it to sublist."5091 while (nodeList.length5092 && nodeList[0] == sublist[sublist.length - 1].nextSibling5093 && !isHtmlElement(nodeList[0], tagName)) {5094 sublist.push(nodeList.shift());5095 }5096 // "Record the values of sublist, and let values be the result."5097 var values = recordValues(sublist);5098 // "Split the parent of sublist."5099 splitParent(sublist, range);5100 // "Fix disallowed ancestors of each member of sublist."5101 for (var i = 0; i < sublist.length; i++) {5102 fixDisallowedAncestors(sublist[i], range);5103 }5104 // "Restore the values from values."5105 restoreValues(values, range);5106 }5107 // "Otherwise, while node list is not empty:"5108 } else {5109 while (nodeList.length) {5110 // "Let sublist be an empty list of nodes."5111 var sublist = [];5112 // "While either sublist is empty, or node list is not empty and5113 // its first member is the nextSibling of sublist's last member:"5114 while (!sublist.length5115 || (nodeList.length5116 && nodeList[0] == sublist[sublist.length - 1].nextSibling)) {5117 // "If node list's first member is a p or div, set the tag name5118 // of node list's first member to "li", and append the result5119 // to sublist. Remove the first member from node list."5120 if (isHtmlElement(nodeList[0], ["p", "div"])) {5121 sublist.push(setTagName(nodeList[0], "li", range));5122 nodeList.shift();5123 // "Otherwise, if the first member of node list is an li or ol5124 // or ul, remove it from node list and append it to sublist."5125 } else if (isHtmlElement(nodeList[0], ["li", "ol", "ul"])) {5126 sublist.push(nodeList.shift());5127 // "Otherwise:"5128 } else {5129 // "Let nodes to wrap be a list of nodes, initially empty."5130 var nodesToWrap = [];5131 // "While nodes to wrap is empty, or node list is not empty5132 // and its first member is the nextSibling of nodes to5133 // wrap's last member and the first member of node list is5134 // an inline node and the last member of nodes to wrap is5135 // an inline node other than a br, remove the first member5136 // from node list and append it to nodes to wrap."5137 while (!nodesToWrap.length5138 || (nodeList.length5139 && nodeList[0] == nodesToWrap[nodesToWrap.length - 1].nextSibling5140 && isInlineNode(nodeList[0])5141 && isInlineNode(nodesToWrap[nodesToWrap.length - 1])5142 && !isHtmlElement(nodesToWrap[nodesToWrap.length - 1], "br"))) {5143 nodesToWrap.push(nodeList.shift());5144 }5145 // "Wrap nodes to wrap, with new parent instructions5146 // returning the result of calling createElement("li") on5147 // the context object. Append the result to sublist."5148 sublist.push(5149 wrap(nodesToWrap,5150 undefined,5151 function() { return document.createElement("li") },5152 range5153 )5154 );5155 }5156 }5157 // "If sublist's first member's parent is an HTML element with5158 // local name tag name, or if every member of sublist is an ol or5159 // ul, continue this loop from the beginning."5160 if (isHtmlElement(sublist[0].parentNode, tagName)5161 || $_(sublist).every(function(node) { return isHtmlElement(node, ["ol", "ul"]) })) {5162 continue;5163 }5164 // "If sublist's first member's parent is an HTML element with5165 // local name other tag name:"5166 if (isHtmlElement(sublist[0].parentNode, otherTagName)) {5167 // "Record the values of sublist, and let values be the5168 // result."5169 var values = recordValues(sublist);5170 // "Split the parent of sublist."5171 splitParent(sublist, range);5172 // "Wrap sublist, with sibling criteria returning true for an5173 // HTML element with local name tag name and false otherwise,5174 // and new parent instructions returning the result of calling5175 // createElement(tag name) on the context object."5176 wrap(sublist,5177 function(node) { return isHtmlElement(node, tagName) },5178 function() { return document.createElement(tagName) },5179 range5180 );5181 // "Restore the values from values."5182 restoreValues(values, range);5183 // "Continue this loop from the beginning."5184 continue;5185 }5186 // "Wrap sublist, with sibling criteria returning true for an HTML5187 // element with local name tag name and false otherwise, and new5188 // parent instructions being the following:"5189 // . . .5190 // "Fix disallowed ancestors of the previous step's result."5191 fixDisallowedAncestors(5192 wrap(sublist,5193 function(node) { return isHtmlElement(node, tagName) },5194 function() {5195 // "If sublist's first member's parent is not an editable5196 // simple indentation element, or sublist's first member's5197 // parent's previousSibling is not an editable HTML element5198 // with local name tag name, call createElement(tag name)5199 // on the context object and return the result."5200 if (!isEditable(sublist[0].parentNode)5201 || !isSimpleIndentationElement(sublist[0].parentNode)5202 || !isEditable(sublist[0].parentNode.previousSibling)5203 || !isHtmlElement(sublist[0].parentNode.previousSibling, tagName)) {5204 return document.createElement(tagName);5205 }5206 5207 // "Let list be sublist's first member's parent's5208 // previousSibling."5209 var list = sublist[0].parentNode.previousSibling;5210 5211 // "Normalize sublists of list's lastChild."5212 normalizeSublists(list.lastChild, range);5213 5214 // "If list's lastChild is not an editable HTML element5215 // with local name tag name, call createElement(tag name)5216 // on the context object, and append the result as the last5217 // child of list."5218 if (!isEditable(list.lastChild)5219 || !isHtmlElement(list.lastChild, tagName)) {5220 list.appendChild(document.createElement(tagName));5221 }5222 5223 // "Return the last child of list."5224 return list.lastChild;5225 },5226 range5227 )5228 , range5229 );5230 }5231 }5232}5233//@}5234///// Justifying the selection /////5235//@{5236function justifySelection(alignment, range) {5237 5238 // "Block-extend the active range, and let new range be the result."5239 var newRange = blockExtend(range);5240 // "Let element list be a list of all editable Elements contained in new5241 // range that either has an attribute in the HTML namespace whose local5242 // name is "align", or has a style attribute that sets "text-align", or is5243 // a center."5244 var elementList = getAllContainedNodes(newRange, function(node) {5245 return node.nodeType == $_.Node.ELEMENT_NODE5246 && isEditable(node)5247 // Ignoring namespaces here5248 && (5249 $_( node ).hasAttribute("align")5250 || node.style.textAlign != ""5251 || isHtmlElement(node, "center")5252 );5253 });5254 // "For each element in element list:"5255 for (var i = 0; i < elementList.length; i++) {5256 var element = elementList[i];5257 // "If element has an attribute in the HTML namespace whose local name5258 // is "align", remove that attribute."5259 element.removeAttribute("align");5260 // "Unset the CSS property "text-align" on element, if it's set by a5261 // style attribute."5262 element.style.textAlign = "";5263 if (element.getAttribute("style") == "") {5264 element.removeAttribute("style");5265 }5266 // "If element is a div or span or center with no attributes, remove5267 // it, preserving its descendants."5268 if (isHtmlElement(element, ["div", "span", "center"])5269 && !element.attributes.length) {5270 removePreservingDescendants(element, range);5271 }5272 // "If element is a center with one or more attributes, set the tag5273 // name of element to "div"."5274 if (isHtmlElement(element, "center")5275 && element.attributes.length) {5276 setTagName(element, "div", range);5277 }5278 }5279 // "Block-extend the active range, and let new range be the result."5280 newRange = blockExtend(globalRange);5281 // "Let node list be a list of nodes, initially empty."5282 var nodeList = [];5283 // "For each node node contained in new range, append node to node list if5284 // the last member of node list (if any) is not an ancestor of node; node5285 // is editable; node is an allowed child of "div"; and node's alignment5286 // value is not alignment."5287 nodeList = getContainedNodes(newRange, function(node) {5288 return isEditable(node)5289 && isAllowedChild(node, "div")5290 && getAlignmentValue(node) != alignment;5291 });5292 // "While node list is not empty:"5293 while (nodeList.length) {5294 // "Let sublist be a list of nodes, initially empty."5295 var sublist = [];5296 // "Remove the first member of node list and append it to sublist."5297 sublist.push(nodeList.shift());5298 // "While node list is not empty, and the first member of node list is5299 // the nextSibling of the last member of sublist, remove the first5300 // member of node list and append it to sublist."5301 while (nodeList.length5302 && nodeList[0] == sublist[sublist.length - 1].nextSibling) {5303 sublist.push(nodeList.shift());5304 }5305 // "Wrap sublist. Sibling criteria returns true for any div that has5306 // one or both of the following two attributes and no other attributes,5307 // and false otherwise:"5308 //5309 // * "An align attribute whose value is an ASCII case-insensitive5310 // match for alignment.5311 // * "A style attribute which sets exactly one CSS property5312 // (including unrecognized or invalid attributes), which is5313 // "text-align", which is set to alignment.5314 //5315 // "New parent instructions are to call createElement("div") on the5316 // context object, then set its CSS property "text-align" to alignment5317 // and return the result."5318 wrap(sublist,5319 function(node) {5320 return isHtmlElement(node, "div")5321 && $_(node.attributes).every(function(attr) {5322 return (attr.name == "align" && attr.value.toLowerCase() == alignment)5323 || (attr.name == "style" && getStyleLength(node) == 1 && node.style.textAlign == alignment);5324 });5325 },5326 function() {5327 var newParent = document.createElement("div");5328 newParent.setAttribute("style", "text-align: " + alignment);5329 return newParent;5330 },5331 range5332 );5333 }5334}5335//@}5336///// Create an end break /////5337//@{5338function createEndBreak() {5339 var endBr = document.createElement("br");5340 endBr.setAttribute("class", "aloha-end-br");5341 return endBr;5342}5343//@}5344///// The delete command /////5345//@{5346commands["delete"] = {5347 action: function(value, range) {5348 // "If the active range is not collapsed, delete the contents of the5349 // active range and abort these steps."5350 if (!range.collapsed) {5351 deleteContents(range);5352 return;5353 }5354 // "Canonicalize whitespace at (active range's start node, active5355 // range's start offset)."5356 canonicalizeWhitespace(range.startContainer, range.startOffset);5357 // "Let node and offset be the active range's start node and offset."5358 var node = range.startContainer;5359 var offset = range.startOffset;5360 var isBr = false;5361 var isHr = false;5362 // "Repeat the following steps:"5363 while ( true ) {5364 // we need to reset isBr and isHr on every interation of the loop5365 if ( offset > 0 ) {5366 isBr = isHtmlElement(node.childNodes[offset - 1], "br") || false;5367 isHr = isHtmlElement(node.childNodes[offset - 1], "hr") || false;5368 }5369 // "If offset is zero and node's previousSibling is an editable5370 // invisible node, remove node's previousSibling from its parent."5371 if (offset == 05372 && isEditable(node.previousSibling)5373 && isInvisible(node.previousSibling)) {5374 node.parentNode.removeChild(node.previousSibling);5375 // "Otherwise, if node has a child with index offset − 1 and that5376 // child is an editable invisible node, remove that child from5377 // node, then subtract one from offset."5378 } else if (0 <= offset - 15379 && offset - 1 < node.childNodes.length5380 && isEditable(node.childNodes[offset - 1])5381 && (isInvisible(node.childNodes[offset - 1]) || isBr || isHr )) {5382 node.removeChild(node.childNodes[offset - 1]);5383 offset--;5384 if (isBr || isHr) {5385 range.setStart(node, offset);5386 range.setEnd(node, offset);5387 return;5388 }5389 // "Otherwise, if offset is zero and node is an inline node, or if5390 // node is an invisible node, set offset to the index of node, then5391 // set node to its parent."5392 } else if ((offset == 05393 && isInlineNode(node))5394 || isInvisible(node)) {5395 offset = getNodeIndex(node);5396 node = node.parentNode;5397 // "Otherwise, if node has a child with index offset − 1 and that5398 // child is an editable a, remove that child from node, preserving5399 // its descendants. Then abort these steps."5400 } else if (0 <= offset - 15401 && offset - 1 < node.childNodes.length5402 && isEditable(node.childNodes[offset - 1])5403 && isHtmlElement(node.childNodes[offset - 1], "a")) {5404 removePreservingDescendants(node.childNodes[offset - 1], range);5405 return;5406 // "Otherwise, if node has a child with index offset − 1 and that5407 // child is not a block node or a br or an img, set node to that5408 // child, then set offset to the length of node."5409 } else if (0 <= offset - 15410 && offset - 1 < node.childNodes.length5411 && !isBlockNode(node.childNodes[offset - 1])5412 && !isHtmlElement(node.childNodes[offset - 1], ["br", "img"])) {5413 node = node.childNodes[offset - 1];5414 offset = getNodeLength(node);5415 // "Otherwise, break from this loop."5416 } else {5417 break;5418 }5419 }5420 // collapse whitespace sequences5421 collapseWhitespace(node, range);5422 offset = range.startOffset;5423 // "If node is a Text node and offset is not zero, call collapse(node,5424 // offset) on the Selection. Then delete the contents of the range with5425 // start (node, offset − 1) and end (node, offset) and abort these5426 // steps."5427 if (node.nodeType == $_.Node.TEXT_NODE5428 && offset != 0) {5429 range.setStart(node, offset);5430 range.setEnd(node, offset);5431 // fix range start container offset according to old code5432 // so we can still pass our range and have it modified, but5433 // also conform with the previous implementation5434 range.startOffset -= 1;5435 deleteContents(range);5436 return;5437 }5438 // "If node is an inline node, abort these steps."5439 if (isInlineNode(node)) {5440 return;5441 }5442 // "If node has a child with index offset − 1 and that child is a br or5443 // hr or img, call collapse(node, offset) on the Selection. Then delete5444 // the contents of the range with start (node, offset − 1) and end5445 // (node, offset) and abort these steps."5446 if (0 <= offset - 15447 && offset - 1 < node.childNodes.length5448 && isHtmlElement(node.childNodes[offset - 1], ["br", "hr", "img"])) {5449 range.setStart(node, offset);5450 range.setEnd(node, offset);5451 deleteContents(range);5452 return;5453 }5454 // "If node is an li or dt or dd and is the first child of its parent,5455 // and offset is zero:"5456 if (isHtmlElement(node, ["li", "dt", "dd"])5457 && node == node.parentNode.firstChild5458 && offset == 0) {5459 // "Let items be a list of all lis that are ancestors of node."5460 //5461 // Remember, must be in tree order.5462 var items = [];5463 for (var ancestor = node.parentNode; ancestor; ancestor = ancestor.parentNode) {5464 if (isHtmlElement(ancestor, "li")) {5465 items.unshift(ancestor);5466 }5467 }5468 // "Normalize sublists of each item in items."5469 for (var i = 0; i < items.length; i++) {5470 normalizeSublists(items[i], range);5471 }5472 // "Record the values of the one-node list consisting of node, and5473 // let values be the result."5474 var values = recordValues([node]);5475 // "Split the parent of the one-node list consisting of node."5476 splitParent([node], range);5477 // "Restore the values from values."5478 restoreValues(values, range);5479 // "If node is a dd or dt, and it is not an allowed child of any of5480 // its ancestors in the same editing host, set the tag name of node5481 // to the default single-line container name and let node be the5482 // result."5483 if (isHtmlElement(node, ["dd", "dt"])5484 && $_(getAncestors(node)).every(function(ancestor) {5485 return !inSameEditingHost(node, ancestor)5486 || !isAllowedChild(node, ancestor)5487 })) {5488 node = setTagName(node, defaultSingleLineContainerName, range);5489 }5490 // "Fix disallowed ancestors of node."5491 fixDisallowedAncestors(node, range);5492 // fix the lists to be html5 conformant5493 for (var i = 0; i < items.length; i++) {5494 unNormalizeSublists(items[i].parentNode, range);5495 }5496 // "Abort these steps."5497 return;5498 }5499 // "Let start node equal node and let start offset equal offset."5500 var startNode = node;5501 var startOffset = offset;5502 // "Repeat the following steps:"5503 while (true) {5504 // "If start offset is zero, set start offset to the index of start5505 // node and then set start node to its parent."5506 if (startOffset == 0) {5507 startOffset = getNodeIndex(startNode);5508 startNode = startNode.parentNode;5509 // "Otherwise, if start node has an editable invisible child with5510 // index start offset minus one, remove it from start node and5511 // subtract one from start offset."5512 } else if (0 <= startOffset - 15513 && startOffset - 1 < startNode.childNodes.length5514 && isEditable(startNode.childNodes[startOffset - 1])5515 && isInvisible(startNode.childNodes[startOffset - 1])) {5516 startNode.removeChild(startNode.childNodes[startOffset - 1]);5517 startOffset--;5518 // "Otherwise, break from this loop."5519 } else {5520 break;5521 }5522 }5523 // "If offset is zero, and node has an editable ancestor container in5524 // the same editing host that's an indentation element:"5525 if (offset == 05526 && $_( getAncestors(node).concat(node) ).filter(function(ancestor) {5527 return isEditable(ancestor)5528 && inSameEditingHost(ancestor, node)5529 && isIndentationElement(ancestor);5530 }).length) {5531 // "Block-extend the range whose start and end are both (node, 0),5532 // and let new range be the result."5533 var newRange = Aloha.createRange();5534 newRange.setStart(node, 0);5535 newRange.setEnd(node, 0);5536 newRange = blockExtend(newRange);5537 // "Let node list be a list of nodes, initially empty."5538 //5539 // "For each node current node contained in new range, append5540 // current node to node list if the last member of node list (if5541 // any) is not an ancestor of current node, and current node is5542 // editable but has no editable descendants."5543 var nodeList = getContainedNodes(newRange, function(currentNode) {5544 return isEditable(currentNode)5545 && !hasEditableDescendants(currentNode);5546 });5547 // "Outdent each node in node list."5548 for (var i = 0; i < nodeList.length; i++) {5549 outdentNode(nodeList[i], range);5550 }5551 // "Abort these steps."5552 return;5553 }5554 // "If the child of start node with index start offset is a table,5555 // abort these steps."5556 if (isHtmlElement(startNode.childNodes[startOffset], "table")) {5557 return;5558 }5559 // "If start node has a child with index start offset − 1, and that5560 // child is a table:"5561 if (0 <= startOffset - 15562 && startOffset - 1 < startNode.childNodes.length5563 && isHtmlElement(startNode.childNodes[startOffset - 1], "table")) {5564 // "Call collapse(start node, start offset − 1) on the context5565 // object's Selection."5566 range.setStart(startNode, startOffset - 1);5567 // "Call extend(start node, start offset) on the context object's5568 // Selection."5569 range.setEnd(startNode, startOffset);5570 // "Abort these steps."5571 return;5572 }5573 // "If offset is zero; and either the child of start node with index5574 // start offset minus one is an hr, or the child is a br whose5575 // previousSibling is either a br or not an inline node:"5576 if (offset == 05577 && (isHtmlElement(startNode.childNodes[startOffset - 1], "hr")5578 || (5579 isHtmlElement(startNode.childNodes[startOffset - 1], "br")5580 && (5581 isHtmlElement(startNode.childNodes[startOffset - 1].previousSibling, "br")5582 || !isInlineNode(startNode.childNodes[startOffset - 1].previousSibling)5583 )5584 )5585 )) {5586 // "Call collapse(node, offset) on the Selection."5587 range.setStart(node, offset);5588 range.setEnd(node, offset);5589 // "Delete the contents of the range with start (start node, start5590 // offset − 1) and end (start node, start offset)."5591 deleteContents(startNode, startOffset - 1, startNode, startOffset);5592 // "Abort these steps."5593 return;5594 }5595 // "If the child of start node with index start offset is an li or dt5596 // or dd, and that child's firstChild is an inline node, and start5597 // offset is not zero:"5598 if (isHtmlElement(startNode.childNodes[startOffset], ["li", "dt", "dd"])5599 && isInlineNode(startNode.childNodes[startOffset].firstChild)5600 && startOffset != 0) {5601 // "Let previous item be the child of start node with index start5602 // offset minus one."5603 var previousItem = startNode.childNodes[startOffset - 1];5604 // "If previous item's lastChild is an inline node other than a br,5605 // call createElement("br") on the context object and append the5606 // result as the last child of previous item."5607 if (isInlineNode(previousItem.lastChild)5608 && !isHtmlElement(previousItem.lastChild, "br")) {5609 previousItem.appendChild(document.createElement("br"));5610 }5611 // "If previous item's lastChild is an inline node, call5612 // createElement("br") on the context object and append the result5613 // as the last child of previous item."5614 if (isInlineNode(previousItem.lastChild)) {5615 previousItem.appendChild(document.createElement("br"));5616 }5617 }5618 // "If the child of start node with index start offset is an li or dt5619 // or dd, and its previousSibling is also an li or dt or dd, set start5620 // node to its child with index start offset − 1, then set start offset5621 // to start node's length, then set node to start node's nextSibling,5622 // then set offset to 0."5623 if (isHtmlElement(startNode.childNodes[startOffset], ["li", "dt", "dd"])5624 && isHtmlElement(startNode.childNodes[startOffset - 1], ["li", "dt", "dd"])) {5625 startNode = startNode.childNodes[startOffset - 1];5626 startOffset = getNodeLength(startNode);5627 node = startNode.nextSibling;5628 offset = 0;5629 // "Otherwise, while start node has a child with index start offset5630 // minus one:"5631 } else {5632 while (0 <= startOffset - 15633 && startOffset - 1 < startNode.childNodes.length) {5634 // "If start node's child with index start offset minus one is5635 // editable and invisible, remove it from start node, then5636 // subtract one from start offset."5637 if (isEditable(startNode.childNodes[startOffset - 1])5638 && isInvisible(startNode.childNodes[startOffset - 1])) {5639 startNode.removeChild(startNode.childNodes[startOffset - 1]);5640 startOffset--;5641 // "Otherwise, set start node to its child with index start5642 // offset minus one, then set start offset to the length of5643 // start node."5644 } else {5645 startNode = startNode.childNodes[startOffset - 1];5646 startOffset = getNodeLength(startNode);5647 }5648 }5649 }5650 // "Delete the contents of the range with start (start node, start5651 // offset) and end (node, offset)."5652 var delRange = Aloha.createRange();5653 delRange.setStart(startNode, startOffset);5654 delRange.setEnd(node, offset);5655 deleteContents(delRange);5656 if (!isAncestorContainer(document.body, range.startContainer)) {5657 if (delRange.startContainer.hasChildNodes() || delRange.startContainer.nodeType == $_.Node.TEXT_NODE) {5658 range.setStart(delRange.startContainer, delRange.startOffset);5659 range.setEnd(delRange.startContainer, delRange.startOffset);5660 } else {5661 range.setStart(delRange.startContainer.parentNode, getNodeIndex(delRange.startContainer));5662 range.setEnd(delRange.startContainer.parentNode, getNodeIndex(delRange.startContainer));5663 }5664 }5665 }5666};5667//@}5668///// The formatBlock command /////5669//@{5670// "A formattable block name is "address", "dd", "div", "dt", "h1", "h2", "h3",5671// "h4", "h5", "h6", "p", or "pre"."5672var formattableBlockNames = ["address", "dd", "div", "dt", "h1", "h2", "h3",5673 "h4", "h5", "h6", "p", "pre"];5674commands.formatblock = {5675 action: function(value) {5676 // "If value begins with a "<" character and ends with a ">" character,5677 // remove the first and last characters from it."5678 if (/^<.*>$/.test(value)) {5679 value = value.slice(1, -1);5680 }5681 // "Let value be converted to ASCII lowercase."5682 value = value.toLowerCase();5683 // "If value is not a formattable block name, abort these steps and do5684 // nothing."5685 if ($_(formattableBlockNames).indexOf(value) == -1) {5686 return;5687 }5688 // "Block-extend the active range, and let new range be the result."5689 var newRange = blockExtend(getActiveRange());5690 // "Let node list be an empty list of nodes."5691 //5692 // "For each node node contained in new range, append node to node list5693 // if it is editable, the last member of original node list (if any) is5694 // not an ancestor of node, node is either a non-list single-line5695 // container or an allowed child of "p" or a dd or dt, and node is not5696 // the ancestor of a prohibited paragraph child."5697 var nodeList = getContainedNodes(newRange, function(node) {5698 return isEditable(node)5699 && (isNonListSingleLineContainer(node)5700 || isAllowedChild(node, "p")5701 || isHtmlElement(node, ["dd", "dt"]))5702 && !$_( getDescendants(node) ).some(isProhibitedParagraphChild);5703 });5704 // "Record the values of node list, and let values be the result."5705 var values = recordValues(nodeList);5706 // "For each node in node list, while node is the descendant of an5707 // editable HTML element in the same editing host, whose local name is5708 // a formattable block name, and which is not the ancestor of a5709 // prohibited paragraph child, split the parent of the one-node list5710 // consisting of node."5711 for (var i = 0; i < nodeList.length; i++) {5712 var node = nodeList[i];5713 while ($_( getAncestors(node) ).some(function(ancestor) {5714 return isEditable(ancestor)5715 && inSameEditingHost(ancestor, node)5716 && isHtmlElement(ancestor, formattableBlockNames)5717 && !$_( getDescendants(ancestor) ).some(isProhibitedParagraphChild);5718 })) {5719 splitParent([node], range);5720 }5721 }5722 // "Restore the values from values."5723 restoreValues(values, range);5724 // "While node list is not empty:"5725 while (nodeList.length) {5726 var sublist;5727 // "If the first member of node list is a single-line5728 // container:"5729 if (isSingleLineContainer(nodeList[0])) {5730 // "Let sublist be the children of the first member of node5731 // list."5732 sublist = [].slice.call(toArray(nodeList[0].childNodes));5733 // "Record the values of sublist, and let values be the5734 // result."5735 var values = recordValues(sublist);5736 // "Remove the first member of node list from its parent,5737 // preserving its descendants."5738 removePreservingDescendants(nodeList[0], range);5739 // "Restore the values from values."5740 restoreValues(values, range);5741 // "Remove the first member from node list."5742 nodeList.shift();5743 // "Otherwise:"5744 } else {5745 // "Let sublist be an empty list of nodes."5746 sublist = [];5747 // "Remove the first member of node list and append it to5748 // sublist."5749 sublist.push(nodeList.shift());5750 // "While node list is not empty, and the first member of5751 // node list is the nextSibling of the last member of5752 // sublist, and the first member of node list is not a5753 // single-line container, and the last member of sublist is5754 // not a br, remove the first member of node list and5755 // append it to sublist."5756 while (nodeList.length5757 && nodeList[0] == sublist[sublist.length - 1].nextSibling5758 && !isSingleLineContainer(nodeList[0])5759 && !isHtmlElement(sublist[sublist.length - 1], "BR")) {5760 sublist.push(nodeList.shift());5761 }5762 }5763 // "Wrap sublist. If value is "div" or "p", sibling criteria5764 // returns false; otherwise it returns true for an HTML element5765 // with local name value and no attributes, and false otherwise.5766 // New parent instructions return the result of running5767 // createElement(value) on the context object. Then fix disallowed5768 // ancestors of the result."5769 fixDisallowedAncestors(5770 wrap(sublist,5771 $_(["div", "p"]).indexOf(value) == - 15772 ? function(node) { return isHtmlElement(node, value) && !node.attributes.length }5773 : function() { return false },5774 function() { return document.createElement(value) },5775 range5776 ),5777 range5778 );5779 }5780 }, indeterm: function() {5781 // "Block-extend the active range, and let new range be the result."5782 var newRange = blockExtend(getActiveRange());5783 // "Let node list be all visible editable nodes that are contained in5784 // new range and have no children."5785 var nodeList = getAllContainedNodes(newRange, function(node) {5786 return isVisible(node)5787 && isEditable(node)5788 && !node.hasChildNodes();5789 });5790 // "If node list is empty, return false."5791 if (!nodeList.length) {5792 return false;5793 }5794 // "Let type be null."5795 var type = null;5796 // "For each node in node list:"5797 for (var i = 0; i < nodeList.length; i++) {5798 var node = nodeList[i];5799 // "While node's parent is editable and in the same editing host as5800 // node, and node is not an HTML element whose local name is a5801 // formattable block name, set node to its parent."5802 while (isEditable(node.parentNode)5803 && inSameEditingHost(node, node.parentNode)5804 && !isHtmlElement(node, formattableBlockNames)) {5805 node = node.parentNode;5806 }5807 // "Let current type be the empty string."5808 var currentType = "";5809 // "If node is an editable HTML element whose local name is a5810 // formattable block name, and node is not the ancestor of a5811 // prohibited paragraph child, set current type to node's local5812 // name."5813 if (isEditable(node)5814 && isHtmlElement(node, formattableBlockNames)5815 && !$_( getDescendants(node) ).some(isProhibitedParagraphChild)) {5816 currentType = node.tagName;5817 }5818 // "If type is null, set type to current type."5819 if (type === null) {5820 type = currentType;5821 // "Otherwise, if type does not equal current type, return true."5822 } else if (type != currentType) {5823 return true;5824 }5825 }5826 // "Return false."5827 return false;5828 }, value: function() {5829 // "Block-extend the active range, and let new range be the result."5830 var newRange = blockExtend(getActiveRange());5831 // "Let node be the first visible editable node that is contained in5832 // new range and has no children. If there is no such node, return the5833 // empty string."5834 var nodes = getAllContainedNodes(newRange, function(node) {5835 return isVisible(node)5836 && isEditable(node)5837 && !node.hasChildNodes();5838 });5839 if (!nodes.length) {5840 return "";5841 }5842 var node = nodes[0];5843 // "While node's parent is editable and in the same editing host as5844 // node, and node is not an HTML element whose local name is a5845 // formattable block name, set node to its parent."5846 while (isEditable(node.parentNode)5847 && inSameEditingHost(node, node.parentNode)5848 && !isHtmlElement(node, formattableBlockNames)) {5849 node = node.parentNode;5850 }5851 // "If node is an editable HTML element whose local name is a5852 // formattable block name, and node is not the ancestor of a prohibited5853 // paragraph child, return node's local name, converted to ASCII5854 // lowercase."5855 if (isEditable(node)5856 && isHtmlElement(node, formattableBlockNames)5857 && !$_( getDescendants(node) ).some(isProhibitedParagraphChild)) {5858 return node.tagName.toLowerCase();5859 }5860 // "Return the empty string."5861 return "";5862 }5863};5864//@}5865///// The forwardDelete command /////5866//@{5867commands.forwarddelete = {5868 action: function(value, range) {5869 5870 // "If the active range is not collapsed, delete the contents of the5871 // active range and abort these steps."5872 if (!range.collapsed) {5873 deleteContents(range);5874 return;5875 }5876 // "Canonicalize whitespace at (active range's start node, active5877 // range's start offset)."5878 canonicalizeWhitespace(range.startContainer, range.startOffset);5879 // "Let node and offset be the active range's start node and offset."5880 var node = range.startContainer;5881 var offset = range.startOffset;5882 var isBr = false;5883 var isHr = false;5884 // "Repeat the following steps:"5885 while (true) {5886 // check whether the next element is a br or hr5887 if ( offset < node.childNodes.length ) {5888 isBr = isHtmlElement(node.childNodes[offset], "br") || false;5889 isHr = isHtmlElement(node.childNodes[offset], "hr") || false;5890 }5891 // "If offset is the length of node and node's nextSibling is an5892 // editable invisible node, remove node's nextSibling from its5893 // parent."5894 if (offset == getNodeLength(node)5895 && isEditable(node.nextSibling)5896 && isInvisible(node.nextSibling)) {5897 node.parentNode.removeChild(node.nextSibling);5898 // "Otherwise, if node has a child with index offset and that child5899 // is an editable invisible node, remove that child from node."5900 } else if (offset < node.childNodes.length5901 && isEditable(node.childNodes[offset])5902 && (isInvisible(node.childNodes[offset]) || isBr || isHr )) {5903 node.removeChild(node.childNodes[offset]);5904 if (isBr || isHr) {5905 range.setStart(node, offset);5906 range.setEnd(node, offset);5907 return;5908 }5909 // "Otherwise, if node has a child with index offset and that child5910 // is a collapsed block prop, add one to offset."5911 } else if (offset < node.childNodes.length5912 && isCollapsedBlockProp(node.childNodes[offset])) {5913 offset++;5914 // "Otherwise, if offset is the length of node and node is an5915 // inline node, or if node is invisible, set offset to one plus the5916 // index of node, then set node to its parent."5917 } else if ((offset == getNodeLength(node)5918 && isInlineNode(node))5919 || isInvisible(node)) {5920 offset = 1 + getNodeIndex(node);5921 node = node.parentNode;5922 // "Otherwise, if node has a child with index offset and that child5923 // is not a block node or a br or an img, set node to that child,5924 // then set offset to zero."5925 } else if (offset < node.childNodes.length5926 && !isBlockNode(node.childNodes[offset])5927 && !isHtmlElement(node.childNodes[offset], ["br", "img"])) {5928 node = node.childNodes[offset];5929 offset = 0;5930 // "Otherwise, break from this loop."5931 } else {5932 break;5933 }5934 }5935 // collapse whitespace in the node, if it is a text node5936 collapseWhitespace(node, range);5937 offset = range.startOffset;5938 // "If node is a Text node and offset is not node's length:"5939 if (node.nodeType == $_.Node.TEXT_NODE5940 && offset != getNodeLength(node)) {5941 // "Call collapse(node, offset) on the Selection."5942 range.setStart(node, offset);5943 range.setEnd(node, offset);5944 // "Let end offset be offset plus one."5945 var endOffset = offset + 1;5946 // "While end offset is not node's length and the end offsetth5947 // element of node's data has general category M when interpreted5948 // as a Unicode code point, add one to end offset."5949 //5950 // TODO: Not even going to try handling anything beyond the most5951 // basic combining marks, since I couldn't find a good list. I5952 // special-case a few Hebrew diacritics too to test basic coverage5953 // of non-Latin stuff.5954 while (endOffset != node.length5955 && /^[\u0300-\u036f\u0591-\u05bd\u05c1\u05c2]$/.test(node.data[endOffset])) {5956 endOffset++;5957 }5958 // "Delete the contents of the range with start (node, offset) and5959 // end (node, end offset)."5960 deleteContents(node, offset, node, endOffset);5961 // "Abort these steps."5962 return;5963 }5964 // "If node is an inline node, abort these steps."5965 if (isInlineNode(node)) {5966 return;5967 }5968 // "If node has a child with index offset and that child is a br or hr5969 // or img, call collapse(node, offset) on the Selection. Then delete5970 // the contents of the range with start (node, offset) and end (node,5971 // offset + 1) and abort these steps."5972 if (offset < node.childNodes.length5973 && isHtmlElement(node.childNodes[offset], ["br", "hr", "img"])) {5974 range.setStart(node, offset);5975 range.setEnd(node, offset);5976 deleteContents(node, offset, node, offset + 1);5977 return;5978 }5979 // "Let end node equal node and let end offset equal offset."5980 var endNode = node;5981 var endOffset = offset;5982 // "Repeat the following steps:"5983 while (true) {5984 // "If end offset is the length of end node, set end offset to one5985 // plus the index of end node and then set end node to its parent."5986 if (endOffset == getNodeLength(endNode)) {5987 endOffset = 1 + getNodeIndex(endNode);5988 endNode = endNode.parentNode;5989 // "Otherwise, if end node has a an editable invisible child with5990 // index end offset, remove it from end node."5991 } else if (endOffset < endNode.childNodes.length5992 && isEditable(endNode.childNodes[endOffset])5993 && isInvisible(endNode.childNodes[endOffset])) {5994 endNode.removeChild(endNode.childNodes[endOffset]);5995 // "Otherwise, break from this loop."5996 } else {5997 break;5998 }5999 }6000 // "If the child of end node with index end offset minus one is a6001 // table, abort these steps."6002 if (isHtmlElement(endNode.childNodes[endOffset - 1], "table")) {6003 return;6004 }6005 // "If the child of end node with index end offset is a table:"6006 if (isHtmlElement(endNode.childNodes[endOffset], "table")) {6007 // "Call collapse(end node, end offset) on the context object's6008 // Selection."6009 range.setStart(endNode, endOffset);6010 // "Call extend(end node, end offset + 1) on the context object's6011 // Selection."6012 range.setEnd(endNode, endOffset + 1);6013 // "Abort these steps."6014 return;6015 }6016 // "If offset is the length of node, and the child of end node with6017 // index end offset is an hr or br:"6018 if (offset == getNodeLength(node)6019 && isHtmlElement(endNode.childNodes[endOffset], ["br", "hr"])) {6020 // "Call collapse(node, offset) on the Selection."6021 range.setStart(node, offset);6022 range.setEnd(node, offset);6023 // "Delete the contents of the range with end (end node, end6024 // offset) and end (end node, end offset + 1)."6025 deleteContents(endNode, endOffset, endNode, endOffset + 1);6026 // "Abort these steps."6027 return;6028 }6029 // "While end node has a child with index end offset:"6030 while (endOffset < endNode.childNodes.length) {6031 // "If end node's child with index end offset is editable and6032 // invisible, remove it from end node."6033 if (isEditable(endNode.childNodes[endOffset])6034 && isInvisible(endNode.childNodes[endOffset])) {6035 endNode.removeChild(endNode.childNodes[endOffset]);6036 // "Otherwise, set end node to its child with index end offset and6037 // set end offset to zero."6038 } else {6039 endNode = endNode.childNodes[endOffset];6040 endOffset = 0;6041 }6042 }6043 // "Delete the contents of the range with start (node, offset) and end6044 // (end node, end offset)."6045 deleteContents(node, offset, endNode, endOffset);6046 }6047};6048//@}6049///// The indent command /////6050//@{6051commands.indent = {6052 action: function() {6053 // "Let items be a list of all lis that are ancestor containers of the6054 // active range's start and/or end node."6055 //6056 // Has to be in tree order, remember!6057 var items = [];6058 for (var node = getActiveRange().endContainer; node != getActiveRange().commonAncestorContainer; node = node.parentNode) {6059 if (isHtmlElement(node, "LI")) {6060 items.unshift(node);6061 }6062 }6063 for (var node = getActiveRange().startContainer; node != getActiveRange().commonAncestorContainer; node = node.parentNode) {6064 if (isHtmlElement(node, "LI")) {6065 items.unshift(node);6066 }6067 }6068 for (var node = getActiveRange().commonAncestorContainer; node; node = node.parentNode) {6069 if (isHtmlElement(node, "LI")) {6070 items.unshift(node);6071 }6072 }6073 // "For each item in items, normalize sublists of item."6074 for (var i = 0; i < items.length; i++) {6075 normalizeSublists(items[i, range]);6076 }6077 // "Block-extend the active range, and let new range be the result."6078 var newRange = blockExtend(getActiveRange());6079 // "Let node list be a list of nodes, initially empty."6080 var nodeList = [];6081 // "For each node node contained in new range, if node is editable and6082 // is an allowed child of "div" or "ol" and if the last member of node6083 // list (if any) is not an ancestor of node, append node to node list."6084 nodeList = getContainedNodes(newRange, function(node) {6085 return isEditable(node)6086 && (isAllowedChild(node, "div")6087 || isAllowedChild(node, "ol"));6088 });6089 // "If the first member of node list is an li whose parent is an ol or6090 // ul, and its previousSibling is an li as well, normalize sublists of6091 // its previousSibling."6092 if (nodeList.length6093 && isHtmlElement(nodeList[0], "LI")6094 && isHtmlElement(nodeList[0].parentNode, ["OL", "UL"])6095 && isHtmlElement(nodeList[0].previousSibling, "LI")) {6096 normalizeSublists(nodeList[0].previousSibling, range);6097 }6098 // "While node list is not empty:"6099 while (nodeList.length) {6100 // "Let sublist be a list of nodes, initially empty."6101 var sublist = [];6102 // "Remove the first member of node list and append it to sublist."6103 sublist.push(nodeList.shift());6104 // "While the first member of node list is the nextSibling of the6105 // last member of sublist, remove the first member of node list and6106 // append it to sublist."6107 while (nodeList.length6108 && nodeList[0] == sublist[sublist.length - 1].nextSibling) {6109 sublist.push(nodeList.shift());6110 }6111 // "Indent sublist."6112 indentNodes(sublist, range);6113 }6114 }6115};6116//@}6117///// The insertHorizontalRule command /////6118//@{6119commands.inserthorizontalrule = {6120 action: function(value, range) {6121 6122 // "While range's start offset is 0 and its start node's parent is not6123 // null, set range's start to (parent of start node, index of start6124 // node)."6125 while (range.startOffset == 06126 && range.startContainer.parentNode) {6127 range.setStart(range.startContainer.parentNode, getNodeIndex(range.startContainer));6128 }6129 // "While range's end offset is the length of its end node, and its end6130 // node's parent is not null, set range's end to (parent of end node, 16131 // + index of start node)."6132 while (range.endOffset == getNodeLength(range.endContainer)6133 && range.endContainer.parentNode) {6134 range.setEnd(range.endContainer.parentNode, 1 + getNodeIndex(range.endContainer));6135 }6136 // "Delete the contents of range, with block merging false."6137 deleteContents(range, {blockMerging: false});6138 // "If the active range's start node is neither editable nor an editing6139 // host, abort these steps."6140 if (!isEditable(getActiveRange().startContainer)6141 && !isEditingHost(getActiveRange().startContainer)) {6142 return;6143 }6144 // "If the active range's start node is a Text node and its start6145 // offset is zero, set the active range's start and end to (parent of6146 // start node, index of start node)."6147 if (getActiveRange().startContainer.nodeType == $_.Node.TEXT_NODE6148 && getActiveRange().startOffset == 0) {6149 getActiveRange().setStart(getActiveRange().startContainer.parentNode, getNodeIndex(getActiveRange().startContainer));6150 getActiveRange().collapse(true);6151 }6152 // "If the active range's start node is a Text node and its start6153 // offset is the length of its start node, set the active range's start6154 // and end to (parent of start node, 1 + index of start node)."6155 if (getActiveRange().startContainer.nodeType == $_.Node.TEXT_NODE6156 && getActiveRange().startOffset == getNodeLength(getActiveRange().startContainer)) {6157 getActiveRange().setStart(getActiveRange().startContainer.parentNode, 1 + getNodeIndex(getActiveRange().startContainer));6158 getActiveRange().collapse(true);6159 }6160 // "Let hr be the result of calling createElement("hr") on the6161 // context object."6162 var hr = document.createElement("hr");6163 // "Run insertNode(hr) on the range."6164 range.insertNode(hr);6165 // "Fix disallowed ancestors of hr."6166 fixDisallowedAncestors(hr, range);6167 // "Run collapse() on the Selection, with first argument equal to the6168 // parent of hr and the second argument equal to one plus the index of6169 // hr."6170 //6171 // Not everyone actually supports collapse(), so we do it manually6172 // instead. Also, we need to modify the actual range we're given as6173 // well, for the sake of autoimplementation.html's range-filling-in.6174 range.setStart(hr.parentNode, 1 + getNodeIndex(hr));6175 range.setEnd(hr.parentNode, 1 + getNodeIndex(hr));6176 Aloha.getSelection().removeAllRanges();6177 Aloha.getSelection().addRange(range);6178 }6179};6180//@}6181///// The insertHTML command /////6182//@{6183commands.inserthtml = {6184 action: function(value, range) {6185 6186 6187 // "Delete the contents of the active range."6188 deleteContents(range);6189 // "If the active range's start node is neither editable nor an editing6190 // host, abort these steps."6191 if (!isEditable(range.startContainer)6192 && !isEditingHost(range.startContainer)) {6193 return;6194 }6195 // "Let frag be the result of calling createContextualFragment(value)6196 // on the active range."6197 var frag = range.createContextualFragment(value);6198 // "Let last child be the lastChild of frag."6199 var lastChild = frag.lastChild;6200 // "If last child is null, abort these steps."6201 if (!lastChild) {6202 return;6203 }6204 // "Let descendants be all descendants of frag."6205 var descendants = getDescendants(frag);6206 // "If the active range's start node is a block node:"6207 if (isBlockNode(range.startContainer)) {6208 // "Let collapsed block props be all editable collapsed block prop6209 // children of the active range's start node that have index6210 // greater than or equal to the active range's start offset."6211 //6212 // "For each node in collapsed block props, remove node from its6213 // parent."6214 $_(range.startContainer.childNodes).filter(function(node, range) {6215 return isEditable(node)6216 && isCollapsedBlockProp(node)6217 && getNodeIndex(node) >= range.startOffset;6218 }, true).forEach(function(node) {6219 node.parentNode.removeChild(node);6220 });6221 }6222 // "Call insertNode(frag) on the active range."6223 range.insertNode(frag);6224 // "If the active range's start node is a block node with no visible6225 // children, call createElement("br") on the context object and append6226 // the result as the last child of the active range's start node."6227 if (isBlockNode(range.startContainer)6228 && !$_(range.startContainer.childNodes).some(isVisible)) {6229 range.startContainer.appendChild(createEndBreak());6230 }6231 // "Call collapse() on the context object's Selection, with last6232 // child's parent as the first argument and one plus its index as the6233 // second."6234 range.setStart(lastChild.parentNode, 1 + getNodeIndex(lastChild));6235 range.setEnd(lastChild.parentNode, 1 + getNodeIndex(lastChild));6236 // "Fix disallowed ancestors of each member of descendants."6237 for (var i = 0; i < descendants.length; i++) {6238 fixDisallowedAncestors(descendants[i], range);6239 }6240 6241 setActiveRange( range );6242 }6243};6244//@}6245///// The insertImage command /////6246//@{6247commands.insertimage = {6248 action: function(value) {6249 // "If value is the empty string, abort these steps and do nothing."6250 if (value === "") {6251 return;6252 }6253 // "Let range be the active range."6254 var range = getActiveRange();6255 // "Delete the contents of range, with strip wrappers false."6256 deleteContents(range, {stripWrappers: false});6257 // "If the active range's start node is neither editable nor an editing6258 // host, abort these steps."6259 if (!isEditable(getActiveRange().startContainer)6260 && !isEditingHost(getActiveRange().startContainer)) {6261 return;6262 }6263 // "If range's start node is a block node whose sole child is a br, and6264 // its start offset is 0, remove its start node's child from it."6265 if (isBlockNode(range.startContainer)6266 && range.startContainer.childNodes.length == 16267 && isHtmlElement(range.startContainer.firstChild, "br")6268 && range.startOffset == 0) {6269 range.startContainer.removeChild(range.startContainer.firstChild);6270 }6271 // "Let img be the result of calling createElement("img") on the6272 // context object."6273 var img = document.createElement("img");6274 // "Run setAttribute("src", value) on img."6275 img.setAttribute("src", value);6276 // "Run insertNode(img) on the range."6277 range.insertNode(img);6278 // "Run collapse() on the Selection, with first argument equal to the6279 // parent of img and the second argument equal to one plus the index of6280 // img."6281 //6282 // Not everyone actually supports collapse(), so we do it manually6283 // instead. Also, we need to modify the actual range we're given as6284 // well, for the sake of autoimplementation.html's range-filling-in.6285 range.setStart(img.parentNode, 1 + getNodeIndex(img));6286 range.setEnd(img.parentNode, 1 + getNodeIndex(img));6287 Aloha.getSelection().removeAllRanges();6288 Aloha.getSelection().addRange(range);6289 // IE adds width and height attributes for some reason, so remove those6290 // to actually do what the spec says.6291 img.removeAttribute("width");6292 img.removeAttribute("height");6293 }6294};6295//@}6296///// The insertLineBreak command /////6297//@{6298commands.insertlinebreak = {6299 action: function(value, range) {6300 // "Delete the contents of the active range, with strip wrappers false."6301 deleteContents(range, {stripWrappers: false});6302 // "If the active range's start node is neither editable nor an editing6303 // host, abort these steps."6304 if (!isEditable(range.startContainer)6305 && !isEditingHost(range.startContainer)) {6306 return;6307 }6308 // "If the active range's start node is an Element, and "br" is not an6309 // allowed child of it, abort these steps."6310 if (range.startContainer.nodeType == $_.Node.ELEMENT_NODE6311 && !isAllowedChild("br", range.startContainer)) {6312 return;6313 }6314 // "If the active range's start node is not an Element, and "br" is not6315 // an allowed child of the active range's start node's parent, abort6316 // these steps."6317 if (range.startContainer.nodeType != $_.Node.ELEMENT_NODE6318 && !isAllowedChild("br", range.startContainer.parentNode)) {6319 return;6320 }6321 // "If the active range's start node is a Text node and its start6322 // offset is zero, call collapse() on the context object's Selection,6323 // with first argument equal to the active range's start node's parent6324 // and second argument equal to the active range's start node's index."6325 if (range.startContainer.nodeType == $_.Node.TEXT_NODE6326 && range.startOffset == 0) {6327 var newNode = range.startContainer.parentNode;6328 var newOffset = getNodeIndex(range.startContainer);6329 Aloha.getSelection().collapse(newNode, newOffset);6330 range.setStart(newNode, newOffset);6331 range.setEnd(newNode, newOffset);6332 }6333 // "If the active range's start node is a Text node and its start6334 // offset is the length of its start node, call collapse() on the6335 // context object's Selection, with first argument equal to the active6336 // range's start node's parent and second argument equal to one plus6337 // the active range's start node's index."6338 if (range.startContainer.nodeType == $_.Node.TEXT_NODE6339 && range.startOffset == getNodeLength(range.startContainer)) {6340 var newNode = range.startContainer.parentNode;6341 var newOffset = 1 + getNodeIndex(range.startContainer);6342 Aloha.getSelection().collapse(newNode, newOffset);6343 range.setStart(newNode, newOffset);6344 range.setEnd(newNode, newOffset);6345 }6346 // "Let br be the result of calling createElement("br") on the context6347 // object."6348 var br = document.createElement("br");6349 // "Call insertNode(br) on the active range."6350 range.insertNode(br);6351 // "Call collapse() on the context object's Selection, with br's parent6352 // as the first argument and one plus br's index as the second6353 // argument."6354 Aloha.getSelection().collapse(br.parentNode, 1 + getNodeIndex(br));6355 range.setStart(br.parentNode, 1 + getNodeIndex(br));6356 range.setEnd(br.parentNode, 1 + getNodeIndex(br));6357 // "If br is a collapsed line break, call createElement("br") on the6358 // context object and let extra br be the result, then call6359 // insertNode(extra br) on the active range."6360 if (isCollapsedLineBreak(br)) {6361 range.insertNode(createEndBreak());6362 // Compensate for nonstandard implementations of insertNode6363 Aloha.getSelection().collapse(br.parentNode, 1 + getNodeIndex(br));6364 range.setStart(br.parentNode, 1 + getNodeIndex(br));6365 range.setEnd(br.parentNode, 1 + getNodeIndex(br));6366 }6367 }6368};6369//@}6370///// The insertOrderedList command /////6371//@{6372commands.insertorderedlist = {6373 // "Toggle lists with tag name "ol"."6374 action: function() { toggleLists("ol") },6375 // "True if the selection's list state is "mixed" or "mixed ol", false6376 // otherwise."6377 indeterm: function() { return /^mixed( ol)?$/.test(getSelectionListState()) },6378 // "True if the selection's list state is "ol", false otherwise."6379 state: function() { return getSelectionListState() == "ol" }6380};6381//@}6382///// The insertParagraph command /////6383//@{6384commands.insertparagraph = {6385 action: function(value, range) {6386 6387 // "Delete the contents of the active range."6388 deleteContents(range);6389 // "If the active range's start node is neither editable nor an editing6390 // host, abort these steps."6391 if (!isEditable(range.startContainer)6392 && !isEditingHost(range.startContainer)) {6393 return;6394 }6395 // "Let node and offset be the active range's start node and offset."6396 var node = range.startContainer;6397 var offset = range.startOffset;6398 // "If node is a Text node, and offset is neither 0 nor the length of6399 // node, call splitText(offset) on node."6400 if (node.nodeType == $_.Node.TEXT_NODE6401 && offset != 06402 && offset != getNodeLength(node)) {6403 node.splitText(offset);6404 }6405 // "If node is a Text node and offset is its length, set offset to one6406 // plus the index of node, then set node to its parent."6407 if (node.nodeType == $_.Node.TEXT_NODE6408 && offset == getNodeLength(node)) {6409 offset = 1 + getNodeIndex(node);6410 node = node.parentNode;6411 }6412 // "If node is a Text or Comment node, set offset to the index of node,6413 // then set node to its parent."6414 if (node.nodeType == $_.Node.TEXT_NODE6415 || node.nodeType == $_.Node.COMMENT_NODE) {6416 offset = getNodeIndex(node);6417 node = node.parentNode;6418 }6419 // "Call collapse(node, offset) on the context object's Selection."6420 Aloha.getSelection().collapse(node, offset);6421 range.setStart(node, offset);6422 range.setEnd(node, offset);6423 // "Let container equal node."6424 var container = node;6425 // "While container is not a single-line container, and container's6426 // parent is editable and in the same editing host as node, set6427 // container to its parent."6428 while (!isSingleLineContainer(container)6429 && isEditable(container.parentNode)6430 && inSameEditingHost(node, container.parentNode)) {6431 container = container.parentNode;6432 }6433 // "If container is not editable or not in the same editing host as6434 // node or is not a single-line container:"6435 if (!isEditable(container)6436 || !inSameEditingHost(container, node)6437 || !isSingleLineContainer(container)) {6438 // "Let tag be the default single-line container name."6439 var tag = defaultSingleLineContainerName;6440 // "Block-extend the active range, and let new range be the6441 // result."6442 var newRange = blockExtend(range);6443 // "Let node list be a list of nodes, initially empty."6444 //6445 // "Append to node list the first node in tree order that is6446 // contained in new range and is an allowed child of "p", if any."6447 var nodeList = getContainedNodes(newRange, function(node) { return isAllowedChild(node, "p") })6448 .slice(0, 1);6449 // "If node list is empty:"6450 if (!nodeList.length) {6451 // "If tag is not an allowed child of the active range's start6452 // node, abort these steps."6453 if (!isAllowedChild(tag, range.startContainer)) {6454 return;6455 }6456 // "Set container to the result of calling createElement(tag)6457 // on the context object."6458 container = document.createElement(tag);6459 // "Call insertNode(container) on the active range."6460 range.insertNode(container);6461 // "Call createElement("br") on the context object, and append6462 // the result as the last child of container."6463 container.appendChild(createEndBreak());6464 // "Call collapse(container, 0) on the context object's6465 // Selection."6466 // TODO: remove selection from command6467 Aloha.getSelection().collapse(container, 0); 6468 range.setStart(container, 0);6469 range.setEnd(container, 0);6470 // "Abort these steps."6471 return;6472 }6473 // "While the nextSibling of the last member of node list is not6474 // null and is an allowed child of "p", append it to node list."6475 while (nodeList[nodeList.length - 1].nextSibling6476 && isAllowedChild(nodeList[nodeList.length - 1].nextSibling, "p")) {6477 nodeList.push(nodeList[nodeList.length - 1].nextSibling);6478 }6479 // "Wrap node list, with sibling criteria returning false and new6480 // parent instructions returning the result of calling6481 // createElement(tag) on the context object. Set container to the6482 // result."6483 container = wrap(nodeList,6484 function() { return false },6485 function() { return document.createElement(tag) },6486 range6487 );6488 }6489 // "If container's local name is "address", "listing", or "pre":"6490 if (container.tagName == "ADDRESS"6491 || container.tagName == "LISTING"6492 || container.tagName == "PRE") {6493 // "Let br be the result of calling createElement("br") on the6494 // context object."6495 var br = document.createElement("br");6496 // remember the old height6497 var oldHeight = container.offsetHeight;6498 // "Call insertNode(br) on the active range."6499 range.insertNode(br);6500 // determine the new height6501 var newHeight = container.offsetHeight;6502 // "Call collapse(node, offset + 1) on the context object's6503 // Selection."6504 Aloha.getSelection().collapse(node, offset + 1);6505 range.setStart(node, offset + 1);6506 range.setEnd(node, offset + 1);6507 // "If br is the last descendant of container, let br be the result6508 // of calling createElement("br") on the context object, then call6509 // insertNode(br) on the active range." (Fix: only do this, if the container height did not change by inserting a single <br/>)6510 //6511 // Work around browser bugs: some browsers select the6512 // newly-inserted node, not per spec.6513 if (oldHeight == newHeight && !isDescendant(nextNode(br), container)) {6514 range.insertNode(createEndBreak());6515 Aloha.getSelection().collapse(node, offset + 1);6516 range.setEnd(node, offset + 1);6517 }6518 // "Abort these steps."6519 return;6520 }6521 // "If container's local name is "li", "dt", or "dd"; and either it has6522 // no children or it has a single child and that child is a br:"6523 if ($_(["LI", "DT", "DD"]).indexOf(container.tagName) != -16524 && (!container.hasChildNodes()6525 || (container.childNodes.length == 16526 && isHtmlElement(container.firstChild, "br")))) {6527 // "Split the parent of the one-node list consisting of container."6528 splitParent([container], range);6529 // "If container has no children, call createElement("br") on the6530 // context object and append the result as the last child of6531 // container."6532 // only do this, if inserting the br does NOT modify the offset height of the container6533 if (!container.hasChildNodes()) {6534 var oldHeight = container.offsetHeight, endBr = createEndBreak();6535 container.appendChild(endBr);6536 if (container.offsetHeight !== oldHeight) {6537 container.removeChild(endBr);6538 }6539 }6540 // "If container is a dd or dt, and it is not an allowed child of6541 // any of its ancestors in the same editing host, set the tag name6542 // of container to the default single-line container name and let6543 // container be the result."6544 if (isHtmlElement(container, ["dd", "dt"])6545 && $_( getAncestors(container) ).every(function(ancestor) {6546 return !inSameEditingHost(container, ancestor)6547 || !isAllowedChild(container, ancestor)6548 })) {6549 container = setTagName(container, defaultSingleLineContainerName, range);6550 }6551 // "Fix disallowed ancestors of container."6552 fixDisallowedAncestors(container, range);6553 // fix invalid nested lists6554 if (isHtmlElement(container, "li")6555 && isHtmlElement(container.nextSibling, "li")6556 && isHtmlElement(container.nextSibling.firstChild, ["ol", "ul"])) {6557 // we found a li containing only a br followed by a li containing a list as first element: merge the two li's6558 var listParent = container.nextSibling, length = container.nextSibling.childNodes.length;6559 for (var i = 0; i < length; i++) {6560 container.appendChild(listParent.childNodes[i]);6561 }6562 listParent.parentNode.removeChild(listParent);6563 }6564 // "Abort these steps."6565 return;6566 }6567 // "Let new line range be a new range whose start is the same as6568 // the active range's, and whose end is (container, length of6569 // container)."6570 var newLineRange = Aloha.createRange();6571 newLineRange.setStart(range.startContainer, range.startOffset);6572 newLineRange.setEnd(container, getNodeLength(container));6573 // "While new line range's start offset is zero and its start node is6574 // not container, set its start to (parent of start node, index of6575 // start node)."6576 while (newLineRange.startOffset == 06577 && newLineRange.startContainer != container) {6578 newLineRange.setStart(newLineRange.startContainer.parentNode, getNodeIndex(newLineRange.startContainer));6579 }6580 // "While new line range's start offset is the length of its start node6581 // and its start node is not container, set its start to (parent of6582 // start node, 1 + index of start node)."6583 while (newLineRange.startOffset == getNodeLength(newLineRange.startContainer)6584 && newLineRange.startContainer != container) {6585 newLineRange.setStart(newLineRange.startContainer.parentNode, 1 + getNodeIndex(newLineRange.startContainer));6586 }6587 // "Let end of line be true if new line range contains either nothing6588 // or a single br, and false otherwise."6589 var containedInNewLineRange = getContainedNodes(newLineRange);6590 var endOfLine = !containedInNewLineRange.length6591 || (containedInNewLineRange.length == 16592 && isHtmlElement(containedInNewLineRange[0], "br"));6593 // "If the local name of container is "h1", "h2", "h3", "h4", "h5", or6594 // "h6", and end of line is true, let new container name be the default6595 // single-line container name."6596 var newContainerName;6597 if (/^H[1-6]$/.test(container.tagName)6598 && endOfLine) {6599 newContainerName = defaultSingleLineContainerName;6600 // "Otherwise, if the local name of container is "dt" and end of line6601 // is true, let new container name be "dd"."6602 } else if (container.tagName == "DT"6603 && endOfLine) {6604 newContainerName = "dd";6605 // "Otherwise, if the local name of container is "dd" and end of line6606 // is true, let new container name be "dt"."6607 } else if (container.tagName == "DD"6608 && endOfLine) {6609 newContainerName = "dt";6610 // "Otherwise, let new container name be the local name of container."6611 } else {6612 newContainerName = container.tagName.toLowerCase();6613 }6614 // "Let new container be the result of calling createElement(new6615 // container name) on the context object."6616 var newContainer = document.createElement(newContainerName);6617 // "Copy all non empty attributes of the container to new container."6618 for ( var i = 0; i < container.attributes.length; i++ ) {6619 if ( typeof newContainer.setAttributeNS === 'function' ) {6620 newContainer.setAttributeNS(container.attributes[i].namespaceURI, container.attributes[i].name, container.attributes[i].value);6621 } else if ( container.attributes[i].value.length > 0 6622 && container.attributes[i].value != 'null'6623 && container.attributes[i].value > 0) {6624 newContainer.setAttribute(container.attributes[i].name, container.attributes[i].value);6625 }6626 }6627 // "If new container has an id attribute, unset it."6628 newContainer.removeAttribute("id");6629 // "Insert new container into the parent of container immediately after6630 // container."6631 container.parentNode.insertBefore(newContainer, container.nextSibling);6632 // "Let contained nodes be all nodes contained in new line range."6633 var containedNodes = getAllContainedNodes(newLineRange);6634 // "Let frag be the result of calling extractContents() on new line6635 // range."6636 var frag = newLineRange.extractContents();6637 // "Unset the id attribute (if any) of each Element descendant of frag6638 // that is not in contained nodes."6639 var descendants = getDescendants(frag);6640 for (var i = 0; i < descendants.length; i++) {6641 if (descendants[i].nodeType == $_.Node.ELEMENT_NODE6642 && $_(containedNodes).indexOf(descendants[i]) == -1) {6643 descendants[i].removeAttribute("id");6644 }6645 }6646 var fragChildren = [], fragChild = frag.firstChild;6647 if (fragChild) {6648 do {6649 if (!isWhitespaceNode(fragChild)) {6650 fragChildren.push(fragChild);6651 }6652 } while(fragChild = fragChild.nextSibling);6653 }6654 // if newContainer is a li and frag contains only a list, we add a br in the li (but only if the height would not change)6655 if (isHtmlElement(newContainer, "li") && fragChildren.length && isHtmlElement(fragChildren[0], ["ul", "ol"])) {6656 var oldHeight = newContainer.offsetHeight;6657 var endBr = createEndBreak();6658 newContainer.appendChild(endBr);6659 var newHeight = newContainer.offsetHeight;6660 if (oldHeight !== newHeight) {6661 newContainer.removeChild(endBr);6662 }6663 }6664 // "Call appendChild(frag) on new container."6665 newContainer.appendChild(frag);6666 // "If container has no visible children, call createElement("br") on6667 // the context object, and append the result as the last child of6668 // container."6669 if (container.offsetHeight == 0 && !$_(container.childNodes).some(isVisible)) {6670 container.appendChild(createEndBreak());6671 }6672 // "If new container has no visible children, call createElement("br")6673 // on the context object, and append the result as the last child of6674 // new container."6675 if (newContainer.offsetHeight == 0 && !$_(newContainer.childNodes).some(isVisible)) {6676 newContainer.appendChild(createEndBreak());6677 }6678 // "Call collapse(new container, 0) on the context object's Selection."6679 Aloha.getSelection().collapse(newContainer, 0);6680 range.setStart(newContainer, 0);6681 range.setEnd(newContainer, 0);6682 }6683};6684//@}6685///// The insertText command /////6686//@{6687commands.inserttext = {6688 action: function(value, range) {6689 // "Delete the contents of the active range, with strip wrappers6690 // false."6691 deleteContents(range, {stripWrappers: false});6692 // "If the active range's start node is neither editable nor an editing6693 // host, abort these steps."6694 if (!isEditable(range.startContainer)6695 && !isEditingHost(range.startContainer)) {6696 return;6697 }6698 // "If value's length is greater than one:"6699 if (value.length > 1) {6700 // "For each element el in value, take the action for the6701 // insertText command, with value equal to el."6702 for (var i = 0; i < value.length; i++) {6703 commands.inserttext.action( value[i], range );6704 }6705 // "Abort these steps."6706 return;6707 }6708 // "If value is the empty string, abort these steps."6709 if (value == "") {6710 return;6711 }6712 // "If value is a newline (U+00A0), take the action for the6713 // insertParagraph command and abort these steps."6714 if (value == "\n") {6715 commands.insertparagraph.action( '', range );6716 return;6717 }6718 // "Let node and offset be the active range's start node and offset."6719 var node = range.startContainer;6720 var offset = range.startOffset;6721 // "If node has a child whose index is offset − 1, and that child is a6722 // Text node, set node to that child, then set offset to node's6723 // length."6724 if (0 <= offset - 16725 && offset - 1 < node.childNodes.length6726 && node.childNodes[offset - 1].nodeType == $_.Node.TEXT_NODE) {6727 node = node.childNodes[offset - 1];6728 offset = getNodeLength(node);6729 }6730 // "If node has a child whose index is offset, and that child is a Text6731 // node, set node to that child, then set offset to zero."6732 if (0 <= offset6733 && offset < node.childNodes.length6734 && node.childNodes[offset].nodeType == $_.Node.TEXT_NODE) {6735 node = node.childNodes[offset];6736 offset = 0;6737 }6738 // "If value is a space (U+0020), and either node is an Element whose6739 // resolved value for "white-space" is neither "pre" nor "pre-wrap" or6740 // node is not an Element but its parent is an Element whose resolved6741 // value for "white-space" is neither "pre" nor "pre-wrap", set value6742 // to a non-breaking space (U+00A0)."6743 var refElement = node.nodeType == $_.Node.ELEMENT_NODE ? node : node.parentNode;6744 if (value == " "6745 && refElement.nodeType == $_.Node.ELEMENT_NODE6746 && $_(["pre", "pre-wrap"]).indexOf($_.getComputedStyle(refElement).whiteSpace) == -1) {6747 value = "\xa0";6748 }6749 // "Record current overrides, and let overrides be the result."6750 var overrides = recordCurrentOverrides( range );6751 // "If node is a Text node:"6752 if (node.nodeType == $_.Node.TEXT_NODE) {6753 // "Call insertData(offset, value) on node."6754 node.insertData(offset, value);6755 // "Call collapse(node, offset) on the context object's Selection."6756 Aloha.getSelection().collapse(node, offset);6757 range.setStart(node, offset);6758 // "Call extend(node, offset + 1) on the context object's6759 // Selection."6760 Aloha.getSelection().extend(node, offset + 1);6761 range.setEnd(node, offset + 1);6762 // "Otherwise:"6763 } else {6764 // "If node has only one child, which is a collapsed line break,6765 // remove its child from it."6766 //6767 // FIXME: IE incorrectly returns false here instead of true6768 // sometimes?6769 if (node.childNodes.length == 16770 && isCollapsedLineBreak(node.firstChild)) {6771 node.removeChild(node.firstChild);6772 }6773 // "Let text be the result of calling createTextNode(value) on the6774 // context object."6775 var text = document.createTextNode(value);6776 // "Call insertNode(text) on the active range."6777 range.insertNode(text);6778 // "Call collapse(text, 0) on the context object's Selection."6779 Aloha.getSelection().collapse(text, 0);6780 range.setStart(text, 0);6781 // "Call extend(text, 1) on the context object's Selection."6782 Aloha.getSelection().extend(text, 1);6783 range.setEnd(text, 1);6784 }6785 // "Restore states and values from overrides."6786 restoreStatesAndValues(overrides);6787 // "Canonicalize whitespace at the active range's start."6788 canonicalizeWhitespace(range.startContainer, range.startOffset);6789 // "Canonicalize whitespace at the active range's end."6790 canonicalizeWhitespace(range.endContainer, range.endOffset);6791 // "Call collapseToEnd() on the context object's Selection."6792 Aloha.getSelection().collapseToEnd();6793 range.collapse(false);6794 }6795};6796//@}6797///// The insertUnorderedList command /////6798//@{6799commands.insertunorderedlist = {6800 // "Toggle lists with tag name "ul"."6801 action: function() { toggleLists("ul") },6802 // "True if the selection's list state is "mixed" or "mixed ul", false6803 // otherwise."6804 indeterm: function() { return /^mixed( ul)?$/.test(getSelectionListState()) },6805 // "True if the selection's list state is "ul", false otherwise."6806 state: function() { return getSelectionListState() == "ul" }6807};6808//@}6809///// The justifyCenter command /////6810//@{6811commands.justifycenter = {6812 // "Justify the selection with alignment "center"."6813 action: function(value, range) { justifySelection("center", range) },6814 indeterm: function() {6815 // "Block-extend the active range. Return true if among visible6816 // editable nodes that are contained in the result and have no6817 // children, at least one has alignment value "center" and at least one6818 // does not. Otherwise return false."6819 var nodes = getAllContainedNodes(blockExtend(getActiveRange()), function(node) {6820 return isEditable(node) && isVisible(node) && !node.hasChildNodes();...

Full Screen

Full Screen

implementation.js

Source:implementation.js Github

copy

Full Screen

...3115 movePreservingRanges(child, newItem, 0);3116 }3117 }3118}3119function getSelectionListState() {3120 3121 if (!getActiveRange()) {3122 return "none";3123 }3124 3125 var newRange = blockExtend(getActiveRange());3126 3127 3128 3129 3130 3131 3132 var nodeList = getContainedNodes(newRange, function(node) {3133 return isEditable(node)3134 && !isIndentationElement(node)3135 && (isHtmlElement(node, ["ol", "ul"])3136 || isHtmlElement(node.parentNode, ["ol", "ul"])3137 || isAllowedChild(node, "li"));3138 });3139 3140 if (!nodeList.length) {3141 return "none";3142 }3143 3144 3145 3146 if (nodeList.every(function(node) {3147 return isHtmlElement(node, "ol")3148 || isHtmlElement(node.parentNode, "ol")3149 || (isHtmlElement(node.parentNode, "li") && isHtmlElement(node.parentNode.parentNode, "ol"));3150 })3151 && !nodeList.some(function(node) { return isHtmlElement(node, "ul") || ("querySelector" in node && node.querySelector("ul")) })) {3152 return "ol";3153 }3154 3155 3156 3157 if (nodeList.every(function(node) {3158 return isHtmlElement(node, "ul")3159 || isHtmlElement(node.parentNode, "ul")3160 || (isHtmlElement(node.parentNode, "li") && isHtmlElement(node.parentNode.parentNode, "ul"));3161 })3162 && !nodeList.some(function(node) { return isHtmlElement(node, "ol") || ("querySelector" in node && node.querySelector("ol")) })) {3163 return "ul";3164 }3165 var hasOl = nodeList.some(function(node) {3166 return isHtmlElement(node, "ol")3167 || isHtmlElement(node.parentNode, "ol")3168 || ("querySelector" in node && node.querySelector("ol"))3169 || (isHtmlElement(node.parentNode, "li") && isHtmlElement(node.parentNode.parentNode, "ol"));3170 });3171 var hasUl = nodeList.some(function(node) {3172 return isHtmlElement(node, "ul")3173 || isHtmlElement(node.parentNode, "ul")3174 || ("querySelector" in node && node.querySelector("ul"))3175 || (isHtmlElement(node.parentNode, "li") && isHtmlElement(node.parentNode.parentNode, "ul"));3176 });3177 3178 3179 3180 3181 if (hasOl && hasUl) {3182 return "mixed";3183 }3184 3185 3186 if (hasOl) {3187 return "mixed ol";3188 }3189 3190 3191 if (hasUl) {3192 return "mixed ul";3193 }3194 3195 return "none";3196}3197function getAlignmentValue(node) {3198 3199 3200 3201 while ((node && node.nodeType != Node.ELEMENT_NODE)3202 || (node.nodeType == Node.ELEMENT_NODE3203 && ["inline", "none"].indexOf(getComputedStyle(node).display) != -1)) {3204 node = node.parentNode;3205 }3206 3207 if (!node || node.nodeType != Node.ELEMENT_NODE) {3208 return "left";3209 }3210 var resolvedValue = getComputedStyle(node).textAlign3211 3212 .replace(/^-(moz|webkit)-/, "")3213 .replace(/^auto$/, "start");3214 3215 3216 if (resolvedValue == "start") {3217 return getDirectionality(node) == "ltr" ? "left" : "right";3218 }3219 3220 3221 if (resolvedValue == "end") {3222 return getDirectionality(node) == "ltr" ? "right" : "left";3223 }3224 3225 3226 if (["center", "justify", "left", "right"].indexOf(resolvedValue) != -1) {3227 return resolvedValue;3228 }3229 3230 return "left";3231}3232function getNextEquivalentPoint(node, offset) {3233 3234 if (getNodeLength(node) == 0) {3235 return null;3236 }3237 3238 3239 if (offset == getNodeLength(node)3240 && node.parentNode3241 && isInlineNode(node)) {3242 return [node.parentNode, 1 + getNodeIndex(node)];3243 }3244 3245 3246 if (0 <= offset3247 && offset < node.childNodes.length3248 && getNodeLength(node.childNodes[offset]) != 03249 && isInlineNode(node.childNodes[offset])) {3250 return [node.childNodes[offset], 0];3251 }3252 3253 return null;3254}3255function getPreviousEquivalentPoint(node, offset) {3256 3257 if (getNodeLength(node) == 0) {3258 return null;3259 }3260 3261 3262 if (offset == 03263 && node.parentNode3264 && isInlineNode(node)) {3265 return [node.parentNode, getNodeIndex(node)];3266 }3267 3268 3269 3270 if (0 <= offset - 13271 && offset - 1 < node.childNodes.length3272 && getNodeLength(node.childNodes[offset - 1]) != 03273 && isInlineNode(node.childNodes[offset - 1])) {3274 return [node.childNodes[offset - 1], getNodeLength(node.childNodes[offset - 1])];3275 }3276 3277 return null;3278}3279function getFirstEquivalentPoint(node, offset) {3280 3281 3282 var prev;3283 while (prev = getPreviousEquivalentPoint(node, offset)) {3284 node = prev[0];3285 offset = prev[1];3286 }3287 3288 return [node, offset];3289}3290function getLastEquivalentPoint(node, offset) {3291 3292 3293 var next;3294 while (next = getNextEquivalentPoint(node, offset)) {3295 node = next[0];3296 offset = next[1];3297 }3298 3299 return [node, offset];3300}3301function isBlockStartPoint(node, offset) {3302 return (!node.parentNode && offset == 0)3303 || (0 <= offset - 13304 && offset - 1 < node.childNodes.length3305 && isVisible(node.childNodes[offset - 1])3306 && (isBlockNode(node.childNodes[offset - 1])3307 || isHtmlElement(node.childNodes[offset - 1], "br")));3308}3309function isBlockEndPoint(node, offset) {3310 return (!node.parentNode && offset == getNodeLength(node))3311 || (offset < node.childNodes.length3312 && isVisible(node.childNodes[offset])3313 && isBlockNode(node.childNodes[offset]));3314}3315function isBlockBoundaryPoint(node, offset) {3316 return isBlockStartPoint(node, offset)3317 || isBlockEndPoint(node, offset);3318}3319function blockExtend(range) {3320 3321 3322 var startNode = range.startContainer;3323 var startOffset = range.startOffset;3324 var endNode = range.endContainer;3325 var endOffset = range.endOffset;3326 3327 3328 3329 var liAncestors = getAncestors(startNode).concat(startNode)3330 .filter(function(ancestor) { return isHtmlElement(ancestor, "li") })3331 .slice(-1);3332 if (liAncestors.length) {3333 startOffset = getNodeIndex(liAncestors[0]);3334 startNode = liAncestors[0].parentNode;3335 }3336 3337 3338 if (!isBlockStartPoint(startNode, startOffset)) do {3339 3340 3341 if (startOffset == 0) {3342 startOffset = getNodeIndex(startNode);3343 startNode = startNode.parentNode;3344 3345 } else {3346 startOffset--;3347 }3348 3349 3350 } while (!isBlockBoundaryPoint(startNode, startOffset));3351 3352 3353 while (startOffset == 03354 && startNode.parentNode) {3355 startOffset = getNodeIndex(startNode);3356 startNode = startNode.parentNode;3357 }3358 3359 3360 3361 var liAncestors = getAncestors(endNode).concat(endNode)3362 .filter(function(ancestor) { return isHtmlElement(ancestor, "li") })3363 .slice(-1);3364 if (liAncestors.length) {3365 endOffset = 1 + getNodeIndex(liAncestors[0]);3366 endNode = liAncestors[0].parentNode;3367 }3368 3369 3370 if (!isBlockEndPoint(endNode, endOffset)) do {3371 3372 3373 if (endOffset == getNodeLength(endNode)) {3374 endOffset = 1 + getNodeIndex(endNode);3375 endNode = endNode.parentNode;3376 3377 } else {3378 endOffset++;3379 }3380 3381 3382 } while (!isBlockBoundaryPoint(endNode, endOffset));3383 3384 3385 3386 while (endOffset == getNodeLength(endNode)3387 && endNode.parentNode) {3388 endOffset = 1 + getNodeIndex(endNode);3389 endNode = endNode.parentNode;3390 }3391 3392 3393 var newRange = startNode.ownerDocument.createRange();3394 newRange.setStart(startNode, startOffset);3395 newRange.setEnd(endNode, endOffset);3396 3397 return newRange;3398}3399function followsLineBreak(node) {3400 3401 var offset = 0;3402 3403 while (!isBlockBoundaryPoint(node, offset)) {3404 3405 3406 if (0 <= offset - 13407 && offset - 1 < node.childNodes.length3408 && isVisible(node.childNodes[offset - 1])) {3409 return false;3410 }3411 3412 3413 if (offset == 03414 || !node.hasChildNodes()) {3415 offset = getNodeIndex(node);3416 node = node.parentNode;3417 3418 3419 } else {3420 node = node.childNodes[offset - 1];3421 offset = getNodeLength(node);3422 }3423 }3424 3425 return true;3426}3427function precedesLineBreak(node) {3428 3429 var offset = getNodeLength(node);3430 3431 while (!isBlockBoundaryPoint(node, offset)) {3432 3433 if (offset < node.childNodes.length3434 && isVisible(node.childNodes[offset])) {3435 return false;3436 }3437 3438 3439 if (offset == getNodeLength(node)3440 || !node.hasChildNodes()) {3441 offset = 1 + getNodeIndex(node);3442 node = node.parentNode;3443 3444 3445 } else {3446 node = node.childNodes[offset];3447 offset = 0;3448 }3449 }3450 3451 return true;3452}3453function recordCurrentOverrides() {3454 3455 3456 var overrides = [];3457 3458 3459 if (getValueOverride("createlink") !== undefined) {3460 overrides.push(["createlink", getValueOverride("createlink")]);3461 }3462 3463 3464 3465 3466 ["bold", "italic", "strikethrough", "subscript", "superscript",3467 "underline"].forEach(function(command) {3468 if (getStateOverride(command) !== undefined) {3469 overrides.push([command, getStateOverride(command)]);3470 }3471 });3472 3473 3474 3475 ["fontname", "fontsize", "forecolor",3476 "hilitecolor"].forEach(function(command) {3477 if (getValueOverride(command) !== undefined) {3478 overrides.push([command, getValueOverride(command)]);3479 }3480 });3481 3482 return overrides;3483}3484function recordCurrentStatesAndValues() {3485 3486 3487 var overrides = [];3488 3489 3490 var node = getAllEffectivelyContainedNodes(getActiveRange())3491 .filter(isFormattableNode)[0];3492 3493 if (!node) {3494 return overrides;3495 }3496 3497 3498 overrides.push(["createlink", getEffectiveCommandValue(node, "createlink")]);3499 3500 3501 3502 3503 3504 ["bold", "italic", "strikethrough", "subscript", "superscript",3505 "underline"].forEach(function(command) {3506 if (commands[command].inlineCommandActivatedValues3507 .indexOf(getEffectiveCommandValue(node, command)) != -1) {3508 overrides.push([command, true]);3509 } else {3510 overrides.push([command, false]);3511 }3512 });3513 3514 3515 ["fontname", "fontsize", "forecolor", "hilitecolor"].forEach(function(command) {3516 overrides.push([command, commands[command].value()]);3517 });3518 3519 3520 overrides.push(["fontsize", getEffectiveCommandValue(node, "fontsize")]);3521 3522 return overrides;3523}3524function restoreStatesAndValues(overrides) {3525 3526 3527 var node = getAllEffectivelyContainedNodes(getActiveRange())3528 .filter(isFormattableNode)[0];3529 3530 3531 if (node) {3532 for (var i = 0; i < overrides.length; i++) {3533 var command = overrides[i][0];3534 var override = overrides[i][1];3535 3536 3537 3538 if (typeof override == "boolean"3539 && myQueryCommandState(command) != override) {3540 commands[command].action("");3541 3542 3543 3544 3545 } else if (typeof override == "string"3546 && command != "createlink"3547 && command != "fontsize"3548 && !areEquivalentValues(command, myQueryCommandValue(command), override)) {3549 commands[command].action(override);3550 3551 3552 3553 3554 3555 3556 } else if (typeof override == "string"3557 && command == "createlink"3558 && (3559 (3560 getValueOverride("createlink") !== undefined3561 && getValueOverride("createlink") !== override3562 ) || (3563 getValueOverride("createlink") === undefined3564 && getEffectiveCommandValue(node, "createlink") !== override3565 )3566 )) {3567 commands.createlink.action(override);3568 3569 3570 3571 3572 3573 } else if (typeof override == "string"3574 && command == "fontsize"3575 && (3576 (3577 getValueOverride("fontsize") !== undefined3578 && getValueOverride("fontsize") !== override3579 ) || (3580 getValueOverride("fontsize") === undefined3581 && !areLooselyEquivalentValues(command, getEffectiveCommandValue(node, "fontsize"), override)3582 )3583 )) {3584 3585 3586 override = getLegacyFontSize(override);3587 3588 3589 commands.fontsize.action(override);3590 3591 } else {3592 continue;3593 }3594 3595 3596 node = getAllEffectivelyContainedNodes(getActiveRange())3597 .filter(isFormattableNode)[0]3598 || node;3599 }3600 3601 } else {3602 for (var i = 0; i < overrides.length; i++) {3603 var command = overrides[i][0];3604 var override = overrides[i][1];3605 3606 3607 if (typeof override == "boolean") {3608 setStateOverride(command, override);3609 }3610 3611 3612 if (typeof override == "string") {3613 setValueOverride(command, override);3614 }3615 }3616 }3617}3618function deleteSelection(flags) {3619 if (flags === undefined) {3620 flags = {};3621 }3622 var blockMerging = "blockMerging" in flags ? Boolean(flags.blockMerging) : true;3623 var stripWrappers = "stripWrappers" in flags ? Boolean(flags.stripWrappers) : true;3624 var direction = "direction" in flags ? flags.direction : "forward";3625 3626 if (!getActiveRange()) {3627 return;3628 }3629 3630 canonicalizeWhitespace(getActiveRange().startContainer, getActiveRange().startOffset);3631 3632 canonicalizeWhitespace(getActiveRange().endContainer, getActiveRange().endOffset);3633 3634 3635 var start = getLastEquivalentPoint(getActiveRange().startContainer, getActiveRange().startOffset);3636 var startNode = start[0];3637 var startOffset = start[1];3638 3639 3640 var end = getFirstEquivalentPoint(getActiveRange().endContainer, getActiveRange().endOffset);3641 var endNode = end[0];3642 var endOffset = end[1];3643 3644 if (getPosition(endNode, endOffset, startNode, startOffset) !== "after") {3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 if (direction == "forward") {3656 if (getSelection().rangeCount) {3657 getSelection().collapseToStart();3658 }3659 getActiveRange().collapse(true);3660 3661 } else {3662 getSelection().collapseToEnd();3663 getActiveRange().collapse(false);3664 }3665 3666 return;3667 }3668 3669 3670 if (startNode.nodeType == Node.TEXT_NODE3671 && startOffset == 0) {3672 startOffset = getNodeIndex(startNode);3673 startNode = startNode.parentNode;3674 }3675 3676 3677 if (endNode.nodeType == Node.TEXT_NODE3678 && endOffset == getNodeLength(endNode)) {3679 endOffset = 1 + getNodeIndex(endNode);3680 endNode = endNode.parentNode;3681 }3682 3683 3684 getSelection().collapse(startNode, startOffset);3685 getActiveRange().setStart(startNode, startOffset);3686 3687 getSelection().extend(endNode, endOffset);3688 getActiveRange().setEnd(endNode, endOffset);3689 3690 var startBlock = getActiveRange().startContainer;3691 3692 3693 while (inSameEditingHost(startBlock, startBlock.parentNode)3694 && isInlineNode(startBlock)) {3695 startBlock = startBlock.parentNode;3696 }3697 3698 3699 3700 if ((!isBlockNode(startBlock) && !isEditingHost(startBlock))3701 || !isAllowedChild("span", startBlock)3702 || isHtmlElement(startBlock, ["td", "th"])) {3703 startBlock = null;3704 }3705 3706 var endBlock = getActiveRange().endContainer;3707 3708 3709 while (inSameEditingHost(endBlock, endBlock.parentNode)3710 && isInlineNode(endBlock)) {3711 endBlock = endBlock.parentNode;3712 }3713 3714 3715 3716 if ((!isBlockNode(endBlock) && !isEditingHost(endBlock))3717 || !isAllowedChild("span", endBlock)3718 || isHtmlElement(endBlock, ["td", "th"])) {3719 endBlock = null;3720 }3721 3722 var overrides = recordCurrentStatesAndValues();3723 3724 3725 if (startNode == endNode3726 && isEditable(startNode)3727 && startNode.nodeType == Node.TEXT_NODE) {3728 3729 3730 startNode.deleteData(startOffset, endOffset - startOffset);3731 3732 3733 canonicalizeWhitespace(startNode, startOffset, false);3734 3735 3736 if (direction == "forward") {3737 if (getSelection().rangeCount) {3738 getSelection().collapseToStart();3739 }3740 getActiveRange().collapse(true);3741 3742 } else {3743 getSelection().collapseToEnd();3744 getActiveRange().collapse(false);3745 }3746 3747 restoreStatesAndValues(overrides);3748 3749 return;3750 }3751 3752 3753 3754 if (isEditable(startNode)3755 && startNode.nodeType == Node.TEXT_NODE) {3756 startNode.deleteData(startOffset, getNodeLength(startNode) - startOffset);3757 }3758 3759 3760 3761 3762 3763 var nodeList = getContainedNodes(getActiveRange(),3764 function(node) {3765 return isEditable(node)3766 && !isHtmlElement(node, ["thead", "tbody", "tfoot", "tr", "th", "td"]);3767 }3768 );3769 3770 for (var i = 0; i < nodeList.length; i++) {3771 var node = nodeList[i];3772 3773 var parent_ = node.parentNode;3774 3775 parent_.removeChild(node);3776 3777 3778 3779 if (![].some.call(getBlockNodeOf(parent_).childNodes, isVisible)3780 && (isEditable(parent_) || isEditingHost(parent_))) {3781 parent_.appendChild(document.createElement("br"));3782 }3783 3784 3785 3786 3787 if (stripWrappers3788 || (!isAncestor(parent_, startNode) && parent_ != startNode)) {3789 while (isEditable(parent_)3790 && isInlineNode(parent_)3791 && getNodeLength(parent_) == 0) {3792 var grandparent = parent_.parentNode;3793 grandparent.removeChild(parent_);3794 parent_ = grandparent;3795 }3796 }3797 }3798 3799 3800 if (isEditable(endNode)3801 && endNode.nodeType == Node.TEXT_NODE) {3802 endNode.deleteData(0, endOffset);3803 }3804 3805 3806 canonicalizeWhitespace(getActiveRange().startContainer, getActiveRange().startOffset, false);3807 3808 3809 canonicalizeWhitespace(getActiveRange().endContainer, getActiveRange().endOffset, false);3810 3811 3812 3813 if (!blockMerging3814 || !startBlock3815 || !endBlock3816 || !inSameEditingHost(startBlock, endBlock)3817 || startBlock == endBlock) {3818 3819 3820 if (direction == "forward") {3821 if (getSelection().rangeCount) {3822 getSelection().collapseToStart();3823 }3824 getActiveRange().collapse(true);3825 3826 } else {3827 if (getSelection().rangeCount) {3828 getSelection().collapseToEnd();3829 }3830 getActiveRange().collapse(false);3831 }3832 3833 restoreStatesAndValues(overrides);3834 3835 return;3836 }3837 3838 3839 if (startBlock.children.length == 13840 && isCollapsedBlockProp(startBlock.firstChild)) {3841 startBlock.removeChild(startBlock.firstChild);3842 }3843 3844 if (isAncestor(startBlock, endBlock)) {3845 3846 var referenceNode = endBlock;3847 3848 3849 while (referenceNode.parentNode != startBlock) {3850 referenceNode = referenceNode.parentNode;3851 }3852 3853 3854 3855 getSelection().collapse(startBlock, getNodeIndex(referenceNode));3856 getActiveRange().setStart(startBlock, getNodeIndex(referenceNode));3857 getActiveRange().collapse(true);3858 3859 if (!endBlock.hasChildNodes()) {3860 3861 3862 3863 3864 while (isEditable(endBlock)3865 && endBlock.parentNode.childNodes.length == 13866 && endBlock.parentNode != startBlock) {3867 var parent_ = endBlock;3868 parent_.removeChild(endBlock);3869 endBlock = parent_;3870 }3871 3872 3873 3874 3875 if (isEditable(endBlock)3876 && !isInlineNode(endBlock)3877 && isInlineNode(endBlock.previousSibling)3878 && isInlineNode(endBlock.nextSibling)) {3879 endBlock.parentNode.insertBefore(document.createElement("br"), endBlock.nextSibling);3880 }3881 3882 if (isEditable(endBlock)) {3883 endBlock.parentNode.removeChild(endBlock);3884 }3885 3886 restoreStatesAndValues(overrides);3887 3888 return;3889 }3890 3891 3892 if (!isInlineNode(endBlock.firstChild)) {3893 restoreStatesAndValues(overrides);3894 return;3895 }3896 3897 var children = [];3898 3899 children.push(endBlock.firstChild);3900 3901 3902 3903 while (!isHtmlElement(children[children.length - 1], "br")3904 && isInlineNode(children[children.length - 1].nextSibling)) {3905 children.push(children[children.length - 1].nextSibling);3906 }3907 3908 var values = recordValues(children);3909 3910 3911 while (children[0].parentNode != startBlock) {3912 splitParent(children);3913 }3914 3915 3916 if (isEditable(children[0].previousSibling)3917 && isHtmlElement(children[0].previousSibling, "br")) {3918 children[0].parentNode.removeChild(children[0].previousSibling);3919 }3920 3921 } else if (isDescendant(startBlock, endBlock)) {3922 3923 3924 getSelection().collapse(startBlock, getNodeLength(startBlock));3925 getActiveRange().setStart(startBlock, getNodeLength(startBlock));3926 getActiveRange().collapse(true);3927 3928 var referenceNode = startBlock;3929 3930 3931 while (referenceNode.parentNode != endBlock) {3932 referenceNode = referenceNode.parentNode;3933 }3934 3935 3936 if (isInlineNode(referenceNode.nextSibling)3937 && isHtmlElement(startBlock.lastChild, "br")) {3938 startBlock.removeChild(startBlock.lastChild);3939 }3940 3941 var nodesToMove = [];3942 3943 3944 if (referenceNode.nextSibling3945 && !isBlockNode(referenceNode.nextSibling)) {3946 nodesToMove.push(referenceNode.nextSibling);3947 }3948 3949 3950 3951 if (nodesToMove.length3952 && !isHtmlElement(nodesToMove[nodesToMove.length - 1], "br")3953 && nodesToMove[nodesToMove.length - 1].nextSibling3954 && !isBlockNode(nodesToMove[nodesToMove.length - 1].nextSibling)) {3955 nodesToMove.push(nodesToMove[nodesToMove.length - 1].nextSibling);3956 }3957 3958 var values = recordValues(nodesToMove);3959 3960 3961 nodesToMove.forEach(function(node) {3962 movePreservingRanges(node, startBlock, -1);3963 });3964 3965 } else {3966 3967 3968 getSelection().collapse(startBlock, getNodeLength(startBlock));3969 getActiveRange().setStart(startBlock, getNodeLength(startBlock));3970 getActiveRange().collapse(true);3971 3972 3973 if (isInlineNode(endBlock.firstChild)3974 && isHtmlElement(startBlock.lastChild, "br")) {3975 startBlock.removeChild(startBlock.lastChild);3976 }3977 3978 3979 var values = recordValues([].slice.call(endBlock.childNodes));3980 3981 3982 while (endBlock.hasChildNodes()) {3983 movePreservingRanges(endBlock.firstChild, startBlock, -1);3984 }3985 3986 3987 3988 while (!endBlock.hasChildNodes()) {3989 var parent_ = endBlock.parentNode;3990 parent_.removeChild(endBlock);3991 endBlock = parent_;3992 }3993 }3994 3995 var ancestor = startBlock;3996 3997 3998 3999 4000 while (getInclusiveAncestors(ancestor).some(function(node) {4001 return inSameEditingHost(ancestor, node)4002 && (4003 (isHtmlElement(node, "ol") && isHtmlElement(node.nextSibling, "ol"))4004 || (isHtmlElement(node, "ul") && isHtmlElement(node.nextSibling, "ul"))4005 ) && inSameEditingHost(ancestor, node.nextSibling);4006 })) {4007 4008 4009 4010 while (!(4011 isHtmlElement(ancestor, "ol")4012 && isHtmlElement(ancestor.nextSibling, "ol")4013 && inSameEditingHost(ancestor, ancestor.nextSibling)4014 ) && !(4015 isHtmlElement(ancestor, "ul")4016 && isHtmlElement(ancestor.nextSibling, "ul")4017 && inSameEditingHost(ancestor, ancestor.nextSibling)4018 )) {4019 ancestor = ancestor.parentNode;4020 }4021 4022 4023 4024 while (ancestor.nextSibling.hasChildNodes()) {4025 movePreservingRanges(ancestor.nextSibling.firstChild, ancestor, -1);4026 }4027 4028 ancestor.parentNode.removeChild(ancestor.nextSibling);4029 }4030 4031 restoreValues(values);4032 4033 4034 if (!startBlock.hasChildNodes()) {4035 startBlock.appendChild(document.createElement("br"));4036 }4037 4038 removeExtraneousLineBreaksAtTheEndOf(startBlock);4039 4040 restoreStatesAndValues(overrides);4041}4042function splitParent(nodeList) {4043 4044 var originalParent = nodeList[0].parentNode;4045 4046 4047 if (!isEditable(originalParent)4048 || !originalParent.parentNode) {4049 return;4050 }4051 4052 4053 if (nodeList.indexOf(originalParent.firstChild) != -1) {4054 removeExtraneousLineBreaksBefore(originalParent);4055 }4056 4057 4058 4059 var followsLineBreak_ = nodeList.indexOf(originalParent.firstChild) != -14060 && followsLineBreak(originalParent);4061 4062 4063 4064 var precedesLineBreak_ = nodeList.indexOf(originalParent.lastChild) != -14065 && precedesLineBreak(originalParent);4066 4067 4068 if (nodeList.indexOf(originalParent.firstChild) == -14069 && nodeList.indexOf(originalParent.lastChild) != -1) {4070 4071 4072 4073 for (var i = nodeList.length - 1; i >= 0; i--) {4074 movePreservingRanges(nodeList[i], originalParent.parentNode, 1 + getNodeIndex(originalParent));4075 }4076 4077 4078 4079 4080 if (precedesLineBreak_4081 && !precedesLineBreak(nodeList[nodeList.length - 1])) {4082 nodeList[nodeList.length - 1].parentNode.insertBefore(document.createElement("br"), nodeList[nodeList.length - 1].nextSibling);4083 }4084 4085 removeExtraneousLineBreaksAtTheEndOf(originalParent);4086 4087 return;4088 }4089 4090 if (nodeList.indexOf(originalParent.firstChild) == -1) {4091 4092 4093 var clonedParent = originalParent.cloneNode(false);4094 4095 originalParent.removeAttribute("id");4096 4097 4098 originalParent.parentNode.insertBefore(clonedParent, originalParent);4099 4100 4101 4102 while (nodeList[0].previousSibling) {4103 movePreservingRanges(originalParent.firstChild, clonedParent, clonedParent.childNodes.length);4104 }4105 }4106 4107 4108 for (var i = 0; i < nodeList.length; i++) {4109 movePreservingRanges(nodeList[i], originalParent.parentNode, getNodeIndex(originalParent));4110 }4111 4112 4113 4114 if (followsLineBreak_4115 && !followsLineBreak(nodeList[0])) {4116 nodeList[0].parentNode.insertBefore(document.createElement("br"), nodeList[0]);4117 }4118 4119 4120 4121 4122 if (isInlineNode(nodeList[nodeList.length - 1])4123 && !isHtmlElement(nodeList[nodeList.length - 1], "br")4124 && isHtmlElement(originalParent.firstChild, "br")4125 && !isInlineNode(originalParent)) {4126 originalParent.removeChild(originalParent.firstChild);4127 }4128 4129 if (!originalParent.hasChildNodes()) {4130 4131 originalParent.parentNode.removeChild(originalParent);4132 4133 4134 4135 4136 if (precedesLineBreak_4137 && !precedesLineBreak(nodeList[nodeList.length - 1])) {4138 nodeList[nodeList.length - 1].parentNode.insertBefore(document.createElement("br"), nodeList[nodeList.length - 1].nextSibling);4139 }4140 4141 } else {4142 removeExtraneousLineBreaksBefore(originalParent);4143 }4144 4145 4146 4147 if (!nodeList[nodeList.length - 1].nextSibling4148 && nodeList[nodeList.length - 1].parentNode) {4149 removeExtraneousLineBreaksAtTheEndOf(nodeList[nodeList.length - 1].parentNode);4150 }4151}4152function removePreservingDescendants(node) {4153 if (node.hasChildNodes()) {4154 splitParent([].slice.call(node.childNodes));4155 } else {4156 node.parentNode.removeChild(node);4157 }4158}4159function canonicalSpaceSequence(n, nonBreakingStart, nonBreakingEnd) {4160 4161 if (n == 0) {4162 return "";4163 }4164 4165 4166 if (n == 1 && !nonBreakingStart && !nonBreakingEnd) {4167 return " ";4168 }4169 4170 if (n == 1) {4171 return "\xa0";4172 }4173 4174 var buffer = "";4175 4176 4177 var repeatedPair;4178 if (nonBreakingStart) {4179 repeatedPair = "\xa0 ";4180 } else {4181 repeatedPair = " \xa0";4182 }4183 4184 4185 while (n > 3) {4186 buffer += repeatedPair;4187 n -= 2;4188 }4189 4190 4191 if (n == 3) {4192 buffer +=4193 !nonBreakingStart && !nonBreakingEnd ? " \xa0 "4194 : nonBreakingStart && !nonBreakingEnd ? "\xa0\xa0 "4195 : !nonBreakingStart && nonBreakingEnd ? " \xa0\xa0"4196 : nonBreakingStart && nonBreakingEnd ? "\xa0 \xa0"4197 : "impossible";4198 4199 4200 } else {4201 buffer +=4202 !nonBreakingStart && !nonBreakingEnd ? "\xa0 "4203 : nonBreakingStart && !nonBreakingEnd ? "\xa0 "4204 : !nonBreakingStart && nonBreakingEnd ? " \xa0"4205 : nonBreakingStart && nonBreakingEnd ? "\xa0\xa0"4206 : "impossible";4207 }4208 4209 return buffer;4210}4211function canonicalizeWhitespace(node, offset, fixCollapsedSpace) {4212 if (fixCollapsedSpace === undefined) {4213 4214 4215 fixCollapsedSpace = true;4216 }4217 4218 if (!isEditable(node) && !isEditingHost(node)) {4219 return;4220 }4221 4222 var startNode = node;4223 var startOffset = offset;4224 4225 while (true) {4226 4227 4228 4229 if (0 <= startOffset - 14230 && inSameEditingHost(startNode, startNode.childNodes[startOffset - 1])) {4231 startNode = startNode.childNodes[startOffset - 1];4232 startOffset = getNodeLength(startNode);4233 4234 4235 4236 4237 } else if (startOffset == 04238 && !followsLineBreak(startNode)4239 && inSameEditingHost(startNode, startNode.parentNode)) {4240 startOffset = getNodeIndex(startNode);4241 startNode = startNode.parentNode;4242 4243 4244 4245 4246 4247 } else if (startNode.nodeType == Node.TEXT_NODE4248 && ["pre", "pre-wrap"].indexOf(getComputedStyle(startNode.parentNode).whiteSpace) == -14249 && startOffset != 04250 && /[ \xa0]/.test(startNode.data[startOffset - 1])) {4251 startOffset--;4252 4253 } else {4254 break;4255 }4256 }4257 4258 var endNode = startNode;4259 var endOffset = startOffset;4260 4261 var length = 0;4262 4263 4264 var collapseSpaces = startOffset == 0 && followsLineBreak(startNode);4265 4266 while (true) {4267 4268 4269 if (endOffset < endNode.childNodes.length4270 && inSameEditingHost(endNode, endNode.childNodes[endOffset])) {4271 endNode = endNode.childNodes[endOffset];4272 endOffset = 0;4273 4274 4275 4276 4277 } else if (endOffset == getNodeLength(endNode)4278 && !precedesLineBreak(endNode)4279 && inSameEditingHost(endNode, endNode.parentNode)) {4280 endOffset = 1 + getNodeIndex(endNode);4281 endNode = endNode.parentNode;4282 4283 4284 4285 4286 } else if (endNode.nodeType == Node.TEXT_NODE4287 && ["pre", "pre-wrap"].indexOf(getComputedStyle(endNode.parentNode).whiteSpace) == -14288 && endOffset != getNodeLength(endNode)4289 && /[ \xa0]/.test(endNode.data[endOffset])) {4290 4291 4292 4293 4294 if (fixCollapsedSpace4295 && collapseSpaces4296 && " " == endNode.data[endOffset]) {4297 endNode.deleteData(endOffset, 1);4298 continue;4299 }4300 4301 4302 collapseSpaces = " " == endNode.data[endOffset];4303 4304 endOffset++;4305 4306 length++;4307 4308 } else {4309 break;4310 }4311 }4312 4313 4314 if (fixCollapsedSpace) {4315 while (getPosition(startNode, startOffset, endNode, endOffset) == "before") {4316 4317 4318 4319 if (0 <= endOffset - 14320 && endOffset - 1 < endNode.childNodes.length4321 && inSameEditingHost(endNode, endNode.childNodes[endOffset - 1])) {4322 endNode = endNode.childNodes[endOffset - 1];4323 endOffset = getNodeLength(endNode);4324 4325 4326 4327 } else if (endOffset == 04328 && inSameEditingHost(endNode, endNode.parentNode)) {4329 endOffset = getNodeIndex(endNode);4330 endNode = endNode.parentNode;4331 4332 4333 4334 4335 } else if (endNode.nodeType == Node.TEXT_NODE4336 && ["pre", "pre-wrap"].indexOf(getComputedStyle(endNode.parentNode).whiteSpace) == -14337 && endOffset == getNodeLength(endNode)4338 && endNode.data[endNode.data.length - 1] == " "4339 && precedesLineBreak(endNode)) {4340 4341 endOffset--;4342 4343 length--;4344 4345 endNode.deleteData(endOffset, 1);4346 4347 } else {4348 break;4349 }4350 }4351 }4352 4353 4354 4355 4356 4357 var replacementWhitespace = canonicalSpaceSequence(length,4358 startOffset == 0 && followsLineBreak(startNode),4359 endOffset == getNodeLength(endNode) && precedesLineBreak(endNode));4360 4361 while (getPosition(startNode, startOffset, endNode, endOffset) == "before") {4362 4363 4364 if (startOffset < startNode.childNodes.length) {4365 startNode = startNode.childNodes[startOffset];4366 startOffset = 0;4367 4368 4369 4370 } else if (startNode.nodeType != Node.TEXT_NODE4371 || startOffset == getNodeLength(startNode)) {4372 startOffset = 1 + getNodeIndex(startNode);4373 startNode = startNode.parentNode;4374 4375 } else {4376 4377 4378 var element = replacementWhitespace[0];4379 replacementWhitespace = replacementWhitespace.slice(1);4380 4381 4382 if (element != startNode.data[startOffset]) {4383 4384 startNode.insertData(startOffset, element);4385 4386 startNode.deleteData(startOffset + 1, 1);4387 }4388 4389 startOffset++;4390 }4391 }4392}4393function indentNodes(nodeList) {4394 4395 if (!nodeList.length) {4396 return;4397 }4398 4399 var firstNode = nodeList[0];4400 4401 if (isHtmlElement(firstNode.parentNode, ["OL", "UL"])) {4402 4403 var tag = firstNode.parentNode.tagName;4404 4405 4406 4407 4408 wrap(nodeList,4409 function(node) { return isHtmlElement(node, tag) },4410 function() { return firstNode.ownerDocument.createElement(tag) });4411 4412 return;4413 }4414 4415 4416 4417 4418 var newParent = wrap(nodeList,4419 function(node) { return isSimpleIndentationElement(node) },4420 function() { return firstNode.ownerDocument.createElement("blockquote") });4421 4422 fixDisallowedAncestors(newParent);4423}4424function outdentNode(node) {4425 4426 if (!isEditable(node)) {4427 return;4428 }4429 4430 4431 if (isSimpleIndentationElement(node)) {4432 removePreservingDescendants(node);4433 return;4434 }4435 4436 if (isIndentationElement(node)) {4437 4438 node.removeAttribute("dir");4439 4440 node.style.margin = "";4441 node.style.padding = "";4442 node.style.border = "";4443 if (node.getAttribute("style") == ""4444 4445 || node.getAttribute("style") == "border-width: initial; border-color: initial; ") {4446 node.removeAttribute("style");4447 }4448 4449 setTagName(node, "div");4450 4451 return;4452 }4453 4454 var currentAncestor = node.parentNode;4455 4456 var ancestorList = [];4457 4458 4459 4460 while (isEditable(currentAncestor)4461 && currentAncestor.nodeType == Node.ELEMENT_NODE4462 && !isSimpleIndentationElement(currentAncestor)4463 && !isHtmlElement(currentAncestor, ["ol", "ul"])) {4464 ancestorList.push(currentAncestor);4465 currentAncestor = currentAncestor.parentNode;4466 }4467 4468 if (!isEditable(currentAncestor)4469 || !isSimpleIndentationElement(currentAncestor)) {4470 4471 currentAncestor = node.parentNode;4472 4473 ancestorList = [];4474 4475 4476 4477 while (isEditable(currentAncestor)4478 && currentAncestor.nodeType == Node.ELEMENT_NODE4479 && !isIndentationElement(currentAncestor)4480 && !isHtmlElement(currentAncestor, ["ol", "ul"])) {4481 ancestorList.push(currentAncestor);4482 currentAncestor = currentAncestor.parentNode;4483 }4484 }4485 4486 4487 if (isHtmlElement(node, ["OL", "UL"])4488 && (!isEditable(currentAncestor)4489 || !isIndentationElement(currentAncestor))) {4490 4491 4492 node.removeAttribute("reversed");4493 node.removeAttribute("start");4494 node.removeAttribute("type");4495 4496 var children = [].slice.call(node.childNodes);4497 4498 4499 if (node.attributes.length4500 && !isHtmlElement(node.parentNode, ["OL", "UL"])) {4501 setTagName(node, "div");4502 4503 } else {4504 4505 4506 var values = recordValues([].slice.call(node.childNodes));4507 4508 removePreservingDescendants(node);4509 4510 restoreValues(values);4511 }4512 4513 for (var i = 0; i < children.length; i++) {4514 fixDisallowedAncestors(children[i]);4515 }4516 4517 return;4518 }4519 4520 4521 if (!isEditable(currentAncestor)4522 || !isIndentationElement(currentAncestor)) {4523 return;4524 }4525 4526 ancestorList.push(currentAncestor);4527 4528 var originalAncestor = currentAncestor;4529 4530 while (ancestorList.length) {4531 4532 4533 4534 currentAncestor = ancestorList.pop();4535 4536 4537 var target = node.parentNode == currentAncestor4538 ? node4539 : ancestorList[ancestorList.length - 1];4540 4541 4542 if (isInlineNode(target)4543 && !isHtmlElement(target, "BR")4544 && isHtmlElement(target.nextSibling, "BR")) {4545 target.parentNode.removeChild(target.nextSibling);4546 }4547 4548 4549 var precedingSiblings = [].slice.call(currentAncestor.childNodes, 0, getNodeIndex(target));4550 var followingSiblings = [].slice.call(currentAncestor.childNodes, 1 + getNodeIndex(target));4551 4552 indentNodes(precedingSiblings);4553 4554 indentNodes(followingSiblings);4555 }4556 4557 outdentNode(originalAncestor);4558}4559function toggleLists(tagName) {4560 4561 4562 var mode = getSelectionListState() == tagName ? "disable" : "enable";4563 var range = getActiveRange();4564 tagName = tagName.toUpperCase();4565 4566 4567 var otherTagName = tagName == "OL" ? "UL" : "OL";4568 4569 4570 4571 4572 4573 var items = [];4574 (function(){4575 for (4576 var ancestorContainer = range.endContainer;4577 ancestorContainer != range.commonAncestorContainer;4578 ancestorContainer = ancestorContainer.parentNode4579 ) {4580 if (isHtmlElement(ancestorContainer, "li")) {4581 items.unshift(ancestorContainer);4582 }4583 }4584 for (4585 var ancestorContainer = range.startContainer;4586 ancestorContainer;4587 ancestorContainer = ancestorContainer.parentNode4588 ) {4589 if (isHtmlElement(ancestorContainer, "li")) {4590 items.unshift(ancestorContainer);4591 }4592 }4593 })();4594 4595 items.forEach(normalizeSublists);4596 4597 var newRange = blockExtend(range);4598 4599 4600 4601 if (mode == "enable") {4602 getAllContainedNodes(newRange, function(node) {4603 return isEditable(node)4604 && isHtmlElement(node, otherTagName);4605 }).forEach(function(list) {4606 4607 4608 if ((isEditable(list.previousSibling) && isHtmlElement(list.previousSibling, tagName))4609 || (isEditable(list.nextSibling) && isHtmlElement(list.nextSibling, tagName))) {4610 4611 var children = [].slice.call(list.childNodes);4612 4613 4614 var values = recordValues(children);4615 4616 splitParent(children);4617 4618 4619 wrap(children, function(node) { return isHtmlElement(node, tagName) });4620 4621 restoreValues(values);4622 4623 } else {4624 setTagName(list, tagName);4625 }4626 });4627 }4628 4629 4630 4631 4632 4633 4634 4635 var nodeList = getContainedNodes(newRange, function(node) {4636 return isEditable(node)4637 && !isIndentationElement(node)4638 && (isHtmlElement(node, ["OL", "UL"])4639 || isHtmlElement(node.parentNode, ["OL", "UL"])4640 || isAllowedChild(node, "li"));4641 });4642 4643 4644 if (mode == "enable") {4645 nodeList = nodeList.filter(function(node) {4646 return !isHtmlElement(node, ["ol", "ul"])4647 || isHtmlElement(node.parentNode, ["ol", "ul"]);4648 });4649 }4650 4651 if (mode == "disable") {4652 while (nodeList.length) {4653 4654 var sublist = [];4655 4656 4657 sublist.push(nodeList.shift());4658 4659 4660 4661 if (isHtmlElement(sublist[0], tagName)) {4662 outdentNode(sublist[0]);4663 continue;4664 }4665 4666 4667 4668 4669 while (nodeList.length4670 && nodeList[0] == sublist[sublist.length - 1].nextSibling4671 && !isHtmlElement(nodeList[0], tagName)) {4672 sublist.push(nodeList.shift());4673 }4674 4675 var values = recordValues(sublist);4676 4677 splitParent(sublist);4678 4679 for (var i = 0; i < sublist.length; i++) {4680 fixDisallowedAncestors(sublist[i]);4681 }4682 4683 restoreValues(values);4684 }4685 4686 } else {4687 while (nodeList.length) {4688 4689 var sublist = [];4690 4691 4692 while (!sublist.length4693 || (nodeList.length4694 && nodeList[0] == sublist[sublist.length - 1].nextSibling)) {4695 4696 4697 4698 if (isHtmlElement(nodeList[0], ["p", "div"])) {4699 sublist.push(setTagName(nodeList[0], "li"));4700 nodeList.shift();4701 4702 4703 } else if (isHtmlElement(nodeList[0], ["li", "ol", "ul"])) {4704 sublist.push(nodeList.shift());4705 4706 } else {4707 4708 var nodesToWrap = [];4709 4710 4711 4712 4713 4714 4715 while (!nodesToWrap.length4716 || (nodeList.length4717 && nodeList[0] == nodesToWrap[nodesToWrap.length - 1].nextSibling4718 && isInlineNode(nodeList[0])4719 && isInlineNode(nodesToWrap[nodesToWrap.length - 1])4720 && !isHtmlElement(nodesToWrap[nodesToWrap.length - 1], "br"))) {4721 nodesToWrap.push(nodeList.shift());4722 }4723 4724 4725 4726 sublist.push(wrap(nodesToWrap,4727 undefined,4728 function() { return document.createElement("li") }));4729 }4730 }4731 4732 4733 4734 if (isHtmlElement(sublist[0].parentNode, tagName)4735 || sublist.every(function(node) { return isHtmlElement(node, ["ol", "ul"]) })) {4736 continue;4737 }4738 4739 4740 if (isHtmlElement(sublist[0].parentNode, otherTagName)) {4741 4742 4743 var values = recordValues(sublist);4744 4745 splitParent(sublist);4746 4747 4748 4749 4750 wrap(sublist,4751 function(node) { return isHtmlElement(node, tagName) },4752 function() { return document.createElement(tagName) });4753 4754 restoreValues(values);4755 4756 continue;4757 }4758 4759 4760 4761 4762 4763 fixDisallowedAncestors(wrap(sublist,4764 function(node) { return isHtmlElement(node, tagName) },4765 function() {4766 4767 4768 4769 4770 4771 if (!isEditable(sublist[0].parentNode)4772 || !isSimpleIndentationElement(sublist[0].parentNode)4773 || !isEditable(sublist[0].parentNode.previousSibling)4774 || !isHtmlElement(sublist[0].parentNode.previousSibling, tagName)) {4775 return document.createElement(tagName);4776 }4777 4778 4779 var list = sublist[0].parentNode.previousSibling;4780 4781 normalizeSublists(list.lastChild);4782 4783 4784 4785 4786 if (!isEditable(list.lastChild)4787 || !isHtmlElement(list.lastChild, tagName)) {4788 list.appendChild(document.createElement(tagName));4789 }4790 4791 return list.lastChild;4792 }4793 ));4794 }4795 }4796}4797function justifySelection(alignment) {4798 4799 var newRange = blockExtend(globalRange);4800 4801 4802 4803 4804 var elementList = getAllContainedNodes(newRange, function(node) {4805 return node.nodeType == Node.ELEMENT_NODE4806 && isEditable(node)4807 4808 && (4809 node.hasAttribute("align")4810 || node.style.textAlign != ""4811 || isHtmlElement(node, "center")4812 );4813 });4814 4815 for (var i = 0; i < elementList.length; i++) {4816 var element = elementList[i];4817 4818 4819 element.removeAttribute("align");4820 4821 4822 element.style.textAlign = "";4823 if (element.getAttribute("style") == "") {4824 element.removeAttribute("style");4825 }4826 4827 4828 if (isHtmlElement(element, ["div", "span", "center"])4829 && !element.attributes.length) {4830 removePreservingDescendants(element);4831 }4832 4833 4834 if (isHtmlElement(element, "center")4835 && element.attributes.length) {4836 setTagName(element, "div");4837 }4838 }4839 4840 newRange = blockExtend(globalRange);4841 4842 var nodeList = [];4843 4844 4845 4846 4847 nodeList = getContainedNodes(newRange, function(node) {4848 return isEditable(node)4849 && isAllowedChild(node, "div")4850 && getAlignmentValue(node) != alignment;4851 });4852 4853 while (nodeList.length) {4854 4855 var sublist = [];4856 4857 sublist.push(nodeList.shift());4858 4859 4860 4861 while (nodeList.length4862 && nodeList[0] == sublist[sublist.length - 1].nextSibling) {4863 sublist.push(nodeList.shift());4864 }4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 wrap(sublist,4879 function(node) {4880 return isHtmlElement(node, "div")4881 && [].every.call(node.attributes, function(attr) {4882 return (attr.name == "align" && attr.value.toLowerCase() == alignment)4883 || (attr.name == "style" && node.style.length == 1 && node.style.textAlign == alignment);4884 });4885 },4886 function() {4887 var newParent = document.createElement("div");4888 newParent.setAttribute("style", "text-align: " + alignment);4889 return newParent;4890 }4891 );4892 }4893}4894var autolinkableUrlRegexp =4895 4896 4897 4898 4899 4900 "([a-zA-Z][a-zA-Z0-9+.-]*://|mailto:)"4901 4902 + "[^ \t\n\f\r]*"4903 4904 + "[^!\"'(),\\-.:;<>[\\]`{}]";4905var validEmailRegexp =4906 "[a-zA-Z0-9!#$%&'*+\\-/=?^_`{|}~.]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*";4907function autolink(node, endOffset) {4908 4909 4910 while (getPreviousEquivalentPoint(node, endOffset)) {4911 var prev = getPreviousEquivalentPoint(node, endOffset);4912 node = prev[0];4913 endOffset = prev[1];4914 }4915 4916 4917 if (node.nodeType != Node.TEXT_NODE4918 || getAncestors(node).some(function(ancestor) { return isHtmlElement(ancestor, "a") })) {4919 return;4920 }4921 4922 4923 var search = /[^ \t\n\f\r]*$/.exec(node.substringData(0, endOffset))[0];4924 4925 if (new RegExp(autolinkableUrlRegexp).test(search)) {4926 4927 4928 while (!(new RegExp(autolinkableUrlRegexp + "$").test(node.substringData(0, endOffset)))) {4929 endOffset--;4930 }4931 4932 4933 var startOffset = new RegExp(autolinkableUrlRegexp + "$").exec(node.substringData(0, endOffset)).index;4934 4935 4936 var href = node.substringData(startOffset, endOffset - startOffset);4937 4938 } else if (new RegExp(validEmailRegexp).test(search)) {4939 4940 4941 while (!(new RegExp(validEmailRegexp + "$").test(node.substringData(0, endOffset)))) {4942 endOffset--;4943 }4944 4945 4946 var startOffset = new RegExp(validEmailRegexp + "$").exec(node.substringData(0, endOffset)).index;4947 4948 4949 var href = "mailto:" + node.substringData(startOffset, endOffset - startOffset);4950 4951 } else {4952 return;4953 }4954 4955 var originalRange = getActiveRange();4956 4957 4958 var newRange = document.createRange();4959 newRange.setStart(node, startOffset);4960 newRange.setEnd(node, endOffset);4961 getSelection().removeAllRanges();4962 getSelection().addRange(newRange);4963 globalRange = newRange;4964 4965 commands.createlink.action(href);4966 4967 getSelection().removeAllRanges();4968 getSelection().addRange(originalRange);4969 globalRange = originalRange;4970}4971commands["delete"] = {4972 preservesOverrides: true,4973 action: function() {4974 4975 4976 if (!getActiveRange().collapsed) {4977 deleteSelection();4978 return true;4979 }4980 4981 canonicalizeWhitespace(getActiveRange().startContainer, getActiveRange().startOffset);4982 4983 var node = getActiveRange().startContainer;4984 var offset = getActiveRange().startOffset;4985 4986 while (true) {4987 4988 4989 if (offset == 04990 && isEditable(node.previousSibling)4991 && isInvisible(node.previousSibling)) {4992 node.parentNode.removeChild(node.previousSibling);4993 4994 4995 4996 } else if (0 <= offset - 14997 && offset - 1 < node.childNodes.length4998 && isEditable(node.childNodes[offset - 1])4999 && isInvisible(node.childNodes[offset - 1])) {5000 node.removeChild(node.childNodes[offset - 1]);5001 offset--;5002 5003 5004 5005 } else if ((offset == 05006 && isInlineNode(node))5007 || isInvisible(node)) {5008 offset = getNodeIndex(node);5009 node = node.parentNode;5010 5011 5012 5013 } else if (0 <= offset - 15014 && offset - 1 < node.childNodes.length5015 && isEditable(node.childNodes[offset - 1])5016 && isHtmlElement(node.childNodes[offset - 1], "a")) {5017 removePreservingDescendants(node.childNodes[offset - 1]);5018 return true;5019 5020 5021 5022 } else if (0 <= offset - 15023 && offset - 1 < node.childNodes.length5024 && !isBlockNode(node.childNodes[offset - 1])5025 && !isHtmlElement(node.childNodes[offset - 1], ["br", "img"])) {5026 node = node.childNodes[offset - 1];5027 offset = getNodeLength(node);5028 5029 } else {5030 break;5031 }5032 }5033 5034 5035 5036 if ((node.nodeType == Node.TEXT_NODE5037 && offset != 0)5038 || (isBlockNode(node)5039 && 0 <= offset - 15040 && offset - 1 < node.childNodes.length5041 && isHtmlElement(node.childNodes[offset - 1], ["br", "hr", "img"]))) {5042 5043 getSelection().collapse(node, offset);5044 getActiveRange().setEnd(node, offset);5045 5046 5047 getSelection().extend(node, offset - 1);5048 getActiveRange().setStart(node, offset - 1);5049 5050 deleteSelection();5051 5052 return true;5053 }5054 5055 if (isInlineNode(node)) {5056 return true;5057 }5058 5059 5060 if (isHtmlElement(node, ["li", "dt", "dd"])5061 && node == node.parentNode.firstChild5062 && offset == 0) {5063 5064 5065 5066 var items = [];5067 for (var ancestor = node.parentNode; ancestor; ancestor = ancestor.parentNode) {5068 if (isHtmlElement(ancestor, "li")) {5069 items.unshift(ancestor);5070 }5071 }5072 5073 for (var i = 0; i < items.length; i++) {5074 normalizeSublists(items[i]);5075 }5076 5077 5078 var values = recordValues([node]);5079 5080 splitParent([node]);5081 5082 restoreValues(values);5083 5084 5085 5086 5087 if (isHtmlElement(node, ["dd", "dt"])5088 && getAncestors(node).every(function(ancestor) {5089 return !inSameEditingHost(node, ancestor)5090 || !isAllowedChild(node, ancestor)5091 })) {5092 node = setTagName(node, defaultSingleLineContainerName);5093 }5094 5095 fixDisallowedAncestors(node);5096 5097 return true;5098 }5099 5100 var startNode = node;5101 var startOffset = offset;5102 5103 while (true) {5104 5105 5106 if (startOffset == 0) {5107 startOffset = getNodeIndex(startNode);5108 startNode = startNode.parentNode;5109 5110 5111 5112 } else if (0 <= startOffset - 15113 && startOffset - 1 < startNode.childNodes.length5114 && isEditable(startNode.childNodes[startOffset - 1])5115 && isInvisible(startNode.childNodes[startOffset - 1])) {5116 startNode.removeChild(startNode.childNodes[startOffset - 1]);5117 startOffset--;5118 5119 } else {5120 break;5121 }5122 }5123 5124 5125 if (offset == 05126 && getAncestors(node).concat(node).filter(function(ancestor) {5127 return isEditable(ancestor)5128 && inSameEditingHost(ancestor, node)5129 && isIndentationElement(ancestor);5130 }).length) {5131 5132 5133 var newRange = document.createRange();5134 newRange.setStart(node, 0);5135 newRange = blockExtend(newRange);5136 5137 5138 5139 5140 5141 5142 var nodeList = getContainedNodes(newRange, function(currentNode) {5143 return isEditable(currentNode)5144 && !hasEditableDescendants(currentNode);5145 });5146 5147 for (var i = 0; i < nodeList.length; i++) {5148 outdentNode(nodeList[i]);5149 }5150 5151 return true;5152 }5153 5154 5155 if (isHtmlElement(startNode.childNodes[startOffset], "table")) {5156 return true;5157 }5158 5159 5160 if (0 <= startOffset - 15161 && startOffset - 1 < startNode.childNodes.length5162 && isHtmlElement(startNode.childNodes[startOffset - 1], "table")) {5163 5164 5165 getSelection().collapse(startNode, startOffset - 1);5166 getActiveRange().setStart(startNode, startOffset - 1);5167 5168 5169 getSelection().extend(startNode, startOffset);5170 getActiveRange().setEnd(startNode, startOffset);5171 5172 return true;5173 }5174 5175 5176 5177 if (offset == 05178 && (isHtmlElement(startNode.childNodes[startOffset - 1], "hr")5179 || (5180 isHtmlElement(startNode.childNodes[startOffset - 1], "br")5181 && (5182 isHtmlElement(startNode.childNodes[startOffset - 1].previousSibling, "br")5183 || !isInlineNode(startNode.childNodes[startOffset - 1].previousSibling)5184 )5185 )5186 )) {5187 5188 5189 getSelection().collapse(startNode, startOffset - 1);5190 getActiveRange().setStart(startNode, startOffset - 1);5191 5192 5193 getSelection().extend(startNode, startOffset);5194 getActiveRange().setEnd(startNode, startOffset);5195 5196 deleteSelection();5197 5198 getSelection().collapse(node, offset);5199 getActiveRange().setStart(node, offset);5200 getActiveRange().collapse(true);5201 5202 return true;5203 }5204 5205 5206 5207 if (isHtmlElement(startNode.childNodes[startOffset], ["li", "dt", "dd"])5208 && isInlineNode(startNode.childNodes[startOffset].firstChild)5209 && startOffset != 0) {5210 5211 5212 var previousItem = startNode.childNodes[startOffset - 1];5213 5214 5215 5216 if (isInlineNode(previousItem.lastChild)5217 && !isHtmlElement(previousItem.lastChild, "br")) {5218 previousItem.appendChild(document.createElement("br"));5219 }5220 5221 5222 5223 if (isInlineNode(previousItem.lastChild)) {5224 previousItem.appendChild(document.createElement("br"));5225 }5226 }5227 5228 5229 if (isHtmlElement(startNode.childNodes[startOffset], ["li", "dt", "dd"])5230 && isHtmlElement(startNode.childNodes[startOffset].previousSibling, ["li", "dt", "dd"])) {5231 5232 5233 5234 5235 5236 var originalRange = getActiveRange().cloneRange();5237 extraRanges.push(originalRange);5238 5239 startNode = startNode.childNodes[startOffset - 1];5240 5241 startOffset = getNodeLength(startNode);5242 5243 node = startNode.nextSibling;5244 5245 5246 getSelection().collapse(startNode, startOffset);5247 getActiveRange().setStart(startNode, startOffset);5248 5249 getSelection().extend(node, 0);5250 getActiveRange().setEnd(node, 0);5251 5252 deleteSelection();5253 5254 getSelection().removeAllRanges();5255 5256 5257 getSelection().addRange(originalRange);5258 getActiveRange().setStart(originalRange.startContainer, originalRange.startOffset);5259 getActiveRange().setEnd(originalRange.endContainer, originalRange.endOffset);5260 5261 extraRanges.pop();5262 return true;5263 }5264 5265 while (0 <= startOffset - 15266 && startOffset - 1 < startNode.childNodes.length) {5267 5268 5269 5270 if (isEditable(startNode.childNodes[startOffset - 1])5271 && isInvisible(startNode.childNodes[startOffset - 1])) {5272 startNode.removeChild(startNode.childNodes[startOffset - 1]);5273 startOffset--;5274 5275 5276 } else {5277 startNode = startNode.childNodes[startOffset - 1];5278 startOffset = getNodeLength(startNode);5279 }5280 }5281 5282 5283 getSelection().collapse(startNode, startOffset);5284 getActiveRange().setStart(startNode, startOffset);5285 5286 getSelection().extend(node, offset);5287 getActiveRange().setEnd(node, offset);5288 5289 deleteSelection({direction: "backward"});5290 5291 return true;5292 }5293};5294var formattableBlockNames = ["address", "dd", "div", "dt", "h1", "h2", "h3",5295 "h4", "h5", "h6", "p", "pre"];5296commands.formatblock = {5297 preservesOverrides: true,5298 action: function(value) {5299 5300 5301 if (/^<.*>$/.test(value)) {5302 value = value.slice(1, -1);5303 }5304 5305 value = value.toLowerCase();5306 5307 if (formattableBlockNames.indexOf(value) == -1) {5308 return false;5309 }5310 5311 var newRange = blockExtend(getActiveRange());5312 5313 5314 5315 5316 5317 5318 5319 var nodeList = getContainedNodes(newRange, function(node) {5320 return isEditable(node)5321 && (isNonListSingleLineContainer(node)5322 || isAllowedChild(node, "p")5323 || isHtmlElement(node, ["dd", "dt"]))5324 && !getDescendants(node).some(isProhibitedParagraphChild);5325 });5326 5327 var values = recordValues(nodeList);5328 5329 5330 5331 5332 5333 for (var i = 0; i < nodeList.length; i++) {5334 var node = nodeList[i];5335 while (getAncestors(node).some(function(ancestor) {5336 return isEditable(ancestor)5337 && inSameEditingHost(ancestor, node)5338 && isHtmlElement(ancestor, formattableBlockNames)5339 && !getDescendants(ancestor).some(isProhibitedParagraphChild);5340 })) {5341 splitParent([node]);5342 }5343 }5344 5345 restoreValues(values);5346 5347 while (nodeList.length) {5348 var sublist;5349 5350 5351 if (isSingleLineContainer(nodeList[0])) {5352 5353 5354 sublist = [].slice.call(nodeList[0].childNodes);5355 5356 5357 var values = recordValues(sublist);5358 5359 5360 removePreservingDescendants(nodeList[0]);5361 5362 restoreValues(values);5363 5364 nodeList.shift();5365 5366 } else {5367 5368 sublist = [];5369 5370 5371 sublist.push(nodeList.shift());5372 5373 5374 5375 5376 5377 5378 while (nodeList.length5379 && nodeList[0] == sublist[sublist.length - 1].nextSibling5380 && !isSingleLineContainer(nodeList[0])5381 && !isHtmlElement(sublist[sublist.length - 1], "BR")) {5382 sublist.push(nodeList.shift());5383 }5384 }5385 5386 5387 5388 5389 5390 5391 fixDisallowedAncestors(wrap(sublist,5392 ["div", "p"].indexOf(value) == - 15393 ? function(node) { return isHtmlElement(node, value) && !node.attributes.length }5394 : function() { return false },5395 function() { return document.createElement(value) }));5396 }5397 5398 return true;5399 }, indeterm: function() {5400 5401 if (!getActiveRange()) {5402 return false;5403 }5404 5405 var newRange = blockExtend(getActiveRange());5406 5407 5408 var nodeList = getAllContainedNodes(newRange, function(node) {5409 return isVisible(node)5410 && isEditable(node)5411 && !node.hasChildNodes();5412 });5413 5414 if (!nodeList.length) {5415 return false;5416 }5417 5418 var type = null;5419 5420 for (var i = 0; i < nodeList.length; i++) {5421 var node = nodeList[i];5422 5423 5424 5425 while (isEditable(node.parentNode)5426 && inSameEditingHost(node, node.parentNode)5427 && !isHtmlElement(node, formattableBlockNames)) {5428 node = node.parentNode;5429 }5430 5431 var currentType = "";5432 5433 5434 5435 5436 if (isEditable(node)5437 && isHtmlElement(node, formattableBlockNames)5438 && !getDescendants(node).some(isProhibitedParagraphChild)) {5439 currentType = node.tagName;5440 }5441 5442 if (type === null) {5443 type = currentType;5444 5445 } else if (type != currentType) {5446 return true;5447 }5448 }5449 5450 return false;5451 }, value: function() {5452 5453 if (!getActiveRange()) {5454 return "";5455 }5456 5457 var newRange = blockExtend(getActiveRange());5458 5459 5460 5461 var nodes = getAllContainedNodes(newRange, function(node) {5462 return isVisible(node)5463 && isEditable(node)5464 && !node.hasChildNodes();5465 });5466 if (!nodes.length) {5467 return "";5468 }5469 var node = nodes[0];5470 5471 5472 5473 while (isEditable(node.parentNode)5474 && inSameEditingHost(node, node.parentNode)5475 && !isHtmlElement(node, formattableBlockNames)) {5476 node = node.parentNode;5477 }5478 5479 5480 5481 5482 if (isEditable(node)5483 && isHtmlElement(node, formattableBlockNames)5484 && !getDescendants(node).some(isProhibitedParagraphChild)) {5485 return node.tagName.toLowerCase();5486 }5487 5488 return "";5489 }5490};5491commands.forwarddelete = {5492 preservesOverrides: true,5493 action: function() {5494 5495 5496 if (!getActiveRange().collapsed) {5497 deleteSelection();5498 return true;5499 }5500 5501 canonicalizeWhitespace(getActiveRange().startContainer, getActiveRange().startOffset);5502 5503 var node = getActiveRange().startContainer;5504 var offset = getActiveRange().startOffset;5505 5506 while (true) {5507 5508 5509 5510 if (offset == getNodeLength(node)5511 && isEditable(node.nextSibling)5512 && isInvisible(node.nextSibling)) {5513 node.parentNode.removeChild(node.nextSibling);5514 5515 5516 } else if (offset < node.childNodes.length5517 && isEditable(node.childNodes[offset])5518 && isInvisible(node.childNodes[offset])) {5519 node.removeChild(node.childNodes[offset]);5520 5521 5522 5523 } else if ((offset == getNodeLength(node)5524 && isInlineNode(node))5525 || isInvisible(node)) {5526 offset = 1 + getNodeIndex(node);5527 node = node.parentNode;5528 5529 5530 5531 } else if (offset < node.childNodes.length5532 && !isBlockNode(node.childNodes[offset])5533 && !isHtmlElement(node.childNodes[offset], ["br", "img"])5534 && !isCollapsedBlockProp(node.childNodes[offset])) {5535 node = node.childNodes[offset];5536 offset = 0;5537 5538 } else {5539 break;5540 }5541 }5542 5543 if (node.nodeType == Node.TEXT_NODE5544 && offset != getNodeLength(node)) {5545 5546 var endOffset = offset + 1;5547 5548 5549 5550 5551 5552 5553 5554 5555 while (endOffset != node.length5556 && /^[\u0300-\u036f\u0591-\u05bd\u05c1\u05c2]$/.test(node.data[endOffset])) {5557 endOffset++;5558 }5559 5560 getSelection().collapse(node, offset);5561 getActiveRange().setStart(node, offset);5562 5563 5564 getSelection().extend(node, endOffset);5565 getActiveRange().setEnd(node, endOffset);5566 5567 deleteSelection();5568 5569 return true;5570 }5571 5572 if (isInlineNode(node)) {5573 return true;5574 }5575 5576 5577 if (offset < node.childNodes.length5578 && isHtmlElement(node.childNodes[offset], ["br", "hr", "img"])5579 && !isCollapsedBlockProp(node.childNodes[offset])) {5580 5581 getSelection().collapse(node, offset);5582 getActiveRange().setStart(node, offset);5583 5584 5585 getSelection().extend(node, offset + 1);5586 getActiveRange().setEnd(node, offset + 1);5587 5588 deleteSelection();5589 5590 return true;5591 }5592 5593 var endNode = node;5594 var endOffset = offset;5595 5596 5597 if (endOffset < endNode.childNodes.length5598 && isCollapsedBlockProp(endNode.childNodes[endOffset])) {5599 endOffset++;5600 }5601 5602 while (true) {5603 5604 5605 if (endOffset == getNodeLength(endNode)) {5606 endOffset = 1 + getNodeIndex(endNode);5607 endNode = endNode.parentNode;5608 5609 5610 } else if (endOffset < endNode.childNodes.length5611 && isEditable(endNode.childNodes[endOffset])5612 && isInvisible(endNode.childNodes[endOffset])) {5613 endNode.removeChild(endNode.childNodes[endOffset]);5614 5615 } else {5616 break;5617 }5618 }5619 5620 5621 if (isHtmlElement(endNode.childNodes[endOffset - 1], "table")) {5622 return true;5623 }5624 5625 if (isHtmlElement(endNode.childNodes[endOffset], "table")) {5626 5627 5628 getSelection().collapse(endNode, endOffset);5629 getActiveRange().setStart(endNode, endOffset);5630 5631 5632 getSelection().extend(endNode, endOffset + 1);5633 getActiveRange().setEnd(endNode, endOffset + 1);5634 5635 return true;5636 }5637 5638 5639 if (offset == getNodeLength(node)5640 && isHtmlElement(endNode.childNodes[endOffset], ["br", "hr"])) {5641 5642 5643 getSelection().collapse(endNode, endOffset);5644 getActiveRange().setStart(endNode, endOffset);5645 5646 5647 getSelection().extend(endNode, endOffset + 1);5648 getActiveRange().setEnd(endNode, endOffset + 1);5649 5650 deleteSelection();5651 5652 getSelection().collapse(node, offset);5653 getActiveRange().setStart(node, offset);5654 getActiveRange().collapse(true);5655 5656 return true;5657 }5658 5659 while (endOffset < endNode.childNodes.length) {5660 5661 5662 if (isEditable(endNode.childNodes[endOffset])5663 && isInvisible(endNode.childNodes[endOffset])) {5664 endNode.removeChild(endNode.childNodes[endOffset]);5665 5666 5667 } else {5668 endNode = endNode.childNodes[endOffset];5669 endOffset = 0;5670 }5671 }5672 5673 getSelection().collapse(node, offset);5674 getActiveRange().setStart(node, offset);5675 5676 5677 getSelection().extend(endNode, endOffset);5678 getActiveRange().setEnd(endNode, endOffset);5679 5680 deleteSelection();5681 5682 return true;5683 }5684};5685commands.indent = {5686 preservesOverrides: true,5687 action: function() {5688 5689 5690 5691 5692 var items = [];5693 for (var node = getActiveRange().endContainer; node != getActiveRange().commonAncestorContainer; node = node.parentNode) {5694 if (isHtmlElement(node, "LI")) {5695 items.unshift(node);5696 }5697 }5698 for (var node = getActiveRange().startContainer; node != getActiveRange().commonAncestorContainer; node = node.parentNode) {5699 if (isHtmlElement(node, "LI")) {5700 items.unshift(node);5701 }5702 }5703 for (var node = getActiveRange().commonAncestorContainer; node; node = node.parentNode) {5704 if (isHtmlElement(node, "LI")) {5705 items.unshift(node);5706 }5707 }5708 5709 for (var i = 0; i < items.length; i++) {5710 normalizeSublists(items[i]);5711 }5712 5713 var newRange = blockExtend(getActiveRange());5714 5715 var nodeList = [];5716 5717 5718 5719 nodeList = getContainedNodes(newRange, function(node) {5720 return isEditable(node)5721 && (isAllowedChild(node, "div")5722 || isAllowedChild(node, "ol"));5723 });5724 5725 5726 if (isHtmlElement(nodeList.filter(isVisible)[0], "li")5727 && isHtmlElement(nodeList.filter(isVisible)[0].parentNode, ["ol", "ul"])) {5728 5729 5730 var sibling = nodeList.filter(isVisible)[0].previousSibling;5731 5732 5733 while (isInvisible(sibling)) {5734 sibling = sibling.previousSibling;5735 }5736 5737 if (isHtmlElement(sibling, "li")) {5738 normalizeSublists(sibling);5739 }5740 }5741 5742 while (nodeList.length) {5743 5744 var sublist = [];5745 5746 sublist.push(nodeList.shift());5747 5748 5749 5750 while (nodeList.length5751 && nodeList[0] == sublist[sublist.length - 1].nextSibling) {5752 sublist.push(nodeList.shift());5753 }5754 5755 indentNodes(sublist);5756 }5757 5758 return true;5759 }5760};5761commands.inserthorizontalrule = {5762 preservesOverrides: true,5763 action: function() {5764 5765 5766 var startNode = getActiveRange().startContainer;5767 var startOffset = getActiveRange().startOffset;5768 var endNode = getActiveRange().endContainer;5769 var endOffset = getActiveRange().endOffset;5770 5771 5772 5773 while (startOffset == 05774 && startNode.parentNode) {5775 startOffset = getNodeIndex(startNode);5776 startNode = startNode.parentNode;5777 }5778 5779 5780 5781 while (endOffset == getNodeLength(endNode)5782 && endNode.parentNode) {5783 endOffset = 1 + getNodeIndex(endNode);5784 endNode = endNode.parentNode;5785 }5786 5787 5788 getSelection().collapse(startNode, startOffset);5789 getActiveRange().setStart(startNode, startOffset);5790 5791 5792 getSelection().extend(endNode, endOffset);5793 getActiveRange().setEnd(endNode, endOffset);5794 5795 deleteSelection({blockMerging: false});5796 5797 5798 if (!isEditable(getActiveRange().startContainer)5799 && !isEditingHost(getActiveRange().startContainer)) {5800 return true;5801 }5802 5803 5804 5805 5806 if (getActiveRange().startContainer.nodeType == Node.TEXT_NODE5807 && getActiveRange().startOffset == 0) {5808 var newNode = getActiveRange().startContainer.parentNode;5809 var newOffset = getNodeIndex(getActiveRange().startContainer);5810 getSelection().collapse(newNode, newOffset);5811 getActiveRange().setStart(newNode, newOffset);5812 getActiveRange().collapse(true);5813 }5814 5815 5816 5817 5818 5819 if (getActiveRange().startContainer.nodeType == Node.TEXT_NODE5820 && getActiveRange().startOffset == getNodeLength(getActiveRange().startContainer)) {5821 var newNode = getActiveRange().startContainer.parentNode;5822 var newOffset = 1 + getNodeIndex(getActiveRange().startContainer);5823 getSelection().collapse(newNode, newOffset);5824 getActiveRange().setStart(newNode, newOffset);5825 getActiveRange().collapse(true);5826 }5827 5828 5829 var hr = document.createElement("hr");5830 5831 getActiveRange().insertNode(hr);5832 5833 fixDisallowedAncestors(hr);5834 5835 5836 5837 getSelection().collapse(hr.parentNode, 1 + getNodeIndex(hr));5838 getActiveRange().setStart(hr.parentNode, 1 + getNodeIndex(hr));5839 getActiveRange().collapse(true);5840 5841 return true;5842 }5843};5844commands.inserthtml = {5845 preservesOverrides: true,5846 action: function(value) {5847 5848 deleteSelection();5849 5850 5851 if (!isEditable(getActiveRange().startContainer)5852 && !isEditingHost(getActiveRange().startContainer)) {5853 return true;5854 }5855 5856 5857 var frag = getActiveRange().createContextualFragment(value);5858 5859 var lastChild = frag.lastChild;5860 5861 if (!lastChild) {5862 return true;5863 }5864 5865 var descendants = getDescendants(frag);5866 5867 if (isBlockNode(getActiveRange().startContainer)) {5868 5869 5870 5871 5872 5873 5874 [].filter.call(getActiveRange().startContainer.childNodes, function(node) {5875 return isEditable(node)5876 && isCollapsedBlockProp(node)5877 && getNodeIndex(node) >= getActiveRange().startOffset;5878 }).forEach(function(node) {5879 node.parentNode.removeChild(node);5880 });5881 }5882 5883 getActiveRange().insertNode(frag);5884 5885 5886 5887 if (isBlockNode(getActiveRange().startContainer)5888 && ![].some.call(getActiveRange().startContainer.childNodes, isVisible)) {5889 getActiveRange().startContainer.appendChild(document.createElement("br"));5890 }5891 5892 5893 5894 getActiveRange().setStart(lastChild.parentNode, 1 + getNodeIndex(lastChild));5895 getActiveRange().setEnd(lastChild.parentNode, 1 + getNodeIndex(lastChild));5896 5897 for (var i = 0; i < descendants.length; i++) {5898 fixDisallowedAncestors(descendants[i]);5899 }5900 5901 return true;5902 }5903};5904commands.insertimage = {5905 preservesOverrides: true,5906 action: function(value) {5907 5908 if (value === "") {5909 return false;5910 }5911 5912 deleteSelection({stripWrappers: false});5913 5914 var range = getActiveRange();5915 5916 5917 if (!isEditable(getActiveRange().startContainer)5918 && !isEditingHost(getActiveRange().startContainer)) {5919 return true;5920 }5921 5922 5923 if (isBlockNode(range.startContainer)5924 && range.startContainer.childNodes.length == 15925 && isHtmlElement(range.startContainer.firstChild, "br")5926 && range.startOffset == 0) {5927 range.startContainer.removeChild(range.startContainer.firstChild);5928 }5929 5930 5931 var img = document.createElement("img");5932 5933 img.setAttribute("src", value);5934 5935 range.insertNode(img);5936 5937 5938 5939 5940 5941 5942 5943 range.setStart(img.parentNode, 1 + getNodeIndex(img));5944 range.setEnd(img.parentNode, 1 + getNodeIndex(img));5945 getSelection().removeAllRanges();5946 getSelection().addRange(range);5947 5948 5949 img.removeAttribute("width");5950 img.removeAttribute("height");5951 5952 return true;5953 }5954};5955commands.insertlinebreak = {5956 preservesOverrides: true,5957 action: function(value) {5958 5959 deleteSelection({stripWrappers: false});5960 5961 5962 if (!isEditable(getActiveRange().startContainer)5963 && !isEditingHost(getActiveRange().startContainer)) {5964 return true;5965 }5966 5967 5968 if (getActiveRange().startContainer.nodeType == Node.ELEMENT_NODE5969 && !isAllowedChild("br", getActiveRange().startContainer)) {5970 return true;5971 }5972 5973 5974 5975 if (getActiveRange().startContainer.nodeType != Node.ELEMENT_NODE5976 && !isAllowedChild("br", getActiveRange().startContainer.parentNode)) {5977 return true;5978 }5979 5980 5981 5982 5983 if (getActiveRange().startContainer.nodeType == Node.TEXT_NODE5984 && getActiveRange().startOffset == 0) {5985 var newNode = getActiveRange().startContainer.parentNode;5986 var newOffset = getNodeIndex(getActiveRange().startContainer);5987 getSelection().collapse(newNode, newOffset);5988 getActiveRange().setStart(newNode, newOffset);5989 getActiveRange().setEnd(newNode, newOffset);5990 }5991 5992 5993 5994 5995 5996 if (getActiveRange().startContainer.nodeType == Node.TEXT_NODE5997 && getActiveRange().startOffset == getNodeLength(getActiveRange().startContainer)) {5998 var newNode = getActiveRange().startContainer.parentNode;5999 var newOffset = 1 + getNodeIndex(getActiveRange().startContainer);6000 getSelection().collapse(newNode, newOffset);6001 getActiveRange().setStart(newNode, newOffset);6002 getActiveRange().setEnd(newNode, newOffset);6003 }6004 6005 6006 var br = document.createElement("br");6007 6008 getActiveRange().insertNode(br);6009 6010 6011 6012 getSelection().collapse(br.parentNode, 1 + getNodeIndex(br));6013 getActiveRange().setStart(br.parentNode, 1 + getNodeIndex(br));6014 getActiveRange().setEnd(br.parentNode, 1 + getNodeIndex(br));6015 6016 6017 6018 if (isCollapsedLineBreak(br)) {6019 getActiveRange().insertNode(document.createElement("br"));6020 6021 getSelection().collapse(br.parentNode, 1 + getNodeIndex(br));6022 getActiveRange().setStart(br.parentNode, 1 + getNodeIndex(br));6023 getActiveRange().setEnd(br.parentNode, 1 + getNodeIndex(br));6024 }6025 6026 return true;6027 }6028};6029commands.insertorderedlist = {6030 preservesOverrides: true,6031 6032 action: function() { toggleLists("ol"); return true },6033 6034 6035 indeterm: function() { return /^mixed( ol)?$/.test(getSelectionListState()) },6036 6037 state: function() { return getSelectionListState() == "ol" },6038};6039commands.insertparagraph = {6040 preservesOverrides: true,6041 action: function() {6042 6043 deleteSelection();6044 6045 6046 if (!isEditable(getActiveRange().startContainer)6047 && !isEditingHost(getActiveRange().startContainer)) {6048 return true;6049 }6050 6051 var node = getActiveRange().startContainer;6052 var offset = getActiveRange().startOffset;6053 6054 6055 if (node.nodeType == Node.TEXT_NODE6056 && offset != 06057 && offset != getNodeLength(node)) {6058 node.splitText(offset);6059 }6060 6061 6062 if (node.nodeType == Node.TEXT_NODE6063 && offset == getNodeLength(node)) {6064 offset = 1 + getNodeIndex(node);6065 node = node.parentNode;6066 }6067 6068 6069 if (node.nodeType == Node.TEXT_NODE6070 || node.nodeType == Node.COMMENT_NODE) {6071 offset = getNodeIndex(node);6072 node = node.parentNode;6073 }6074 6075 getSelection().collapse(node, offset);6076 getActiveRange().setStart(node, offset);6077 getActiveRange().setEnd(node, offset);6078 6079 var container = node;6080 6081 6082 6083 while (!isSingleLineContainer(container)6084 && isEditable(container.parentNode)6085 && inSameEditingHost(node, container.parentNode)) {6086 container = container.parentNode;6087 }6088 6089 6090 if (isEditable(container)6091 && isSingleLineContainer(container)6092 && inSameEditingHost(node, container.parentNode)6093 && (container.tagName == "P" || container.tagName == "DIV")) {6094 6095 var outerContainer = container;6096 6097 6098 6099 while (!isHtmlElement(outerContainer, ["dd", "dt", "li"])6100 && isEditable(outerContainer.parentNode)) {6101 outerContainer = outerContainer.parentNode;6102 }6103 6104 6105 if (isHtmlElement(outerContainer, ["dd", "dt", "li"])) {6106 container = outerContainer;6107 }6108 }6109 6110 6111 if (!isEditable(container)6112 || !inSameEditingHost(container, node)6113 || !isSingleLineContainer(container)) {6114 6115 var tag = defaultSingleLineContainerName;6116 6117 6118 var newRange = blockExtend(getActiveRange());6119 6120 6121 6122 6123 var nodeList = getContainedNodes(newRange, function(node) { return isAllowedChild(node, "p") })6124 .slice(0, 1);6125 6126 if (!nodeList.length) {6127 6128 6129 if (!isAllowedChild(tag, getActiveRange().startContainer)) {6130 return true;6131 }6132 6133 6134 container = document.createElement(tag);6135 6136 getActiveRange().insertNode(container);6137 6138 6139 container.appendChild(document.createElement("br"));6140 6141 6142 getSelection().collapse(container, 0);6143 getActiveRange().setStart(container, 0);6144 getActiveRange().setEnd(container, 0);6145 6146 return true;6147 }6148 6149 6150 while (nodeList[nodeList.length - 1].nextSibling6151 && isAllowedChild(nodeList[nodeList.length - 1].nextSibling, "p")) {6152 nodeList.push(nodeList[nodeList.length - 1].nextSibling);6153 }6154 6155 6156 6157 6158 container = wrap(nodeList,6159 function() { return false },6160 function() { return document.createElement(tag) }6161 );6162 }6163 6164 if (container.tagName == "ADDRESS"6165 || container.tagName == "LISTING"6166 || container.tagName == "PRE") {6167 6168 6169 var br = document.createElement("br");6170 6171 getActiveRange().insertNode(br);6172 6173 6174 getSelection().collapse(node, offset + 1);6175 getActiveRange().setStart(node, offset + 1);6176 getActiveRange().setEnd(node, offset + 1);6177 6178 6179 6180 6181 6182 6183 if (!isDescendant(nextNode(br), container)) {6184 getActiveRange().insertNode(document.createElement("br"));6185 getSelection().collapse(node, offset + 1);6186 getActiveRange().setEnd(node, offset + 1);6187 }6188 6189 return true;6190 }6191 6192 6193 if (["LI", "DT", "DD"].indexOf(container.tagName) != -16194 && (!container.hasChildNodes()6195 || (container.childNodes.length == 16196 && isHtmlElement(container.firstChild, "br")))) {6197 6198 splitParent([container]);6199 6200 6201 6202 if (!container.hasChildNodes()) {6203 container.appendChild(document.createElement("br"));6204 }6205 6206 6207 6208 6209 if (isHtmlElement(container, ["dd", "dt"])6210 && getAncestors(container).every(function(ancestor) {6211 return !inSameEditingHost(container, ancestor)6212 || !isAllowedChild(container, ancestor)6213 })) {6214 container = setTagName(container, defaultSingleLineContainerName);6215 }6216 6217 fixDisallowedAncestors(container);6218 6219 return true;6220 }6221 6222 6223 6224 var newLineRange = document.createRange();6225 newLineRange.setStart(getActiveRange().startContainer, getActiveRange().startOffset);6226 newLineRange.setEnd(container, getNodeLength(container));6227 6228 6229 6230 while (newLineRange.startOffset == 06231 && !isProhibitedParagraphChild(newLineRange.startContainer)) {6232 newLineRange.setStart(newLineRange.startContainer.parentNode, getNodeIndex(newLineRange.startContainer));6233 }6234 6235 6236 6237 while (newLineRange.startOffset == getNodeLength(newLineRange.startContainer)6238 && !isProhibitedParagraphChild(newLineRange.startContainer)) {6239 newLineRange.setStart(newLineRange.startContainer.parentNode, 1 + getNodeIndex(newLineRange.startContainer));6240 }6241 6242 6243 var containedInNewLineRange = getContainedNodes(newLineRange);6244 var endOfLine = !containedInNewLineRange.length6245 || (containedInNewLineRange.length == 16246 && isHtmlElement(containedInNewLineRange[0], "br"));6247 6248 6249 6250 var newContainerName;6251 if (/^H[1-6]$/.test(container.tagName)6252 && endOfLine) {6253 newContainerName = defaultSingleLineContainerName;6254 6255 6256 } else if (container.tagName == "DT"6257 && endOfLine) {6258 newContainerName = "dd";6259 6260 6261 } else if (container.tagName == "DD"6262 && endOfLine) {6263 newContainerName = "dt";6264 6265 } else {6266 newContainerName = container.tagName.toLowerCase();6267 }6268 6269 6270 var newContainer = document.createElement(newContainerName);6271 6272 for (var i = 0; i < container.attributes.length; i++) {6273 newContainer.setAttributeNS(container.attributes[i].namespaceURI, container.attributes[i].name, container.attributes[i].value);6274 }6275 6276 newContainer.removeAttribute("id");6277 6278 6279 container.parentNode.insertBefore(newContainer, container.nextSibling);6280 6281 var containedNodes = getAllContainedNodes(newLineRange);6282 6283 6284 var frag = newLineRange.extractContents();6285 6286 6287 var descendants = getDescendants(frag);6288 for (var i = 0; i < descendants.length; i++) {6289 if (descendants[i].nodeType == Node.ELEMENT_NODE6290 && containedNodes.indexOf(descendants[i]) == -1) {6291 descendants[i].removeAttribute("id");6292 }6293 }6294 6295 newContainer.appendChild(frag);6296 6297 6298 while (isProhibitedParagraphChild(container.lastChild)) {6299 container = container.lastChild;6300 }6301 6302 6303 while (isProhibitedParagraphChild(newContainer.lastChild)) {6304 newContainer = newContainer.lastChild;6305 }6306 6307 6308 6309 if (![].some.call(container.childNodes, isVisible)) {6310 container.appendChild(document.createElement("br"));6311 }6312 6313 6314 6315 if (![].some.call(newContainer.childNodes, isVisible)) {6316 newContainer.appendChild(document.createElement("br"));6317 }6318 6319 getSelection().collapse(newContainer, 0);6320 getActiveRange().setStart(newContainer, 0);6321 getActiveRange().setEnd(newContainer, 0);6322 6323 return true;6324 }6325};6326commands.inserttext = {6327 action: function(value) {6328 6329 deleteSelection({stripWrappers: false});6330 6331 6332 if (!isEditable(getActiveRange().startContainer)6333 && !isEditingHost(getActiveRange().startContainer)) {6334 return true;6335 }6336 6337 if (value.length > 1) {6338 6339 6340 for (var i = 0; i < value.length; i++) {6341 commands.inserttext.action(value[i]);6342 }6343 6344 return true;6345 }6346 6347 if (value == "") {6348 return true;6349 }6350 6351 6352 if (value == "\n") {6353 commands.insertparagraph.action();6354 return true;6355 }6356 6357 var node = getActiveRange().startContainer;6358 var offset = getActiveRange().startOffset;6359 6360 6361 6362 if (0 <= offset - 16363 && offset - 1 < node.childNodes.length6364 && node.childNodes[offset - 1].nodeType == Node.TEXT_NODE) {6365 node = node.childNodes[offset - 1];6366 offset = getNodeLength(node);6367 }6368 6369 6370 if (0 <= offset6371 && offset < node.childNodes.length6372 && node.childNodes[offset].nodeType == Node.TEXT_NODE) {6373 node = node.childNodes[offset];6374 offset = 0;6375 }6376 6377 var overrides = recordCurrentOverrides();6378 6379 getSelection().collapse(node, offset);6380 getActiveRange().setStart(node, offset);6381 getActiveRange().setEnd(node, offset);6382 6383 canonicalizeWhitespace(node, offset);6384 6385 node = getActiveRange().startContainer;6386 offset = getActiveRange().startOffset;6387 6388 if (node.nodeType == Node.TEXT_NODE) {6389 6390 node.insertData(offset, value);6391 6392 getSelection().collapse(node, offset);6393 getActiveRange().setStart(node, offset);6394 6395 6396 6397 6398 6399 try { getSelection().extend(node, offset + 1); } catch(e) {}6400 getActiveRange().setEnd(node, offset + 1);6401 6402 } else {6403 6404 6405 6406 6407 6408 if (node.childNodes.length == 16409 && isCollapsedLineBreak(node.firstChild)) {6410 node.removeChild(node.firstChild);6411 }6412 6413 6414 var text = document.createTextNode(value);6415 6416 getActiveRange().insertNode(text);6417 6418 getSelection().collapse(text, 0);6419 getActiveRange().setStart(text, 0);6420 6421 getSelection().extend(text, 1);6422 getActiveRange().setEnd(text, 1);6423 }6424 6425 restoreStatesAndValues(overrides);6426 6427 6428 canonicalizeWhitespace(getActiveRange().startContainer, getActiveRange().startOffset, false);6429 6430 6431 canonicalizeWhitespace(getActiveRange().endContainer, getActiveRange().endOffset, false);6432 6433 if (/^[ \t\n\f\r]$/.test(value)) {6434 autolink(getActiveRange().startContainer, getActiveRange().startOffset);6435 }6436 6437 6438 6439 6440 try { getSelection().collapseToEnd(); } catch(e) {}6441 getActiveRange().collapse(false);6442 6443 return true;6444 }6445};6446commands.insertunorderedlist = {6447 preservesOverrides: true,6448 6449 action: function() { toggleLists("ul"); return true },6450 6451 6452 indeterm: function() { return /^mixed( ul)?$/.test(getSelectionListState()) },6453 6454 state: function() { return getSelectionListState() == "ul" },6455};6456commands.justifycenter = {6457 preservesOverrides: true,6458 6459 action: function() { justifySelection("center"); return true },6460 indeterm: function() {6461 6462 6463 6464 6465 6466 if (!getActiveRange()) {6467 return false;6468 }...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1var wptoolkit = require('wptoolkit');2var selectionListState = wptoolkit.getSelectionListState();3console.log(selectionListState);4var wptoolkit = require('wptoolkit');5var selectionListState = wptoolkit.getSelectionListState();6console.log(selectionListState);7var wptoolkit = require('wptoolkit');8var selectionListState = wptoolkit.getSelectionListState();9console.log(selectionListState);10var wptoolkit = require('wptoolkit');11var selectionListState = wptoolkit.getSelectionListState();12console.log(selectionListState);13var wptoolkit = require('wptoolkit');14var selectionListState = wptoolkit.getSelectionListState();15console.log(selectionListState);16var wptoolkit = require('wptoolkit');17var selectionListState = wptoolkit.getSelectionListState();18console.log(selectionListState);19var wptoolkit = require('wptoolkit');20var selectionListState = wptoolkit.getSelectionListState();21console.log(selectionListState);22var wptoolkit = require('wptoolkit');23var selectionListState = wptoolkit.getSelectionListState();24console.log(selectionListState);25var wptoolkit = require('wptoolkit');26var selectionListState = wptoolkit.getSelectionListState();27console.log(selectionListState);28var wptoolkit = require('wptoolkit');29var selectionListState = wptoolkit.getSelectionListState();30console.log(selectionListState);31var wptoolkit = require('wptoolkit');

Full Screen

Using AI Code Generation

copy

Full Screen

1var wptoolbar = window.external.wptoolbar;2var selectionListState = wptoolbar.getSelectionListState();3var selectionListStateValue = selectionListState.value;4var selectionListStateEnabled = selectionListState.enabled;5var selectionListStateChecked = selectionListState.checked;6var wptoolbar = window.external.wptoolbar;7var selectionListState = wptoolbar.getSelectionListState();8var selectionListStateValue = selectionListState.value;9var selectionListStateEnabled = selectionListState.enabled;10var selectionListStateChecked = selectionListState.checked;11var wptoolbar = window.external.wptoolbar;12var selectionListState = wptoolbar.getSelectionListState();13var selectionListStateValue = selectionListState.value;14var selectionListStateEnabled = selectionListState.enabled;15var selectionListStateChecked = selectionListState.checked;16var wptoolbar = window.external.wptoolbar;17var selectionListState = wptoolbar.getSelectionListState();18var selectionListStateValue = selectionListState.value;19var selectionListStateEnabled = selectionListState.enabled;20var selectionListStateChecked = selectionListState.checked;21var wptoolbar = window.external.wptoolbar;22var selectionListState = wptoolbar.getSelectionListState();23var selectionListStateValue = selectionListState.value;24var selectionListStateEnabled = selectionListState.enabled;25var selectionListStateChecked = selectionListState.checked;26var wptoolbar = window.external.wptoolbar;27var selectionListState = wptoolbar.getSelectionListState();28var selectionListStateValue = selectionListState.value;29var selectionListStateEnabled = selectionListState.enabled;30var selectionListStateChecked = selectionListState.checked;31var wptoolbar = window.external.wptoolbar;32var selectionListState = wptoolbar.getSelectionListState();33var selectionListStateValue = selectionListState.value;

Full Screen

Using AI Code Generation

copy

Full Screen

1var wptextbox = require("wptextbox");2var wpTextbox1 = wptextbox.wpTextbox1;3wpTextbox1.getSelectionListState();4define(function() {5 var wpTextbox1 = {6 getSelectionListState: function() {7 console.log("getSelectionListState");8 }9 };10 return {11 };12});13var requirejs = require('requirejs');14requirejs.config({15 paths: {16 }17});18requirejs(["wptextbox"], function(wptextbox) {19 describe("getSelectionListState", function() {20 it("should return the state of selection in list", function() {21 });22 });23});24Your name to display (optional):25Your name to display (optional):26Your name to display (optional):

Full Screen

Using AI Code Generation

copy

Full Screen

1var wptk = new wptoolkit();2var sel = wptk.getSelectionListState('list1');3alert(sel);4function wptoolkit() {5 this.getSelectionListState = function (id) {6 var sel = document.getElementById(id);7 var selIndex = sel.selectedIndex;8 var selValue = sel.options[selIndex].value;9 return selValue;10 }11}

Full Screen

Using AI Code Generation

copy

Full Screen

1var selectionListState = wptoolbar.getSelectionListState();2var selectionList = selectionListState.selectionList;3var selectionListLength = selectionList.length;4var selectionListState = wpcomtoolbar.getSelectionListState();5var selectionList = selectionListState.selectionList;6var selectionListLength = selectionList.length;7var selectionListState = wpcomtoolbar.getSelectionListState();8var selectionList = selectionListState.selectionList;9var selectionListLength = selectionList.length;10var selectionListState = wptoolbar.getSelectionListState();11var selectionList = selectionListState.selectionList;12var selectionListLength = selectionList.length;13var selectionListState = wpcomtoolbar.getSelectionListState();14var selectionList = selectionListState.selectionList;15var selectionListLength = selectionList.length;16var selectionListState = wpcomtoolbar.getSelectionListState();17var selectionList = selectionListState.selectionList;18var selectionListLength = selectionList.length;19var selectionListState = wptoolbar.getSelectionListState();20var selectionList = selectionListState.selectionList;21var selectionListLength = selectionList.length;22var selectionListState = wpcomtoolbar.getSelectionListState();23var selectionList = selectionListState.selectionList;24var selectionListLength = selectionList.length;25var selectionListState = wpcomtoolbar.getSelectionListState();26var selectionList = selectionListState.selectionList;27var selectionListLength = selectionList.length;28var selectionListState = wptoolbar.getSelectionListState();29var selectionList = selectionListState.selectionList;30var selectionListLength = selectionList.length;31var selectionListState = wpcomtoolbar.getSelectionListState();32var selectionList = selectionListState.selectionList;33var selectionListLength = selectionList.length;

Full Screen

Using AI Code Generation

copy

Full Screen

1function testGetSelectionListState()2{ 3 var list = document.getElementById("myList");4 var state = wptoolbar.getSelectionListState(list);5 alert("state = " + state);6}7<body onload="testGetSelectionListState();">8function testGetSelectionListState()9{ 10 var list = document.getElementById("myList");11 var state = wptoolbar.getSelectionListState(list);12 alert("state = " + state);13}14<body onload="testGetSelectionListState();">15function testGetSelectionListState()16{ 17 var list = document.getElementById("myList");18 var state = wptoolbar.getSelectionListState(list);19 alert("state = " + state);20}

Full Screen

Using AI Code Generation

copy

Full Screen

1var tableState = new wptbTableState();2var selectionListState = tableState.getSelectionListState();3var tableState = new wptbTableState();4var selectionListState = tableState.getSelectionListState();5var tableState = new wptbTableState();6var selectionListState = tableState.getSelectionListState();7var tableState = new wptbTableState();8var selectionListState = tableState.getSelectionListState();9var tableState = new wptbTableState();10var selectionListState = tableState.getSelectionListState();

Full Screen

Using AI Code Generation

copy

Full Screen

1var toolkit = require('wptoolkit');2var tk = new toolkit();3var toolkit = require('wptoolkit');4var tk = new toolkit();5var toolkit = require('wptoolkit');6var tk = new toolkit();7var toolkit = require('wptoolkit');8var tk = new toolkit();9var toolkit = require('wptoolkit');10var tk = new toolkit();11var toolkit = require('wptoolkit');12var tk = new toolkit();13var toolkit = require('wptoolkit');14var tk = new toolkit();15var toolkit = require('wptoolkit');16var tk = new toolkit();17var toolkit = require('wptoolkit');18var tk = new toolkit();

Full Screen

Using AI Code Generation

copy

Full Screen

1var items = ["item1", "item2", "item3", "item4", "item5"];2var selectionList = new wptoolkit.selectionList.SelectionList("single", items);3var state = selectionList.getSelectionListState();4console.log(state);5selectionList.setSelectedItem("item3");6var state = selectionList.getSelectionListState();7console.log(state);8selectionList.setSelectedIndex(1);9var state = selectionList.getSelectionListState();10console.log(state);11selectionList.setSelectedItems(["item2", "item3"]);12var state = selectionList.getSelectionListState();13console.log(state);14selectionList.setSelectedIndices([0, 1]);15var state = selectionList.getSelectionListState();

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run wpt automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful