/**
* @ignore
* bubble or tip view for kissy editor
* @author yiminghe@gmail.com
*/
KISSY.add("editor/plugin/bubble", function (S, Overlay, Editor) {
var undefined = {}['a'],
logger= S.getLogger('s/editor'),
BUBBLE_CFG = {
zIndex: Editor.baseZIndex(Editor.ZIndexManager.BUBBLE_VIEW),
elCls: "{prefixCls}editor-bubble",
prefixCls: "{prefixCls}editor-",
effect: {
effect: "fade",
duration: 0.3
}
};
function inRange(t, b, r) {
return t <= r && b >= r;
}
// 是否两个bubble上下重叠?
function overlap(b1, b2) {
var b1_top = b1.get("y"),
b1_bottom = b1_top + b1.get("el").outerHeight(),
b2_top = b2.get("y"),
b2_bottom = b2_top + b2.get("el").outerHeight();
return inRange(b1_top, b1_bottom, b2_bottom) ||
inRange(b1_top, b1_bottom, b2_top);
}
// 得到依附在同一个节点上的所有 bubble 中的最下面一个
function getTopPosition(self) {
var archor = null,
editor = self.get("editor"),
myBubbles = editor.getControls();
S.each(myBubbles, function (bubble) {
if (bubble.isKeBubble &&
bubble !== self &&
bubble.get("visible") &&
overlap(self, bubble)) {
if (!archor) {
archor = bubble;
} else if (archor.get("y") < bubble.get("y")) {
archor = bubble;
}
}
});
return archor;
}
function getXy(bubble) {
var el = bubble.get("editorSelectedEl");
if (!el) {
return undefined;
}
var editor = bubble.get("editor"),
editorWin = editor.get("window"),
iframeXY = editor.get("iframe").offset(),
top = iframeXY.top,
left = iframeXY.left,
right = left + editorWin.width(),
bottom = top + editorWin.height();
// ie 中途设置 domain 后,不能获取 window 的相关属性
// 例如 window.frameEl
// 所以不能直接用 el.offset(undefined,window);
var elXY = el.offset();
elXY = Editor.Utils.getXY(elXY, editor);
var elTop = elXY.top,
elLeft = elXY.left,
elRight = elLeft + el.width(),
elBottom = elTop + el.height(),
x,
y;
// ie 图片缩放框大于编辑区域底部,bubble 点击不了了,干脆不显示
if (S.UA.ie &&
el[0].nodeName.toLowerCase() == 'img' &&
elBottom > bottom) {
return undefined;
}
// 对其下边
// el 位于编辑区域,下边界超了编辑区域下边界
if (elBottom > bottom && elTop < bottom) {
// 别挡着滚动条
y = bottom - 30;
}
// el bottom 在编辑区域内
else if (elBottom > top && elBottom < bottom) {
y = elBottom;
}
// 同上,对齐左边
if (elRight > left && elLeft < left) {
x = left;
} else if (elLeft > left && elLeft < right) {
x = elLeft;
}
if (x !== undefined && y !== undefined) {
return [x, y];
}
return undefined;
}
Editor.prototype.addBubble = function (id, filter, cfg) {
var editor = this,
prefixCls = editor.get('prefixCls'),
bubble;
cfg = cfg || {};
cfg.editor = editor;
S.mix(cfg, BUBBLE_CFG);
cfg.elCls = S.substitute(cfg.elCls, {
prefixCls: prefixCls
});
cfg.prefixCls = S.substitute(cfg.prefixCls, {
prefixCls: prefixCls
});
bubble = new Overlay(cfg);
bubble.isKeBubble = 1;
editor.addControl(id + "/bubble", bubble);
// 借鉴google doc tip提示显示
editor.on("selectionChange", function (ev) {
var elementPath = ev.path,
elements = elementPath.elements,
a,
lastElement;
if (elementPath && elements) {
lastElement = elementPath.lastElement;
if (!lastElement) {
return;
}
a = filter(lastElement);
if (a) {
bubble.set("editorSelectedEl", a);
// 重新触发 bubble show 事件
bubble.hide();
// 等所有 bubble hide 再show
S.later(onShow, 10);
} else {
onHide();
}
}
});
// 代码模式下就消失
// !TODO 耦合---
function onHide() {
bubble.hide();
var editorWin = editor.get("window");
// 刚开始就配置 mode 为 sourcecode
if (editorWin) {
editorWin.detach("scroll", onScroll);
bufferScroll.stop();
}
}
editor.on("sourceMode", onHide);
function showImmediately() {
var xy = getXy(bubble);
if (xy) {
bubble.move(xy[0],xy[1]);
var archor = getTopPosition(bubble);
if (archor) {
xy[1] = archor.get("y") + archor.get("el").outerHeight();
bubble.move(xy[0],xy[1]);
}
if (!bubble.get("visible")) {
bubble.show();
} else {
logger.debug("already show by selectionChange");
}
}
}
var bufferScroll = S.buffer(showImmediately, 350);
function onScroll() {
if (!bubble.get("editorSelectedEl")) {
return;
}
var el = bubble.get("el");
bubble.hide();
bufferScroll();
}
function onShow() {
var editorWin = editor.get("window");
editorWin.on("scroll", onScroll);
showImmediately();
}
};
}, {
requires: ['overlay', 'editor']
});