1 /**
  2  * bubble or tip view for kissy editor
  3  * @author yiminghe@gmail.com
  4  */
  5 KISSY.add("editor/plugin/bubble/index", function (S, Overlay, Editor) {
  6     var Event = S.Event,
  7         undefined = {}['a'],
  8         DOM = S.DOM,
  9         BUBBLE_CFG = {
 10             zIndex:Editor.baseZIndex(Editor.zIndexManager.BUBBLE_VIEW),
 11             elCls:"ks-editor-bubble",
 12             prefixCls:"ks-editor-",
 13             effect:{
 14                 effect:"fade",
 15                 duration:0.3
 16             }
 17         };
 18 
 19     function inRange(t, b, r) {
 20         return t <= r && b >= r;
 21     }
 22 
 23     /**
 24      * 是否两个bubble上下重叠?
 25      */
 26     function overlap(b1, b2) {
 27         var b1_top = b1.get("y"),
 28             b1_bottom = b1_top + b1.get("el").outerHeight(),
 29             b2_top = b2.get("y"),
 30             b2_bottom = b2_top + b2.get("el").outerHeight();
 31 
 32         return inRange(b1_top, b1_bottom, b2_bottom) ||
 33             inRange(b1_top, b1_bottom, b2_top);
 34     }
 35 
 36     /**
 37      * 得到依附在同一个节点上的所有 bubble 中的最下面一个
 38      */
 39     function getTopPosition(self) {
 40         var archor = null,
 41             editor = self.get("editor"),
 42             myBubbles = editor.getControls();
 43         S.each(myBubbles, function (bubble) {
 44             if (bubble.get && (bubble.get("elCls") || "").indexOf("bubble") != -1 &&
 45                 bubble !== self &&
 46                 bubble.get("visible") &&
 47                 overlap(self, bubble)) {
 48                 if (!archor) {
 49                     archor = bubble;
 50                 } else if (archor.get("y") < bubble.get("y")) {
 51                     archor = bubble;
 52                 }
 53             }
 54             return archor;
 55         });
 56     }
 57 
 58     function getXy(bubble) {
 59 
 60         var el = bubble.get("editorSelectedEl");
 61 
 62 
 63         if (!el) {
 64             return undefined;
 65         }
 66 
 67         var editor = bubble.get("editor"),
 68             editorWin = editor.get("window")[0],
 69             iframeXY = editor.get("iframe").offset(),
 70             top = iframeXY.top,
 71             left = iframeXY.left,
 72             right = left + DOM.width(editorWin),
 73             bottom = top + DOM.height(editorWin),
 74             elXY = el.offset(undefined, window),
 75             elTop = elXY.top,
 76             elLeft = elXY.left,
 77             elRight = elLeft + el.width(),
 78             elBottom = elTop + el.height(),
 79             x,
 80             y;
 81 
 82         // 对其下边
 83         // el 位于编辑区域,下边界超了编辑区域下边界
 84         if (elBottom > bottom && elTop < bottom) {
 85             // 别挡着滚动条
 86             y = bottom - 30;
 87         }
 88         // el bottom 在编辑区域内
 89         else if (elBottom > top && elBottom < bottom) {
 90             y = elBottom;
 91         }
 92 
 93         // 同上,对齐左边
 94         if (elRight > left && elLeft < left) {
 95             x = left;
 96         } else if (elLeft > left && elLeft < right) {
 97             x = elLeft;
 98         }
 99 
100         if (x !== undefined && y !== undefined) {
101             return [x, y];
102         }
103         return undefined;
104     }
105 
106     Editor.prototype.addBubble = function (id, filter, cfg) {
107         var editor = this,
108             bubble;
109 
110         cfg = cfg || {};
111 
112         cfg.editor = editor;
113 
114         S.mix(cfg, BUBBLE_CFG);
115 
116         bubble = new Overlay(cfg);
117 
118         editor.addControl(id + "/bubble", bubble);
119 
120         // 借鉴google doc tip提示显示
121         editor.on("selectionChange", function (ev) {
122             var elementPath = ev.path,
123                 elements = elementPath.elements,
124                 a,
125                 lastElement;
126             if (elementPath && elements) {
127                 lastElement = elementPath.lastElement;
128                 if (!lastElement) {
129                     return;
130                 }
131                 a = filter(lastElement);
132                 if (a) {
133                     bubble.set("editorSelectedEl", a);
134                     // 重新触发 bubble show 事件
135                     bubble.hide();
136                     // 等所有 bubble hide 再show
137                     S.later(onShow, 10);
138                 } else {
139                     onHide();
140                 }
141             }
142         });
143 
144         // 代码模式下就消失
145         // !TODO 耦合---
146         function onHide() {
147             bubble.hide();
148             var editorWin = editor.get("window")[0];
149             Event.remove(editorWin, "scroll", onScroll);
150         }
151 
152         editor.on("sourceMode", onHide);
153 
154 
155 
156         function showImmediately() {
157 
158             var xy = getXy(bubble);
159             if (xy) {
160                 bubble.set("xy", xy);
161                 var archor = getTopPosition(bubble);
162                 if (archor) {
163                     xy[1] = archor.get("y") + archor.get("el").outerHeight();
164                     bubble.set("xy", xy);
165                 }
166                 if (!bubble.get("visible")) {
167                     bubble.show();
168                 } else {
169                     S.log("already show by selectionChange");
170                 }
171             }
172         }
173 
174         var bufferScroll = S.buffer(showImmediately, 350);
175 
176         function onScroll() {
177             if (!bubble.get("editorSelectedEl")) {
178                 return;
179             }
180             var el = bubble.get("el");
181             bubble.hide();
182             bufferScroll();
183         }
184 
185         function onShow() {
186             var editorWin = editor.get("window")[0];
187             Event.on(editorWin, "scroll", onScroll);
188             showImmediately();
189         }
190     };
191 }, {
192     requires:['overlay', 'editor']
193 });