Changeset 1397
- Timestamp:
- 02/11/18 03:30:39 (12 months ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/XinhaCore.js
r1396 r1397 4805 4805 --this._undoPos; 4806 4806 } 4807 // use the fasted method (getInnerHTML); 4807 4808 var snapshotData = { 4809 'txt' : null, 4810 'caretInBody': false 4811 }; 4812 4813 4814 // Caret preservation, when you hit undo, ideally put the caret back where it was. 4815 // To accomplish this we figure out what NODE (Text Node typically) and offset into 4816 // it had the caret save this into the parent element of that node if it has one 4817 // as an attribute, and attach a classname to be able to find it again 4818 // then in the actual undo function we look for that class name after dropping 4819 // the HTML back into place, and reverse the process 4820 4821 // Note that IE <11 does not support this, FF <3.6 won't either, 4822 // nor Safari <5.1, Chrome I dont' know it does currently I don't know how long ago 4823 // Opera same, don't know how long but it works currently 4824 // If a browser doesn't work with it, it won't cause a problem, it just won't 4825 // preserve the caret. 4826 4827 try 4828 { 4829 // Insert a marker so we know where we are 4830 var sel = this.getSelection(); 4831 var caretNode = sel.focusNode; 4832 if(caretNode) 4833 { 4834 var caretOffset = sel.focusOffset 4835 var rng = this.createRange(sel); 4836 var caretParent = this.getParentElement(sel); 4837 4838 var caretRestorationData = false; 4839 4840 switch(caretNode.nodeType) 4841 { 4842 case 3: // TEXT 4843 case 4: // CDATA 4844 case 8: // COMMENT 4845 // We need to record which child node the focus node is of the parent 4846 // default to the end 4847 var whichChild = caretParent.childNodes.length-1; 4848 for(var i = 0; i < caretParent.childNodes.length; i++) 4849 { 4850 if(caretParent.childNodes[i] == caretNode) 4851 { 4852 whichChild = i; 4853 } 4854 } 4855 caretRestorationData = { 4856 caretChild: whichChild, 4857 caretOffset: caretOffset 4858 }; 4859 break; 4860 4861 case 1: 4862 // Ehhhm, not sure. This would be the case if the selection is an image, table whatever 4863 // 4864 // For example <p><img></p> with img selected you get caretNode = img, caretParent = p, 4865 // caretOffset = 0 (0th child of the p) 4866 // 4867 // I was going to make this handled so it would select the image again, but actually 4868 // I think it works just fine without this, it feels natural-enough anyway. 4869 break; 4870 } 4871 4872 if(caretRestorationData) 4873 { 4874 if(caretParent == this._doc.body) 4875 { 4876 // Body is tricky because it won't be included in the snapshot or restoration 4877 // unless fullPage mode is being used, since there is only one body then we 4878 // can record it in the snapshot data and we know where to put it in undo 4879 snapshotData.caretInBody = true; 4880 snapshotData.caretRestorationData = JSON.stringify(caretRestorationData); 4881 } 4882 else 4883 { 4884 // For other elements encode the caret data in the element itself 4885 // so we can be sure we are looking at the right one when we find it 4886 Xinha.addClass(caretParent, 'xinha-undo-caret'); 4887 caretParent.setAttribute('xinha-undo-caret', JSON.stringify(caretRestorationData)); 4888 } 4889 } 4890 4891 // Debug helper 4892 //console.log({n: caretNode, p: caretOffset, r: rng, e: caretParent}); 4893 } 4894 } 4895 catch(e) 4896 { 4897 // Browser doesn't support something. I'm not going to try and support 4898 // very old browsers for this caret preservation feature 4899 4900 // Old IE doesn't support 4901 if(Xinha.is_gecko || Xinha.is_webkit) 4902 { 4903 Xinha.debugMsg('Caret preservation code for undo snapshot failed. If your browser is modern, developers need to check it out in XinhaCore.js (search for caret preservation).','warn'); 4904 } 4905 } 4906 4907 // use the faster method (getInnerHTML); 4808 4908 var take = true; 4809 var txt = this.getInnerHTML(); 4909 snapshotData.txt = this.getInnerHTML(); 4910 4911 // Find all carets we might have added (in theory, 0 or 1, but always a possibility of more) 4912 var existingCarets = Xinha.getElementsByClassName(this._doc.body, 'xinha-undo-caret'); 4913 4914 // Remove them all 4915 for(var i = 0; i < existingCarets.length; i++) 4916 { 4917 Xinha.removeClass(existingCarets[i], 'xinha-undo-caret'); 4918 existingCarets[i].removeAttribute('xinha-undo-caret'); 4919 } 4920 4810 4921 if ( this._undoPos > 0 ) 4811 4922 { 4812 take = (this._undoQueue[this._undoPos - 1] !=txt);4923 take = (this._undoQueue[this._undoPos - 1].txt != snapshotData.txt); 4813 4924 } 4814 4925 if ( take ) 4815 4926 { 4816 this._undoQueue[this._undoPos] = txt;4927 this._undoQueue[this._undoPos] = snapshotData; 4817 4928 } 4818 4929 else … … 4828 4939 if ( this._undoPos > 0 ) 4829 4940 { 4830 var txt= this._undoQueue[--this._undoPos];4831 if ( txt )4832 { 4833 this.setHTML( txt);4834 4941 var snapshotData = this._undoQueue[--this._undoPos]; 4942 if ( snapshotData.txt ) 4943 { 4944 this.setHTML(snapshotData.txt); 4945 this._restoreCaretForUndoRedo(snapshotData); 4835 4946 } 4836 4947 else … … 4840 4951 } 4841 4952 }; 4953 4842 4954 /** Custom implementation of redo functionality 4843 4955 * @private … … 4847 4959 if ( this._undoPos < this._undoQueue.length - 1 ) 4848 4960 { 4849 var txt = this._undoQueue[++this._undoPos]; 4850 if ( txt ) 4851 { 4852 this.setHTML(txt); 4961 var snapshotData = this._undoQueue[++this._undoPos]; 4962 if ( snapshotData.txt ) 4963 { 4964 this.setHTML(snapshotData.txt); 4965 this._restoreCaretForUndoRedo(snapshotData); 4853 4966 } 4854 4967 else … … 4858 4971 } 4859 4972 }; 4973 4974 /** Used by undo and redo to restore a saved caret position. 4975 * 4976 * Undo and redo must have already set the html when they call this. 4977 * 4978 * @private 4979 * @param mixed snapshotData as recorded by _undoTakeSnapshot 4980 */ 4981 4982 Xinha.prototype._restoreCaretForUndoRedo = function(snapshotData) 4983 { 4984 // Caret restoration 4985 try 4986 { 4987 // If the snapped caret was actually in the body as it's parent 4988 // (ie text with no containing element except body) 4989 // push that data back into the body element so we can treat it as 4990 // any other element 4991 if(snapshotData.caretInBody) 4992 { 4993 Xinha.addClass(this._doc.body, 'xinha-undo-caret'); 4994 this._doc.body.setAttribute('xinha-undo-caret', snapshotData.caretRestorationData); 4995 } 4996 4997 // Find the caret data we might have recorded in the html 4998 var caretParents = Xinha.getElementsByClassName(this._doc.body,'xinha-undo-caret'); 4999 5000 // Body itself may be the one 5001 if(Xinha._hasClass(this._doc.body, 'xinha-undo-caret')) 5002 { 5003 caretParents[caretParents.length] = this._doc.body; 5004 } 5005 5006 // Just in case some bug happened and there was more than one caret saved 5007 // we will do them all to clear them, but there should only really be 0 or 1 5008 for(var i = 0; i < caretParents.length; i++) 5009 { 5010 if(caretParents[i].getAttribute('xinha-undo-caret').length) 5011 { 5012 var caretRestorationData = JSON.parse(caretParents[i].getAttribute('xinha-undo-caret')); 5013 5014 if(caretParents[i].childNodes.length > caretRestorationData.caretChild) 5015 { 5016 var rng = this.createRange(); 5017 rng.setStart(caretParents[i].childNodes[caretRestorationData.caretChild], caretRestorationData.caretOffset); 5018 rng.collapse(true); // collapse to the start, although end would be ok I think, should be the same 5019 5020 var sel = this.getSelection(); 5021 sel.removeAllRanges(); 5022 sel.addRange(rng); 5023 } 5024 } 5025 5026 Xinha.removeClass(caretParents[i], 'xinha-undo-caret'); 5027 caretParents[i].removeAttribute('xinha-undo-caret'); 5028 } 5029 } 5030 catch(e) 5031 { 5032 // Browser doesn't support something, I'm not going to try and 5033 // implement this on old browsers. 5034 if(Xinha.is_gecko || Xinha.is_webkit) 5035 { 5036 Xinha.debugMsg('Caret restoration code for undo failed. If your browser is modern, developers should check it out in XinhaCore.js (search for caret restoration).','warn'); 5037 } 5038 } 5039 } 5040 4860 5041 /** Disables (greys out) the buttons of the toolbar 4861 5042 * @param {Array} except this array contains ids of toolbar objects that will not be disabled
Note: See TracChangeset
for help on using the changeset viewer.