Commit e3d55a0f authored by techird's avatar techird

merge multiline select

parents d87b85c6 9f8f1eeb
......@@ -78,6 +78,7 @@
'src/module/select.js',
'src/module/history.js',
'src/module/editor.js',
'src/module/editor.keyboard.js',
'src/module/editor.range.js',
'src/module/editor.receiver.js',
'src/module/editor.selection.js',
......
Subproject commit f83c5f109addefd4f89ba11b4dfa88c191809e77
Subproject commit fee45f69801839d897e5e6245be93eaefafd615a
describe("editor.range", function () {
var _div = document.createElement('div');
_div.setAttribute('contenteditable', true);
var range = new KM.Minder.Range(_div);
describe("getStartOffset",function(){
_div.innerHTML = 'xxx<br\/><br\/>xxx<br\/>';
it("选区在容器上", function () {
range.startContainer = _div;
range.startOffset = 2;
expect(range.getStartOffset()).toBe(4);
});
it("选区在文本节点上", function () {
range.startContainer = _div.childNodes[3];
range.startOffset = 2;
expect(range.getStartOffset()).toBe(7);
});
});
describe("setStartOffset",function(){
_div.innerHTML = 'sdfsdfsdfsdf<br><br>sdf3<br>23232<br>';
it("选区在容器上", function () {
range.container = _div;
debugger
range.setStartOffset(26);
expect(range.startContainer).toBe(_div);
});
// it("选区在文本节点上", function () {
//
// range.startContainer = _div.childNodes[3];
// range.startOffset = 2;
// expect(range.getStartOffset()).toBe(7);
// });
})
});
describe("editor.receiver", function () {
debugger
var _div = window.document.createElement('div');
_div.id ="testDiv";
var km = KM.getMinder('testDiv');
var sel = new KM.Minder.Selection();
var range = new KM.Minder.Range();
var receiver = new KM.Minder.Receiver(this,sel,range);
describe("getStartOffset",function(){
//
// _div.innerHTML = 'xxx<br\/><br\/>xxx<br\/>';
// it("选区在容器上", function () {
//
// range.startContainer = _div;
// range.startOffset = 2;
// expect(range.getStartOffset()).toBe(4);
// });
// it("选区在文本节点上", function () {
//
// range.startContainer = _div.childNodes[3];
// range.startOffset = 2;
// expect(range.getStartOffset()).toBe(7);
// });
})
});
......@@ -108,6 +108,9 @@ var MinderNode = KityMinder.MinderNode = kity.createClass('MinderNode', {
* @param {String} text 文本数据
*/
setText: function(text) {
if(utils.isArray(text)){
text = text.join('\n');
}
return this.setData('text', text);
},
......@@ -115,8 +118,14 @@ var MinderNode = KityMinder.MinderNode = kity.createClass('MinderNode', {
* 获取节点的文本数据
* @return {String}
*/
getText: function() {
return this.getData('text');
getText: function(str2arr) {
var text = this.getData('text');
if(str2arr){
text = text.split('\n');
}
return text;
},
/**
......
......@@ -271,7 +271,47 @@ var utils = Utils = KityMinder.Utils = {
},
cloneArr:function(arr){
return [].concat(arr);
},
clearWhitespace:function(str){
return str.replace(/[\u200b\t\r\n]/g, '');
},
getValueByIndex:function(data,index){
var initIndex = 0,result = 0;
utils.each(data,function(i,arr){
if(initIndex + arr.length >= index){
if(index - initIndex == arr.length){
result = {
x: arr[arr.length - 1].x + arr[arr.length - 1].width,
y: arr[arr.length - 1].y
};
}else{
result = arr[index - initIndex];
}
return false;
}else{
initIndex += arr.length + (arr.length == 1 && arr[0].width === 0 ? 0 : 1);
}
});
return result;
},
getNodeIndex:function (node, ignoreTextNode) {
var preNode = node,
i = 0;
while (preNode = preNode.previousSibling) {
if (ignoreTextNode && preNode.nodeType == 3) {
if(preNode.nodeType != preNode.nextSibling.nodeType ){
i++;
}
continue;
}
i++;
}
return i;
}
};
......
......@@ -5,11 +5,15 @@ KityMinder.registerModule('basestylemodule', function() {
return node.getData(name) || node.getStyle(name);
}
KityMinder.TextRenderer.registerStyleHook(function(node, text) {
text.setFont({
weight: getNodeDataOrStyle(node, 'font-weight'),
style: getNodeDataOrStyle(node, 'font-style')
KityMinder.TextRenderer.registerStyleHook(function(node, textGroup) {
textGroup.eachItem(function(index,item){
item.setFont({
'weight': getNodeDataOrStyle(node,'font-weight'),
'style': getNodeDataOrStyle(node,'font-style')
});
});
});
return {
'commands': {
......
......@@ -5,14 +5,13 @@ KityMinder.registerModule('TextEditModule', function() {
var sel = new Minder.Selection();
var range = new Minder.Range();
var receiver = new Minder.Receiver(this,sel,range);
var keyboarder = new Minder.keyboarder(receiver);
this.receiver = receiver;
//鼠标被点击,并未太抬起时为真
var mouseDownStatus = false;
var lastEvtPosition, dir = 1;
//当前是否有选区存在
var selectionReadyShow = false;
......@@ -24,17 +23,16 @@ KityMinder.registerModule('TextEditModule', function() {
var color = node.getStyle('text-selection-color');
//准备输入状态
var textShape = node.getTextShape();
receiver.updateByMinderNode(node);
sel.setHide()
.setStartOffset(0)
.setEndOffset(textShape.getContent().length)
.setEndOffset(receiver.getTxtOfContainer().length)
.setColor(color);
receiver
.setMinderNode(node)
.updateContainerRangeBySel();
receiver.updateContainerRangeBySel();
if(browser.ie ){
var timer = setInterval(function(){
......@@ -57,7 +55,7 @@ KityMinder.registerModule('TextEditModule', function() {
km.textEditNode = function(node){
inputStatusReady(node);
km.setStatus('textedit');
receiver.updateSelectionShow();
receiver.updateSelection();
};
return {
'events': {
......@@ -92,27 +90,28 @@ KityMinder.registerModule('TextEditModule', function() {
if(node){
var textShape = node.getTextShape();
textShape.setStyle('cursor', 'default');
if (this.isSingleSelect() && node.isSelected()) {
var textGroup = node.getTextGroup();
textGroup.setStyle('cursor', 'default');
sel.collapse(true);
sel.setColor(node.getStyle('text-selection-color'));
receiver
.setMinderNode(node)
.setCurrentIndex(e.getPosition(this.getRenderContainer()))
.updateByMinderNode(node)
.updateIndexByMouse(e.getPosition(node.getRenderContainer()))
.setRange(range)
.setReady();
lastEvtPosition = e.getPosition(this.getRenderContainer());
if(selectionReadyShow){
textShape.setStyle('cursor', 'text');
receiver.updateSelection();
textGroup.setStyle('cursor', 'text');
sel.setShowStatus();
setTimeout(function() {
sel.setShow();
sel.collapse(true)
.updatePosition(receiver.getOffsetByIndex())
.setShow();
}, 200);
km.setStatus('textedit');
......@@ -166,9 +165,8 @@ KityMinder.registerModule('TextEditModule', function() {
node.getTextShape().setStyle('cursor', 'text');
// node.getTextShape().setStyle('cursor', 'text');
receiver.updateSelection();
//必须再次focus,要不不能呼出键盘
if(browser.ipad){
......@@ -176,24 +174,22 @@ KityMinder.registerModule('TextEditModule', function() {
}
setTimeout(function() {
sel.setShow();
sel.collapse(true)
.updatePosition(receiver.getOffsetByIndex())
.setShow();
}, 200);
lastEvtPosition = e.getPosition(this.getRenderContainer());
km.setStatus('textedit');
return;
}
//当选中节点后,输入状态准备
if(sel.isHide()){
inputStatusReady(e.getTargetNode());
}else {
//当有光标时,要同步选区
if(!sel.collapsed){
receiver.updateContainerRangeBySel();
}
......@@ -205,22 +201,21 @@ KityMinder.registerModule('TextEditModule', function() {
},
'textedit.beforemousemove inputready.beforemousemove': function(e) {
if(browser.ipad){
return;
}
//ipad下不做框选
if (mouseDownStatus && receiver.isReady() && selectionReadyShow) {
var node = e.getTargetNode();
e.stopPropagationImmediately();
var offset = e.getPosition(this.getRenderContainer());
dir = offset.x > lastEvtPosition.x ? 1 : (offset.x < lastEvtPosition.x ? -1 : dir);
receiver.updateSelectionByMousePosition(offset, dir)
.updateSelectionShow(dir)
var offset = e.getPosition(node.getRenderContainer());
receiver.updateSelectionByMousePosition(offset)
.updateSelection(offset)
.updateContainerRangeBySel();
lastEvtPosition = e.getPosition(this.getRenderContainer());
}else if(mouseDownStatus && !selectionReadyShow){
//第一次点中,第二次再次点中进行拖拽
......@@ -231,12 +226,13 @@ KityMinder.registerModule('TextEditModule', function() {
'normal.dblclick textedit.dblclick inputready.dblclick': function(e) {
var node = e.getTargetNode();
if(node){
inputStatusReady(e.getTargetNode());
inputStatusReady(node);
km.setStatus('textedit');
receiver.updateSelectionShow();
receiver.updateSelection();
}
},
......@@ -259,7 +255,7 @@ KityMinder.registerModule('TextEditModule', function() {
};
if (cmds[e.commandName]) {
inputStatusReady(km.getSelectedNode());
receiver.updateSelectionShow();
receiver.updateSelection();
return;
}
......@@ -270,9 +266,7 @@ KityMinder.registerModule('TextEditModule', function() {
},
'layoutfinish':function(e){
if (e.node === receiver.minderNode && (this.getStatus() == 'textedit' || this.getStatus() == 'inputready') ) {//&& selectionReadyShow
receiver
.setBaseOffset()
.setContainerStyle();
receiver.setContainerStyle();
}
},
'selectionclear': function() {
......@@ -287,13 +281,13 @@ KityMinder.registerModule('TextEditModule', function() {
},
'blur': function() {
receiver.clear();
!/\?debug#?/.test(location.href) && receiver.clear();
},
'textedit.import': function() {
km.setStatus('normal');
receiver.clear();
},
'textedit.mousewheel': function() {
'inputready.mousewheel textedit.mousewheel': function() {
receiver.setContainerStyle();
}
......
This diff is collapsed.
Minder.Range = kity.createClass('Range',{
constructor : function(){
Minder.Range = kity.createClass('Range',function(){
function getOffset(rng,dir){
var node = rng[dir + 'Container'],
offset = rng[dir + 'Offset'],
rOffset = 0;
if(node.nodeType == 1){
//默认不会出现得不到子节点的情况
node = node.childNodes[offset];
if(node.nodeType == 3){
offset = 0;
}
}
utils.each(rng.container.childNodes,function(index,n){
if(n === node){
if(n.nodeType == 1){
return false;
}else{
rOffset += offset;
return false;
}
}
rOffset += (n.nodeType == 1 ? 1 : utils.clearWhitespace(n.nodeValue).length);
});
return rOffset;
}
function setBoundary(rng,offset,dir){
var rOffset = 0,cont = rng.container;
utils.each(cont.childNodes,function(index,node){
if(node.nodeType == 1){
rOffset++;
if(rOffset == offset){
rng['set' + dir](cont,index);
return false;
}
return;
}
var currentLength = utils.clearWhitespace(node.nodeValue).length;
if(rOffset + currentLength >= offset){
rng['set' + dir](node,offset - rOffset);
return false;
}
rOffset += currentLength;
});
}
return {
constructor : function(container){
this.nativeRange = document.createRange();
this.nativeSel = window.getSelection();
this.startContainer =
this.endContainer =
this.startOffset =
this.endOffset = null;
this.collapsed = false;
this.container = container || null;
},
hasNativeRange : function(){
return this.nativeSel.rangeCount !== 0 ;
},
deleteContents : function(){
this.nativeRange.deleteContents();
return this._updateBoundary();
},
select:function(){
var start = this.nativeRange.startContainer;
if(start.nodeType == 1 && start.childNodes.length === 0){
......@@ -23,47 +81,81 @@ Minder.Range = kity.createClass('Range',{
this.nativeSel.addRange(this.nativeRange);
return this;
},
setStart:function(node,index){
try{
this.nativeRange.setStart(node,index);
}catch(error){
console.log(error);
}
_updateBoundary : function(){
var nRange = this.nativeRange;
this.startContainer = nRange.startContainer;
this.endContainer = nRange.endContainer;
this.startOffset = nRange.startOffset;
this.endOffset = nRange.endOffset;
this.collapsed = nRange.collapsed;
return this;
},
setStartOffset:function(offset){
setBoundary(this,offset,'Start');
return this;
},
setEnd:function(node,index){
this.nativeRange.setEnd(node,index);
setEndOffset:function(offset){
setBoundary(this,offset,'End');
return this;
},
setStart:function(node,offset){
this.nativeRange.setStart(node,offset);
this._updateBoundary();
return this;
},
setStartAfter:function(node){
return this.setStart(node.parentNode,utils.getNodeIndex(node) + 1);
},
setEnd:function(node,offset){
this.nativeRange.setEnd(node,offset);
this._updateBoundary();
return this;
},
update:function(){
this.updateNativeRange()
._updateBoundary();
return this;
},
getStart:function(){
var range = this.nativeSel.getRangeAt(0);
this.update();
return {
startContainer:range.startContainer,
startOffset:range.startOffset
startContainer:this.startContainer,
startOffset:this.startOffset
};
},
getStartOffset:function(){
return this.nativeRange.startOffset;
return getOffset(this,'start');
},
getEndOffset:function(){
return this.nativeRange.endOffset;
return getOffset(this,'end');
},
collapse:function(toStart){
this.nativeRange.collapse(toStart === true);
this._updateBoundary();
return this;
},
isCollapsed:function(){
return this.nativeRange.collapsed;
this._updateBoundary();
return this.collapsed;
},
insertNode:function(node){
this.nativeRange.insertNode(node);
return this;
return this._updateBoundary();
},
updateNativeRange:function(){
this.nativeRange = this.nativeSel.getRangeAt(0);
return this;
},
clear : function(){
this.nativeSel.removeAllRanges();
return this;
}
});
\ No newline at end of file
};
}());
\ No newline at end of file
This diff is collapsed.
//模拟光标
Minder.Selection = kity.createClass( 'Selection', {
base: kity.Rect,
base: kity.Group,
constructor: function ( height, color, width ) {
this.callBase();
this.height = height || 20;
......@@ -14,14 +14,85 @@ Minder.Selection = kity.createClass( 'Selection', {
this.setOpacity(0.5);
this.setStyle('cursor','text');
this._show = false;
this.offset = [];
},
setMinderNode : function(node){
this.minderNode = node;
},
setColor:function(color){
this.fill(color);
},
updateOffsetByTextData:function(data,offset){
if(this.collapsed){
this.offset = utils.getValueByIndex(data,this.startOffset);
return this;
}else{
var arrOffset = [],tmpOffset = {},
startOffset = this.startOffset,
endOffset = this.endOffset,
cIndex = 0;
utils.each(data,function(l,arr){
tmpOffset = {
width:0,
x:0,
y:0
};
utils.each(arr,function(i,o){
if(cIndex >= startOffset && cIndex <= endOffset){
if(i === 0 || cIndex === startOffset){
tmpOffset.x = o.x;
tmpOffset.y = o.y;
tmpOffset.width = i === 0 && offset && offset.x <= o.x && cIndex != startOffset ? 0 : o.width;
}else if(cIndex < endOffset){
tmpOffset.width += o.width;
}else if(cIndex === endOffset){
return false;
}
}
cIndex++;
});
if(tmpOffset.x !== undefined) {
arrOffset.push(tmpOffset);
}
if(cIndex === endOffset) {
return false;
}
if(arr.length == 1 && arr[0].width === 0)
return;
cIndex++;
});
this.offset = arrOffset;
return this;
}
},
updatePosition:function(offset){
var me = this;
this.clear();
offset = offset || this.offset;
if(this.collapsed){
var rect = new kity.Rect().fill(null).stroke(null).setWidth(2).setHeight(this.height);
rect.setPosition(Math.round(offset.x) - 0.5,Math.round(offset.y) - 1.5);
this.addShape(rect);
}else{
utils.each(offset,function(i,v){
var rect = new kity.Rect().fill(null).stroke(null).setWidth(v.width).setHeight(me.height);
rect.setPosition(Math.round(v.x) - 0.5,Math.round(v.y) - 1.5);
me.addShape(rect);
});
}
return this;
},
collapse : function(toStart){
this.setOpacity(1);
this.width = 2;
this.collapsed = true;
if(toStart){
this.endOffset = this.startOffset;
......@@ -52,24 +123,12 @@ Minder.Selection = kity.createClass( 'Selection', {
this.setOpacity(0.5);
return this;
},
updateShow : function(offset,width){
if(width){
this.setShowHold();
}
this.setPosition(offset).setWidth(width);
this.bringTop();
return this;
},
setPosition: function ( offset ) {
try {
// 这两个是神奇的 0.5 —— SVG 要边缘锐利,你需要一些对齐
this.x = Math.round(offset.x) - 0.5;
this.y = Math.round(offset.y) - 1.5;
} catch ( e ) {
console.log(e);
update : function(data,offset){
if(data){
this.updateOffsetByTextData(data,offset);
}
this.update();
this.updatePosition();
this.setShow();
return this;
},
setHeight: function ( height ) {
......@@ -82,13 +141,13 @@ Minder.Selection = kity.createClass( 'Selection', {
this._show = false;
return this;
},
setShowHold: function () {
clearInterval( this.timer );
this.setStyle( 'display', '' );
this._show = true;
setHoldShow:function(){
this.setStyle('display','');
clearInterval(this.timer);
return this;
},
setShow: function () {
this.bringTop();
clearInterval( this.timer );
var me = this,
state = '';
......@@ -104,10 +163,15 @@ Minder.Selection = kity.createClass( 'Selection', {
}
return this;
},
setShowStatus:function(){
this._show = true;
return this;
},
isShow:function(){
return this._show;
},
isHide:function(){
return !this._show;
}
} );
\ No newline at end of file
......@@ -3,16 +3,18 @@ KityMinder.registerModule("fontmodule", function() {
return node.getData(name) || node.getStyle(name);
}
KityMinder.TextRenderer.registerStyleHook(function(node, text) {
KityMinder.TextRenderer.registerStyleHook(function(node, textGroup) {
var dataColor = node.getData('color');
var selectedColor = node.getStyle('selected-color');
var styleColor = node.getStyle('color');
text.fill(dataColor || (node.isSelected() && selectedColor ? selectedColor : styleColor));
textGroup.fill(dataColor || (node.isSelected() && selectedColor ? selectedColor : styleColor));
text.setFont({
family: getNodeDataOrStyle(node, 'font-family'),
size: getNodeDataOrStyle(node, 'font-size')
textGroup.eachItem(function(index,item){
item.setFont({
'family': getNodeDataOrStyle(node,'font-family'),
'size': getNodeDataOrStyle(node,'font-size')
});
});
});
......
......@@ -4,39 +4,61 @@ var TextRenderer = KityMinder.TextRenderer = kity.createClass('TextRenderer', {
base: Renderer,
create: function() {
return new kity.Text()
.setId(KityMinder.uuid('node_text'))
.setVerticalAlign('middle')
.setAttr('text-rendering', 'inherit');
return new kity.Group().setId(KityMinder.uuid('node_text'));
},
update: function(text,node) {
update: function(textGroup,node) {
var textArr = node.getText(true);
var lineHeight = node.getStyle('line-height');
var fontSize = node.getData('font-size') || node.getStyle('font-size');
var height = textArr.length *
node.getStyle('line-height') * (node.getData('font-size') || node.getStyle('font-size')) / 2;
var rBox = new kity.Box(),
var tmpText = node.getText();
r = Math.round;
this.setTextStyle(node, text.setContent(tmpText));
if(tmpText.length || !this._lastBox){
var box = text.getBoundaryBox();
var r = Math.round;
if (kity.Browser.ie) {
box.y += 1;
this.setTextStyle(node, textGroup);
for(var i= 0,text,textShape;
(text=textArr[i],textShape=textGroup.getItem(i),
text!==undefined||textShape!==undefined);i++){
if(text === undefined && textShape){
textGroup.removeItem(i);
}else{
if(text!==undefined&&!textShape){
textShape = new kity.Text()
.setVerticalAlign('top')
.setAttr('text-rendering', 'inherit');
textGroup.addItem(textShape);
}
textShape.setContent(text);
}
this._lastBox = {
x : r(box.x),
y : r(box.y),
width : r(box.width),
height: r(box.height)
};
}else {
this._lastBox.width = 0;
}
var lastBox = this._lastBox;
node._lastTextShapeBox = lastBox;
this.setTextStyle(node, textGroup);
textGroup.eachItem(function(i,textShape){
var y = i * fontSize * lineHeight - height;
textShape.setY(y);
rBox = rBox.merge(new kity.Box(0, y, textShape.getBoundaryBox().width, fontSize));
});
var nBox = new kity.Box(r(rBox.x), r(rBox.y),r(rBox.width), r(rBox.height));
node._currentTextGroupBox = nBox;
return function() {
return new kity.Box(lastBox.x, lastBox.y,lastBox.width, lastBox.height);
return nBox;
};
},
......@@ -58,7 +80,7 @@ utils.extend(TextRenderer, {
});
kity.extendClass(MinderNode,{
getTextShape : function() {
getTextGroup : function() {
return this.getRenderer('TextRenderer').getRenderShape();
}
});
......
......@@ -51,5 +51,6 @@ KityMinder.registerTheme('classic', {
'order-hint-path-color': '#0f0',
'order-hint-path-width': 1,
'text-selection-color': 'rgb(27,171,255)'
'text-selection-color': 'rgb(27,171,255)',
'line-height':1.5
});
\ No newline at end of file
......@@ -54,7 +54,8 @@
'order-hint-path-color': hsl(h, 100, 25),
'order-hint-path-width': 1,
'text-selection-color': hsl(h, 100, 20)
'text-selection-color': hsl(h, 100, 20),
'line-height':1.5
};
}
......
......@@ -47,5 +47,6 @@ KityMinder.registerTheme('snow', {
'order-hint-path-color': '#0f0',
'order-hint-path-width': 1,
'text-selection-color': 'rgb(27,171,255)'
'text-selection-color': 'rgb(27,171,255)',
'line-height':1.5
});
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment