+ if (start == end && /^(div|p)$/i.test(start[_nn_])) {
+ $bq = $(start).wrapInner('
').parent();
+ if (start != end) $bq.append($(end).prevUntil(_bx_).toArray().reverse()).append(end);
+ }
+
+ sel.select();
+
+ // save undo point
+ this.cast('SAVE_UNDO_POINT');
+
+ this.fireChangeNode(sel);
+ },
+ API_EXEC_INDENT : function(sender, params) {
+ var parents = this.getBlockParents();
+
+ $(parents).each(function(){
+ var $this = $(this), left = parseInt($this.css('margin-left'), 10);
+
+ left = isNaN(left)?30:left+30;
+
+ $this.css('margin-left', left+'px');
+ });
+
+ // save undo point
+ this.cast('SAVE_UNDO_POINT');
+
+ this.fireChangeNode();
+ },
+ API_EXEC_OUTDENT : function(sender, params) {
+ var parents = this.getBlockParents();
+
+ $(parents).each(function(){
+ var $this = $(this), left = parseInt($this.css('margin-left'), 10);
+
+ left = Math.max(isNaN(left)?0:left-30, 0);
+ left = left?left+'px':'';
+
+ $this.css('margin-left', left);
+
+ if (!$this.attr('style')) $this.removeAttr('style');
+ });
+
+ // save undo point
+ this.cast('SAVE_UNDO_POINT');
+
+ this.fireChangeNode();
+ },
+ /**
+ * @brief
+ * @param Number indicates heading level.
+ */
+ API_EXEC_HEADING : function(sender, params) {
+ var sel = this.oApp.getSelection(), nodes, n, i, c, $node, first, end;
+
+ if (!sel) return false;
+
+ nodes = this.getBlockParents();
+ n = parseInt(params[0], 10);
+
+ for(i=0,c=nodes.length; i
':'');
+
+ if (!$node.is('td,th,li')) $node = $node.children(0).unwrap();
+ if (!first) first = $node[0];
+ }
+
+ end = $node[0];
+ if (first == end) {
+ sel.selectNode(first);
+ } else {
+ sel.setStartBefore(first);
+ sel.setEndAfter(end);
+ }
+
+ sel.select();
+
+ // save undo point
+ this.cast('SAVE_UNDO_POINT');
+
+ this.fireChangeNode(sel);
+ },
+ /**
+ * @brief Shows the heading options layer
+ */
+ API_SHOW_HEADING_LAYER : function(sender, params) {
+ var $layer = this.$head_layer;
+
+ if (!$layer || $layer.hasClass('open')) return;
+
+ $layer.addClass('open').parent('li').addClass('active');
+
+ this.cast('HIDE_ALL_LAYER', [$layer[0]]);
+ },
+ /**
+ * @brief Hides the heading options layer
+ */
+ API_HIDE_HEADING_LAYER : function(sender, params) {
+ var $layer = this.$head_layer;
+
+ if (!$layer || !$layer.hasClass('open')) return;
+
+ $layer.removeClass('open').parent('li').removeClass('active');
+ },
+ /**
+ * @brief Toggle the haeding options layer
+ */
+ API_TOGGLE_HEADING_LAYER : function(sender, params) {
+ if (!this.$head_layer) return;
+ if (this.$head_layer.hasClass('open')) {
+ this.cast('HIDE_HEADING_LAYER');
+ } else {
+ this.cast('SHOW_HEADING_LAYER');
+ }
+ },
+ API_EXEC_LINEHEIGHT : function(sender, params) {
+ var sel = this.oApp.getSelection(), nodes;
+
+ if (!sel) return;
+
+ nodes = this.getBlockParents();
+ $(nodes).css('line-height', params[0]);
+
+ // save undo point
+ this.cast('SAVE_UNDO_POINT');
+
+ this.fireChangeNode(sel);
+ },
+ API_SHOW_LINEHEIGHT_LAYER : function(sender, params) {
+ var $layer = this.$line_layer;
+
+ if (!$layer || $layer.hasClass('open')) return;
+
+ $layer.addClass('open').parent('li').addClass('active');
+
+ this.cast('HIDE_ALL_LAYER', [$layer[0]]);
+ },
+ API_HIDE_LINEHEIGHT_LAYER : function(sender, params) {
+ var $layer = this.$line_layer;
+
+ if (!$layer || !$layer.hasClass('open')) return;
+
+ $layer.removeClass('open').parent('li').removeClass('active');
+ },
+ API_TOGGLE_LINEHEIGHT_LAYER : function(sender, params) {
+ if (!this.$line_layer) return;
+ if (this.$line_layer.hasClass('open')) {
+ this.cast('HIDE_LINEHEIGHT_LAYER');
+ } else {
+ this.cast('SHOW_LINEHEIGHT_LAYER');
+ }
+ },
+ /**
+ * @brief Hides All layer
+ */
+ API_HIDE_ALL_LAYER : function(sender, params) {
+ if (this.$head_layer && this.$head_layer[0] != params[0]) this.cast('HIDE_HEADING_LAYER');
+ if (this.$line_layer && this.$line_layer[0] != params[0]) this.cast('HIDE_LINEHEIGHT_LAYER');
+ },
+ API_ON_CHANGE_NODE : function(sender, params) {
+ var self=this, node = params[0], state = {}, $nodes, $node, i, ml;
+
+ if (!node) {
+ $.each(this.$btns, function(key){
+ self.cast('SET_COMMAND_STATE', [this[0], 'disable']);
+ });
+ return;
+ }
+
+ state.qm = state.bx = state.id = 'normal';
+ state.od = 'disable';
+
+ $nodes = $(node).parentsUntil('.'+_xr_).andSelf();
+
+ for(i = $nodes.length-1; i > -1 ; i--) {
+ if (!is_block($nodes[i])) continue;
+
+ $node = $nodes.eq(i);
+
+ if ($node.is('blockquote.bq')) state.qm = 'active';
+ else if ($node.is('div.bx')) state.bx = 'active';
+
+ if (state.od == 'disable' && (ml = $node.css('margin-left')) && !isNaN(ml=parseInt(ml)) && ml > 0) state.od = 'normal';
+ }
+
+ $.each(this.$btns, function(key) {
+ self.cast('SET_COMMAND_STATE', [this[0], state[key]]);
+ });
+ }
+});
+/**
+ * }}}
+ */
+
+/**
+ * {{{ Font Plugin
+ * @brief Set font name, size and color
+ */
+Font = xe.createPlugin('Font', {
+ _fn : {},
+ rx_color : /^(#([0-9a-f]{3}|[0-9a-f]{6})|rgb\( *\d{1,3} *, *\d{1,3} *, *\d{1,3} *\))$/i,
+ selection : null, // preserved selection
+ $ff_layer : null, // font famliy layer
+ $fs_layer : null, // font size layer
+ $cr_layer : null, // color layer
+ $ff_btn : null,
+ $fs_btn : null,
+ $cr_btn : null,
+
+ init : function() {
+ var self = this;
+
+ this._fn = {
+ ff : function(){ self.cast('TOGGLE_FONTFAMILY_LAYER'); return false; },
+ fs : function(){ self.cast('TOGGLE_FONTSIZE_LAYER'); return false; },
+ cr : function(){ self.cast('TOGGLE_COLOR_LAYER'); return false; },
+ hover : function(){ $(this).parent().addClass('hover'); return false; },
+ out : function(){ $(this).parent().removeClass('hover'); return false; }
+ };
+ },
+ activate : function() {
+ var self = this, $tb = this.oApp.$toolbar, $pv, $col, $bgcol, col, bgcol, _bc_ = 'background-color';
+
+ // buttons
+ this.$ff_btn = $tb.find('li.ff > button:first').mousedown(this._fn.ff);
+ this.$fs_btn = $tb.find('li.fs > button:first').mousedown(this._fn.fs);
+ this.$cr_btn = $tb.find('li.cr > button:first').mousedown(this._fn.cr);
+
+ // layers
+ this.$ff_layer = this.$ff_btn.next('.lr');
+ this.$fs_layer = this.$fs_btn.next('.lr');
+ this.$cr_layer = this.$cr_btn.next('.lr').mousedown(function(event){ event.stopPropagation() });
+ this.$cr_preview = $pv = this.$cr_layer.find('>.pv span');
+
+ // items
+ this.$ff_layer.find('button')
+ .hover(this._fn.hover, this._fn.out)
+ .click(function(){
+ self.cast('EXEC_FONTFAMILY', [$(this).css('font-family')]);
+ self.cast('HIDE_FONTFAMILY_LAYER');
+ return false;
+ });
+
+ this.$fs_layer.find('button')
+ .hover(this._fn.hover, this._fn.out)
+ .click(function(){
+ self.cast('EXEC_FONTSIZE', [$(this).css('font-size')]);
+ self.cast('HIDE_FONTSIZE_LAYER');
+ return false;
+ });
+
+ $col = this.$cr_layer.find('>.fc input[type=text]')
+ .focus(function(){ })
+ .blur(function(){ })
+ .keypress(function(){ });
+
+ $bgcol = this.$cr_layer.find('>.bc input[type=text]')
+ .focus(function(){ })
+ .blur(function(){ })
+ .keypress(function(){ });
+
+ this.$cr_layer
+ .find('>.fc button')
+ .hover(
+ function(){ $pv.css('color', $(this).css(_bc_)); },
+ function(){ $pv.css('color', $pv.data('col')); }
+ )
+ .click(function(){
+ var val = $(this).css(_bc_);
+
+ if (col && val == col) {
+ self.cast('EXEC_FONTCOLOR', [val]);
+ self.cast('HIDE_COLOR_LAYER');
+ } else {
+ $pv.data('col', col=val).css('color', val);
+ $col.val(self.toHex(val)).prev('label').hide();
+ setTimeout(function(){ col = '' }, 500);
+ }
+ return false;
+ })
+ .end()
+ .find('>.bc button')
+ .hover(
+ function(){ $pv.css(_bc, $(this).css(_bc_)); },
+ function(){ $pv.css(_bc_, $pv.data('bgcol')); }
+ )
+ .click(function(){
+ var $this = $(this), val = $this.css(_bc_);
+
+ if (bgcol && val == bgcol) {
+ self.cast('EXEC_FONTBGCOLOR', [val]);
+ self.cast('HIDE_COLOR_LAYER');
+ } else {
+ $pv.data('bgcol', bgcol=val).css(_bc_, val);
+ $bgcol.val(self.toHex(val)).prev('label').hide();
+ setTimeout(function(){ bgcol = '' }, 500);
+ }
+ return false;
+ })
+ .end()
+ .find('>button')
+ .click(function(){
+ var col = $pv.data('col'), bgcol = $pv.data('bgcol');
+
+ if (col) self.cast('EXEC_FONTCOLOR', [col]);
+ if (bgcol) self.cast('EXEC_FONTBGCOLOR', [bgcol]);
+ self.cast('HIDE_COLOR_LAYER');
+
+ return false;
+ });
+ },
+ deactivate : function() {
+ this.$ff_btn.unbind('mousedown');
+ this.$fs_btn.unbind('mousedown');
+ this.$cr_btn.unbind('mousedown');
+
+ this.$ff_layer.find('button').unbind();
+ this.$fs_layer.find('button').unbind();
+ this.$cr_layer.find('button,input').unbind();
+ },
+ showLayer : function($layer) {
+ if (!$layer || $layer.hasClass('open')) return;
+
+ $layer.addClass('open').parent('li').addClass('active');
+
+ this.cast('HIDE_ALL_LAYER', [$layer[0]]);
+ },
+ hideLayer : function($layer) {
+ if (!$layer || !$layer.hasClass('open')) return;
+
+ $layer.removeClass('open').parent('li').removeClass('active');
+ },
+ toggleLayer : function($layer, api) {
+ if (!$layer) return;
+ if ($layer.hasClass('open')) {
+ this.cast('HIDE_'+api+'_LAYER');
+ } else {
+ this.cast('SHOW_'+api+'_LAYER');
+ }
+ },
+ toHex : function(col) {
+ var regNoSharp, regRGB;
+
+ regNoSharp = /^([0-9A-F]{3}|[0-9A-F]{6})$/i;
+ regRGB = /^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i;
+
+ function fixed(num, count) {
+ var str = num + '';
+
+ while(str.length < count) str = '0' + str;
+
+ return str;
+ };
+
+ if (regNoSharp.test(col)) {
+ col = '#'+col;
+ } else if (regRGB.test(col)) {
+ col = '#'+fixed((+RegExp.$1).toString(16),2)+fixed((+RegExp.$2).toString(16),2)+fixed((+RegExp.$3).toString(16),2);
+ }
+
+ return col;
+ },
+ /**
+ * @brief Set font size
+ * @param Number indicates font size in pixel
+ */
+ API_EXEC_FONTSIZE : function(sender, params) {
+ if(!params[0]) return;
+
+ this.cast('EXEC_FONTSTYLE', [{fontSize:params[0]}]);
+
+ // save undo point
+ this.cast('SAVE_UNDO_POINT');
+ },
+ /**
+ * @brief Set font family
+ * @param String indicates font family
+ */
+ API_EXEC_FONTFAMILY : function(sender, params) {
+ if(!params[0]) return;
+
+ this.cast('EXEC_FONTSTYLE', [{fontFamily:params[0]}]);
+
+ // save undo point
+ this.cast('SAVE_UNDO_POINT');
+ },
+ /**
+ * @brief Set font color
+ * @param String indicates color
+ */
+ API_EXEC_FONTCOLOR : function(sender, params) {
+ if(!params[0]) return;
+
+ this.cast('EXEC_FONTSTYLE', [{color:params[0]}]);
+
+ // save undo point
+ this.cast('SAVE_UNDO_POINT');
+ },
+ /**
+ * @breif Set font background color
+ * @param String indicates background color
+ */
+ API_EXEC_FONTBGCOLOR : function(sender, params) {
+ if(!params[0]) return;
+
+ this.cast('EXEC_FONTSTYLE', [{backgroundColor:params[0]}]);
+
+ // save undo point
+ this.cast('SAVE_UNDO_POINT');
+ },
+ /**
+ * @brief Set font style
+ * @param styles Styling information
+ */
+ API_EXEC_FONTSTYLE : function(sender, params) {
+ var sel = this.oApp.getSelection(), styles = params[0], span, val;
+
+ if (sel.collapsed) {
+ }
+
+ sel.styleRange(styles);
+ this.oApp.$richedit.focus();
+ sel.select();
+
+ // save undo point
+ this.cast('SAVE_UNDO_POINT');
+ },
+ /**
+ * @brief Show fontfamily layer
+ */
+ API_SHOW_FONTFAMILY_LAYER : function(sender, params) {
+ this.showLayer(this.$ff_layer);
+ },
+ /**
+ * @brief Hide fontfamily layer
+ */
+ API_HIDE_FONTFAMILY_LAYER : function(sender, params) {
+ this.hideLayer(this.$ff_layer);
+ },
+ /**
+ * @brief Toggle fontfamily layer
+ */
+ API_TOGGLE_FONTFAMILY_LAYER : function(sender, params) {
+ this.toggleLayer(this.$ff_layer, 'FONTFAMILY');
+ },
+ /**
+ * @brief Show fontsize layer
+ */
+ API_SHOW_FONTSIZE_LAYER : function(sender, params) {
+ this.showLayer(this.$fs_layer);
+ },
+ /**
+ * @brief Hide fontsize layer
+ */
+ API_HIDE_FONTSIZE_LAYER : function(sender, params) {
+ this.hideLayer(this.$fs_layer);
+ },
+ /**
+ * @brief Toggle fontsize layer
+ */
+ API_TOGGLE_FONTSIZE_LAYER : function(sender, params) {
+ this.toggleLayer(this.$fs_layer, 'FONTSIZE');
+ },
+ /**
+ * @brief Show color layer
+ */
+ API_SHOW_COLOR_LAYER : function(sender, params) {
+ this.showLayer(this.$cr_layer);
+ },
+ /**
+ * @brief Hide color layer
+ */
+ API_HIDE_COLOR_LAYER : function(sender, params) {
+ this.hideLayer(this.$cr_layer);
+ },
+ /**
+ * @brief Toggle color layer
+ */
+ API_TOGGLE_COLOR_LAYER : function(sender, params) {
+ this.toggleLayer(this.$cr_layer, 'COLOR');
+ },
+ API_HIDE_ALL_LAYER : function(sender, params) {
+ var $ff = this.$ff_layer, $fs = this.$fs_layer, $cr = this.$cr_layer, except = params[0];
+
+ if ($ff && $ff[0] != except) this.cast('HIDE_FONTFAMILY_LAYER');
+ if ($fs && $fs[0] != except) this.cast('HIDE_FONTSIZE_LAYER');
+ if ($cr && $cr[0] != except) this.cast('HIDE_COLOR_LAYER');
+ }
+});
+/**
+ * }}}
+ */
+
+/**
+ * {{{ LineBreak plugin
+ * @brief Insert
or wrap the block with
when return key is pressed.
+ */
+LineBreak = xe.createPlugin('LineBreak', {
+ _fn : null,
+ _br_timer : null,
+ _in_br : false,
+
+ init : function(){
+ this._fn = bind(this, this.keydown);
+ },
+ activate : function() {
+ this.oApp.$richedit.keydown(this._fn);
+
+ // If you pres Enter key,
will be inserted by default.
+ this.oApp.setDefault('force_br', true);
+ },
+ deactivate : function() {
+ this.oApp.$richedit.unbind('keydown', this._fn);
+ },
+ keydown : function(event) {
+ var sel, sc, ec;
+
+ if (event.keyCode != 13 || event.ctrlKey || event.altKey || event.metaKey) {
+ if (this._in_br) {
+ clearTimeout(this._br_timer);
+ this._br_timer = null;
+ this._in_br = false;
+ }
+ return;
+ }
+ if (!this.oApp.getOption('force_br')) return;
+ if (!(sel = this.oApp.getSelection())) return;
+
+ event.shiftKey ? this.wrapBlock(sel) : this.insertBR(sel);
+
+ return false;
+ },
+ wrapBlock : function(sel) {
+ var self = this, sc, so, eo, $node, $clone, $bookmark, last, _xb_ = '_xeed_tmp_bookmark';
+
+ // collect all sibling
+ function sibling(node, prev) {
+ var s, ret = [];
+
+ while(s=node[prev?_ps_:_ns_]) {
+ if (s[_nt_] == 3 || (s[_nt_] == 1 && !rx_block.test(s[_nn_]))) ret.push(s);
+ node = s;
+ }
+
+ if (prev) ret = ret.reverse();
+
+ return ret;
+ }
+
+ // delete contents
+ sel.deleteContents();
+
+ // is this node in a list?
+ sc = sel[_sc_];
+ so = sel[_so_];
+ eo = sel[_eo_];
+
+ if (sc[_nt_] == 1 && so > -1 && (sc=sc[_cn_][so]) && sc[_nt_] == 1) {
+ $node = $(sc);
+ } else {
+ if (sc[_nt_] == 3) sc = sc[_pn_];
+ $node = $(sc=sel[_sc_]);
+ }
+
+ // find block parent
+ $node = $node.parentsUntil('.'+_xr_).filter(function(){ return rx_block.test(this[_nn_]) });
+ $node = $node.length?$node.eq(0):$($.merge(sibling(sc,1), [sc], sibilng(sc))).wrap('
').parent();
+
+ // wrap with '
' in a table cell
+ if ($node.is('td,th')) $node = $node.wrapInner('
').children(0);
+
+ // create clone
+ if (/^h[1-6]$/i.test($node[0].nodeName)) {
+ $clone = $('
');
+ } else {
+ $clone = $node.clone().empty();
+ }
+
+ // append clone of this node
+ $node.after($clone).append($bookmark=$('').attr('id',_xb_));
+
+ sel.setEndAfter($bookmark[0]);
+ $clone.append(sel.extractContents());
+ $bookmark.remove();
+ $('#'+_xb_).remove();
+
+ if (!$.browser.msie && !$clone[0][_cn_].length) {
+ $clone.append(d.createTextNode(invisibleCh));
+ }
+
+ sel.setStart($clone[0], 0);
+ sel.collapseToStart();
+ sel.select();
+ },
+ insertBR : function(sel) {
+ var self = this, $br = $('
'), st, $par, $p;
+
+ // insert Node
+ sel.insertNode($br[0]);
+ sel.selectNode($br[0]);
+ sel.collapseToEnd();
+
+ // Opera web browser can't prevent default keydown event
+ // So, you need to workaround it with blur and focus tricks.
+ if ($.browser.opera) {
+ st = d.documentElement.scrollTop;
+ this.oApp.$richedit[0].blur();
+
+ setTimeout(function(){
+ var _sel = self.oApp.getEmptySelection();
+
+ self.oApp.$richedit[0].focus();
+
+ if (st != d.documentElement.scrollTop) d.documentElement.scrollTop = st;
+ if (!$br[0][_ns_]) $br.after(d.createTextNode(invisibleCh));
+
+ _sel.selectNode($br[0][_ns_]);
+ _sel.collapseToStart();
+ _sel.select();
+ }, 0);
+ return;
+ }
+
+ if (!$.browser.msie) {
+ if (!$br[0][_ns_]) $br.after(d.createTextNode(invisibleCh));
+ if ($.browser.safari) {
+ // TODO : remove broken character which is displayed only in Safari.
+ }
+ }
+
+ sel.select();
+
+ //
timer
+ if (!this._in_br) {
+ this._in_br = true;
+ this._br_timer = setTimeout(function(){ self._in_br = false; }, 500);
+ return;
+ }
+
+ if ($br && $br.prev().is('br')) {
+ $p = $(''+invisibleCh+'
');
+ $par = $br.parentsUntil('.'+_xr_);
+
+ $par.length ? $par.eq(-1).after($p) : $br.after($p);
+
+ $br.prev('br').remove().end().remove();
+
+ sel.setStart($p[0], 0);
+ sel.collapseToStart();
+ sel.select();
+ }
+
+ this._in_br = false;
+ clearTimeout(this._br_timer);
+ }
+});
+/**
+ * }}}
+ */
+
+/**
+ * {{{ Hotkey plugin
+ */
+Hotkey = xe.createPlugin('Hotkey', {
+ _fn : null,
+ _key : {},
+ map : {
+ 'BACKSPACE BKSP':8,
+ 'TAB':9,
+ 'ENTER RETURN':13,
+ 'ESC ESCAPE':27,
+ 'SPACE':32,
+ // arrows
+ 'UP':38,
+ 'DOWN':40,
+ 'LEFT':37,
+ 'RIGHT':39,
+
+ 'HOME':36,
+ 'PAGEUP PGUP':33,
+ 'PAGEDOWN PGDN':34,
+ 'END':35,
+ 'INSERT INS':45,
+ 'DELETE DEL':46,
+
+ // special chars
+ '=':187, ', <':188, '- _':189, '. >':190, '/ ?':191, '` ~':192, '{ [':219, '\\ |':220, '} ]':221, '\'\"':222
+ },
+
+ // constructor
+ init : function() {
+ var self = this;
+
+ this._fn = function(event){ return self.hotkey(event) };
+ this._key = {};
+
+ // build key map
+ if (!this.map.A) {
+ // split multiple name
+ $.each(this.map, function(k,v){
+ if(k.indexOf(' ')<0) return true;
+
+ var keys = k.split(' '), i, c;
+
+ for(i=0,c=keys.length; i < c; i++)
+ self.map[keys[i]] = v;
+
+ delete(self.map[k]);
+ });
+
+ function build(from, to) {
+ for(var c=from; c<=to; c++) self.map[String.fromCharCode(c)] = c;
+ };
+
+ // A~Z
+ build(65, 90);
+
+ // 0~9
+ build(48, 57);
+ }
+ },
+
+ // on activate
+ activate : function() {
+ this.oApp.$richedit.keydown(this._fn);
+ },
+
+ // on deactivate
+ deactivate : function() {
+ this.oApp.$richedit.unbind('keydown',this._fn);
+ },
+
+ /**
+ * @brief hotkey event handler
+ * @param e jQuery event object
+ */
+ hotkey : function(e) {
+ var _k = this._key, kc = e.keyCode, k;
+
+ // always skip - shift, alt, ctrl, windows, kor/eng
+ if ((15 < kc && kc < 19) || kc == 229) return true;
+ if (e.metaKey && e.ctrlKey) e.metaKey = false;
+
+ // make hotkey string
+ k = this.key2str(e);
+
+ if (_k[k]) {
+ _k[k](e);
+ return false;
+ }
+
+ return true;
+ },
+
+ /**
+ * @brief return normalize hotkey string
+ */
+ normalize : function(str) {
+ var keys = (str||'').replace(/ \t\r\n/g, '').toUpperCase().split('+'), obj={}, i, c;
+
+ for(i=0,c=keys.length; i < c; i++) {
+ switch(keys[i]) {
+ case 'ALT': obj.altKey = 1; break;
+ case 'CTRL': obj.ctrlKey = 1; break;
+ case 'META': obj.metaKey = 1; break;
+ case 'SHIFT': obj.shiftKey = 1; break;
+ default:
+ if (this.map[keys[i]]) obj.keyCode = this.map[keys[i]];
+ }
+ }
+
+ // if there is no valid keyCode, return undefined object.
+ if (!obj.keyCode) return;
+
+ return this.key2str(obj);
+ },
+
+ /**
+ * @brief make hotkey string from the key event object.
+ * @return hotkey string
+ */
+ key2str : function(e) {
+ var ret = [];
+
+ if (e.altKey) ret.push('ALT');
+ if (e.ctrlKey) ret.push('CTRL');
+ if (e.metaKey) ret.push('META');
+ if (e.shiftKey) ret.push('SHIFT');
+ ret.push(e.keyCode);
+
+ return ret.join('+');
+ },
+
+ /**
+ * @brief register a hotkey
+ * @param str Hotkey string
+ * @param fn Hotkey function
+ */
+ API_REGISTER_HOTKEY : function(sender, params) {
+ var str = this.normalize(params[0]), fn = params[1];
+
+ if (str) this._key[str] = fn;
+ },
+
+ /**
+ * @brief unregister a hotkey
+ * @param str Hotkey string
+ */
+ API_UNREGISTER_HOTKEY : function(sender, params) {
+ var str = this.normalize(params[0]);
+
+ if (str && this._key[str]) delete this._key[str];
+ }
+});
+/**
+ * }}}
+ */
+
+/**
+ * {{{ Content Filter plugin
+ */
+Filter = xe.createPlugin('ContentFilter', {
+ _in : [], // input filters
+ _out : [], // output filters
+ init : function() {
+ this._in = [];
+ this._out = [];
+ },
+
+ /**
+ * @brief Register a filter
+ * @params type Filter type string. 'in', 'out',
+ * @params func Filter function
+ */
+ API_REGISTER_FILTER : function(sender, params) {
+ var type = params[0], func = params[1];
+
+ if (type != 'in' || type != 'out') return;
+ this['_'+type].push(func);
+ },
+
+ /**
+ * @brief Unregister a filter
+ * @params type Filter type string.
+ * @params func Filter function
+ */
+ API_UNREGISTER_FILTER : function(sender, params) {
+ var type = params[0], func = params[1], pool, newPool=[], i, c;
+
+ if (type != 'in' || type != 'out') return;
+ for(i=0,pool=this['_'+type],c=pool.length; i < c; i++) {
+ if (pool[i] !== func) newPool.push(pool[i]);
+ }
+ this['_'+type] = newPool;
+ },
+
+ /**
+ * @brief Run input filters before SET_CONTENT
+ */
+ API_BEFORE_SET_CONTENT : function(sender, params) {
+ for(var i=0,c=this._in.length; i < c; i++) params[0] = this._in[i](params[0]);
+ },
+
+ /**
+ * @brief Run output filters before GET_CONTENT
+ */
+ API_BEFORE_GET_CONTENT : function(sender, params) {
+ for(var i=0,c=this._out.length; i < c; i++) params[0] = this._out[i](params[0]);
+ }
+});
+/**
+ * }}}
+ */
+
+/**
+ * {{{ Edit Mode plugin
+ * @brief Switch edit mode
+ */
+EditMode = xe.createPlugin('EditMode', {
+ // constructor
+ init : function() { },
+ activate : function() {
+ var self = this, app = this.oApp, $r = this.oApp.$root;
+
+ this.$btn_wysiwyg = $r.find('button.wysiwyg').mousedown(function(){ self.cast('MODE_WYSIWYG'); return false; });
+ this.$btn_html = $r.find('button.html').mousedown(function(){ self.cast('MODE_HTML'); return false; });
+
+ this.$btn_wysiwyg_p = this.$btn_wysiwyg.parent();
+ this.$btn_html_p = this.$btn_html.parent();
+
+ app.$textarea.hide();
+ app.$richedit.show();
+ },
+ deactivate : function() {
+ this.$btn_wysiwyg.unbind('mousedown');
+ this.$btn_html.unbind('mousedown');
+ },
+ API_MODE_WYSIWYG : function(sender, params) {
+ var app = this.oApp;
+
+ if (app.$richedit.is(':visible')) return true;
+
+ app.$richedit.show().parent().css('overflow','');
+ app.$textarea.hide();
+
+ // set active button
+ this.$btn_wysiwyg_p.addClass('active');
+ this.$btn_html_p.removeClass('active');
+
+ // set content
+ this.cast('SET_CONTENT', [app.$textarea.val()]);
+ },
+ API_MODE_HTML : function(sender, params) {
+ var app = this.oApp, h;
+ if (app.$richedit.is(':hidden')) return true;
+
+ app.$textarea.show().css('height', '100%').css('width', '100%').css('border',0);
+ app.$richedit.hide().parent().css('overflow','hidden');
+
+ // set active button
+ this.$btn_wysiwyg_p.removeClass('active');
+ this.$btn_html_p.addClass('active');
+
+ // set html code
+ this.cast('SET_CONTENT_HTML', [app.$richedit.html()]);
+ },
+ // If html editor is activated, cancel default SET_CONTENT action
+ // and call SET_CONTENT_HTML instead.
+ API_BEFORE_SET_CONTENT : function(sender, params) { },
+ API_BEFORE_BEFORE_SET_CONTENT : function(sender, params) {
+ if (this.$btn_html_p.hasClass('active')) {
+ this.cast('SET_CONTENT_HTML', params);
+ return false;
+ }
+ },
+ /**
+ * @brief Put html code into the textarea
+ * @param html String html code
+ */
+ API_SET_CONTENT_HTML : function(sender, params) {
+ this.oApp.$textarea.val( params[0]||'' );
+ }
+});
+/**
+ * }}}
+ */
+
+/**
+ * {{{ Resize Plugin
+ */
+Resize = xe.createPlugin('Resize', {
+ _fn : null,
+ prev_height : 400,
+ $resize_bar : null,
+ $auto_check : null,
+ $container : null,
+
+ init : function() {
+ var self = this, startY, startH, resizing;
+
+ function isLeftButton(event) {
+ return (event.which == 1);
+ }
+
+ this._fn = {
+ down : function(event) {
+ var oChk = self.$auto_check.get(0);
+
+ if (event.target != this || !isLeftButton(event)) return true;
+ if (oChk.checked || oChk.disabled) return true;
+
+ startY = event.pageY;
+ startH = parseInt(self.$container.css('height'));
+ $(document).mousemove(self._fn.move).one('mouseup', self._fn.up);
+
+ resizing = true;
+
+ self.cast('RESIZE_START');
+
+ return false;
+ },
+ up : function(event) {
+ if (!resizing) return true;
+
+ $(document).unbind({mousemove:self._fn.move, mouseup:self._fn.up});
+
+ resizing = false;
+
+ self.cast('RESIZE_END');
+
+ return false;
+ },
+ move : function(event) {
+ var diff_y, new_h;
+
+ if (!resizing || !isLeftButton(event)) return true;
+
+ diff_y = event.pageY - startY;
+ new_h = startH + diff_y;
+
+ self.$container.css('height', new_h);
+ self.cast('RESIZE');
+
+ return false;
+ },
+ check : function(event) {
+ self.cast('SET_AUTO_RESIZE', [this.checked]);
+ }
+ };
+ },
+ activate : function() {
+ var $root = this.oApp.$root, chk;
+
+ this.$container = this.oApp.$richedit.parent();
+ this.$resize_bar = $root.find('button.resize').mousedown(this._fn.down);
+ this.$auto_check = $root.find('div.autoResize > :checkbox').click(this._fn.check);
+
+ chk = this.$auto_check[0].checked;
+ this.cast('SET_AUTO_RESIZE', [chk]);
+ },
+ deactivate : function() {
+ this.$resize_bar.unbind({dragstart:this._fn.dragstart, drag:this._fn.drag});
+ this.$auto_check.unbind('click', this._fn.check);
+ },
+ /**
+ * @brief on resizing
+ * @param new height
+ * @param new mouse y position
+ */
+ // API_RESIZE : function(sender, params) { },
+ /**
+ * @brief Start resizing
+ * @param current height
+ * @param current mouse y position
+ */
+ // API_RESIZE_START : function(sender, params) { },
+ /**
+ * @brief End resizing
+ * @param new height
+ * @param new mouse y position
+ */
+ // API_RESIZE_END : function(sender, params) { },
+ /**
+ * @brief Set auto resize
+ * @param Boolean indicates whether or not to resize automatically.
+ */
+ API_SET_AUTO_RESIZE : function(sender, params) {
+ var $ctn = this.$container, $rich = this.oApp.$richedit, h;
+
+ if (params[0]) {
+ h = parseInt($ctn.css('height'), 10);
+ if (!isNaN(h)) this.prev_height = h;
+
+ $ctn.css('height', 'auto');
+ } else {
+ $ctn.css('height', this.prev_height);
+ }
+ },
+ API_BEFORE_MODE_HTML : function(sender, params) {
+ var chk = this.$auto_check.get(0).checked;
+
+ if (chk) {
+ this.prev_height = this.$container.height();
+ this.cast('SET_AUTO_RESIZE', [false]);
+ }
+ },
+ API_AFTER_MODE_HTML : function(sender, params) {
+ this.$auto_check.attr('disabled', true);
+ },
+ API_BEFORE_MODE_WYSIWYG : function(sender, params) {
+ var chk = this.$auto_check.get(0).checked;
+
+ if (chk) this.cast('SET_AUTO_RESIZE', [true]);
+ },
+ API_AFTER_MODE_WYSIWYG : function(sender, params) {
+ this.$auto_check.removeAttr('disabled');
+ }
+});
+/**
+ * }}}
+ */
+
+/**
+ * {{{ UndoRedo
+ * @brief Manages undo and redo actions, save undo point.
+ */
+UndoRedo = xe.createPlugin('UndoRedo', {
+ _history : [],
+ _index : -1,
+ _timer : null,
+ _keypress : null,
+ $undo_btn : null,
+ $redo_btn : null,
+
+ init : function() {
+ this._history = [];
+ },
+ activate : function() {
+ var self = this, $tb = this.oApp.$toolbar;
+
+ if (!$tb) return;
+
+ this.$undo_btn = $tb.find('button.ud');
+ this.$redo_btn = $tb.find('button.rd');
+
+ this.cast('REGISTER_COMMAND', [this.$undo_btn[0], 'ctrl+z', 'EXEC_UNDO']);
+ this.cast('REGISTER_COMMAND', [this.$redo_btn[0], 'ctrl+shift+z', 'EXEC_REDO']);
+
+ // keypress
+ this._keypress = function( ) {
+ if (self._timer) clearTimeout(self._timer);
+ self._timer = setTimeout(function(){ self.cast('SAVE_UNDO_POINT') }, 500);
+ };
+ this.oApp.$richedit.keypress(this._keypress);
+ },
+ deactivate : function() {
+ this.oApp.$richedit.unbind('keypress', this._keypress);
+
+ this.cast('UNREGISTER_COMMAND', [this.$undo_btn[0]]);
+ this.cast('UNREGISTER_COMMAND', [this.$redo_btn[0]]);
+
+ this.$undo_btn = null;
+ this.$redo_btn = null;
+
+ },
+ // redraw buttons
+ redraw : function(index) {
+ var $undo = this.$undo_btn, $redo = this.$redo_btn, fn, index = this._index, len = this._history.length;
+
+ if ($undo && $undo[0]) {
+ fn = (index > 0 && len)?'removeClass':'addClass';
+ $undo.parent()[fn]('disable');
+ }
+
+ if ($redo && $redo[0]) {
+ fn = (index+1 < len)?'removeClass':'addClass';
+ $redo.parent()[fn]('disable');
+ }
+ },
+ API_EXEC_UNDO : function(sender, params) {
+ this.cast('RESTORE_UNDO_POINT', [this._index-1]);
+ },
+ API_EXEC_REDO : function(sender, params) {
+ this.cast('RESTORE_UNDO_POINT', [this._index+1]);
+ },
+ /**
+ * Save undo point
+ * @return Number indicates current undo point
+ */
+ API_SAVE_UNDO_POINT : function(sender, params) {
+ var sel = this.oApp.getSelection(), history = this._history, index = this._index, item = {}, last_item, $rich = this.oApp.$richedit;
+
+ // if richedit is not shown, don't execute this command.
+ if ($rich.is(':hidden')) return -1;
+
+ // when undo history is saved, clear saving timer
+ if (this._timer) {
+ clearTimeout(this._timer);
+ this._timer = null;
+ }
+
+ // delete redo history
+ if (index+1 < history.length) history = history.slice(0, index+1);
+
+ item.content = this.cast('GET_CONTENT');
+ if (sel) {
+ item.bookmark = sel.getXPathBookmark();
+ } else {
+ item.bookmark = null;
+ }
+
+ // if the content isn't changed, don't save this history.
+ if (history.length) {
+ last_item = history[history.length-1];
+ if (item.content == last_item.content) return this._index;
+ }
+
+ history.push(item);
+
+ this._history = history;
+ this._index = history.length - 1;
+
+ this.redraw();
+
+ return this._index;
+ },
+ /**
+ * Restore to saved undo point
+ * @param Number indicates saved undo point
+ * @return Number indicates current undo point
+ */
+ API_RESTORE_UNDO_POINT : function(sender, params) {
+ var idx = params[0], item, sel, $rich = this.oApp.$richedit;
+
+ // error : invalid index
+ if (idx < 0 || !(item=this._history[idx])) return -1;
+
+ // if richedit is not shown, don't execute this command.
+ if ($rich.is(':hidden')) return -1;
+
+ // when undo history is restored, clear saving timer
+ if (this._timer) {
+ clearTimeout(this._timer);
+ this._timer = null;
+ }
+
+ // restore content
+ this.cast('SET_CONTENT', [item.content]);
+
+ // restore selection
+ sel = this.oApp.getEmptySelection();
+ if (item.bookmark) {
+ sel.moveToXPathBookmark(item.bookmark);
+ sel.select();
+ } else {
+ $rich.focus().get(0);
+ if ($rich[0][_cn_][0]) {
+ sel.selectNode($rich[0][_cn_][0]);
+ sel.collapseToStart();
+ sel.select();
+ }
+ }
+
+ // next undo point
+ this._index = idx;
+
+ this.redraw();
+
+ return idx;
+ },
+ API_AFTER_SET_CONTENT : function(sender, params) {
+ if (sender != this && this.oApp.$richedit.is(':visible')) {
+ this.cast('SAVE_UNDO_POINT');
+ }
+ }
+});
+/**
+ * }}}
+ */
+
+/**
+ * {{{ SChar
+ */
+SChar = xe.createPlugin('SChar', {
+ $btn : null,
+ $layer : null,
+ $text : null,
+ $btns : null,
+
+ init : function(){
+ },
+ activate : function() {
+ var self=this, app = this.oApp, $tb = app.$toolbar;
+
+ if (!$tb) return;
+
+ this.$btn = $tb.find('button.sc').mousedown(function(){ self.cast('TOGGLE_SCHAR_LAYER'); return false; });
+ this.$layer = this.$btn.next('div.lr')
+ .mousedown(function(event){ event.stopPropagation(); })
+ .find('button.tab')
+ .mousedown(function(){
+ self.$layer.find('li.li').removeClass('active');
+ $(this[_pn_]).addClass('active');
+ })
+ .click(function(){ $(this).mousedown() })
+ .end()
+ .find('li>button:not(.tab)')
+ .click(function(event){ self.$text[0].value += $(this).text(); })
+ .end();
+
+ this.$text = this.$layer.find('input:text')
+ .keypress(function(event){
+ if (event.keyCode == 13) {
+ self.$btns.eq(0).click();
+ return false;
+ }
+ });
+
+ this.$btns = this.$layer.find('button.btn')
+ .each(function(i){
+ var $this = $(this);
+
+ if (i == 0) {
+ $this.click(function(){
+ var sel = self.sel, dt, rt;
+
+ dt = d.documentElement.scrollTop;
+ rt = app.$richedit[0][_pn_].scrollTop;
+
+ sel.pasteHTML(self.$text.val());
+
+ self.sel = null;
+ self.cast('HIDE_SCHAR_LAYER');
+
+ app.$richedit.focus();
+ sel.select();
+
+ if (dt != d.documentElement.scrollTop) d.documentElement.scrollTop = dt;
+ if (rt != app.$richedit[0][_pn_].scrollTop) app.$richedit[0][_pn_].scrollTop = rt;
+ });
+ } else {
+ $this.click(function(){
+ self.cast('HIDE_SCHAR_LAYER');
+ });
+ }
+ });
+ },
+ deactivate : function() {
+ if (this.$btn) this.$btn.unbind('mousedown');
+ if (this.$layer) this.$layer.unbind('mousedown').find('button,input').unbind();
+ if (this.$text) this.$text.unbind();
+ },
+ API_SHOW_SCHAR_LAYER : function() {
+ var sel;
+
+ if (!this.$layer || this.$layer.hasClass('open')) return;
+ if (!(sel=this.oApp.getSelection())) return;
+
+ this.sel = sel; // save selection
+ this.$btn.parent().addClass('active');
+ this.$layer.addClass('open');
+ },
+ API_HIDE_SCHAR_LAYER : function() {
+ if (!this.$layer || !this.$layer.hasClass('open')) return;
+
+ this.$btn.parent().removeClass('active');
+ this.$layer.removeClass('open');
+ },
+ /**
+ * @brief Toggle special chars layer
+ */
+ API_TOGGLE_SCHAR_LAYER : function() {
+ this.cast( (this.$layer.hasClass('open')?'HIDE':'SHOW')+'_SCHAR_LAYER' );
+ },
+ API_HIDE_ALL_LAYER : function(sender, params) {
+ if (sender != this) this.cast('HIDE_SCHAR_LAYER');
+ }
+});
+/**
+ * }}}
+ */
+
+/**
+ * {{{ FileUpload
+ */
+FileUpload = xe.createPlugin('FileUpload', {
+ $btns : null,
+ $modal_box : null,
+ $template : null,
+ $file_list : null,
+ esc_fn : null,
+ selection : null,
+ _index : 0,
+
+ init : function(){
+ var self = this;
+
+ this.$btns = [];
+ this.esc_fn = function(event){ if(event.keyCode == 27) self.cast('HIDE_FILE_MODAL'); };
+ },
+ activate : function() {
+ var self = this, app = this.oApp, $tb = app.$toolbar;
+
+ this.$modal_box = app.$root.find('div.xdmw');
+
+ if (this.$modal_box.length) {
+ this.$attach_list = this.$modal_box.find('div.al');
+ this.$attach_list
+ .mousedown(function(event){ event.stopPropagation(); })
+ .find('button.btn.cs')
+ .click(function(){ self.cast('HIDE_FILE_MODAL'); return false; });
+
+ // show and hide function button on hover
+ this.$file_list = this.$attach_list.find('div.sn')
+ .delegate('button.ctr', 'mouseover',
+ function(){
+ var $par = $(this).parent();
+
+ if ($par.is('.uploading')) return;
+ $par.find('button.ctr').addClass('show');
+ }
+ )
+ .delegate('button.ctr', 'mouseout',
+ function(){ $(this).parent().find('button.ctr').removeClass('show'); }
+ )
+ .delegate('button.ctr.ins', 'click',
+ function(){
+ var $this = $(this), $item = $this.parent(), file_url = $this.parent().data('url');
+
+ self.cast('INSERT_FILE_INTO', [$item.attr('type'), file_url, $item.find('label').text()]);
+ }
+ )
+ .delegate('button.ctr.del', 'click',
+ function(){
+ var $this = $(this), $item = $this.parent(), file_srl = $this.parent().attr('file_srl');
+
+ self.cast('DELETE_FILE', [file_srl]);
+ }
+ );
+
+ this.$template = this.$file_list.eq(0).find('>ul:first>li:first').remove();
+
+ // select all
+ this.$attach_list.find('p.task button.all')
+ .click(function(){
+ $(this).parents('div.sn:first').find('li > input:checkbox:not([disabled])').attr('checked', 'checked');
+ });
+
+ // insert selected files
+ this.$attach_list.find('p.task button.insert')
+ .click(function(){
+ var $list = $(this).parents('div.sn:first').find('li > input:checked:not([disabled])');
+ });
+ }
+
+ if ($tb) {
+ $.each(['al','img','mov','file'], function(){
+ self.$btns[this] = $tb.find('>div.t1 li.'+this).children('a,button');
+ });
+
+ this.$btns.al.click(function(){ self.cast('SHOW_FILE_MODAL'); return false; });
+ }
+ },
+ deactivate : function() {
+ this.$attach_list.unbind()
+ .find('div.sn').undelegate()
+ .end()
+ .find('button,input').unbind();
+
+ // buttons
+ $.each(this.$btns, function(key){ this.unbind('click'); });
+ this.$btns = [];
+ },
+ createItem : function(file) {
+ var $item, ext, id = 'xeed-id-'+(this._index++);
+
+ ext = file.name.match(/\.([a-z0-9]+)$/i)[1] || '';
+ ext = ext.toLowerCase();
+
+ // get file type
+ if ($.inArray(ext, ['gif','jpg','jpeg','png']) > -1) type = 'img';
+ else if ($.inArray(ext, ['avi','mov','mpg','wmv','flv']) > -1) type = 'mov';
+ else type = 'file';
+
+ if ($.inArray(ext, ['pptx','xlsx','docx']) > -1) ext = ext.substr(0,3);
+
+ $item = this.$template.clone()
+ .find('button.ob > img').attr('alt', file.name).end()
+ .find('label').text(file.name).attr('for', id).end()
+ .find('input:checkbox').attr('id', id).end()
+ .attr('type', type).attr('ext', ext);
+
+ if (type == 'file') {
+ $item.find('>button:first').addClass(ext).empty().text(file.name);
+ }
+
+ return $item;
+ },
+ getKey : function(file) {
+ return file.name.toLowerCase()+'-'+file.size;
+ },
+ updateCount : function() {
+ var $items = this.$file_list.find('li[type]'), $tb = this.oApp.$toolbar, $area, types = ['img','mov','file'], i, c;
+
+ this.$modal_box.find('h2 strong').text($items.length);
+ $tb.find('li.ti.al em > strong').text($items.length);
+
+ for(i=0,c=types.length; i strong').text($items.length);
+ }
+ },
+ API_SHOW_FILE_MODAL : function() {
+ var self = this, uploader, file_group = [], $form, params, seq;
+
+ this.$modal_box.show();
+ this.$attach_list.show();
+
+ // register ESC hotkey
+ $(document).keydown(this.esc_fn);
+
+ this.selection = this.oApp.getSelection();
+
+ // get form
+ $form = this.oApp.$textarea.parents('form:first');
+ seq = $form.attr('editor_sequence');
+
+ // create an uploader
+ if (!this.$modal_box.data('uploader')) {
+ // additional parameter
+ params = {
+ mid : current_mid,
+ act : 'procFileUpload',
+ editor_sequence : seq,
+ upload_target_srl : $form.find('input[name=document_srl]').val()
+ };
+
+ // file onselect event
+ function file_onselect(files, old_len) {
+ var html, $ob, i, c, $list, $item, type;
+
+ for(i=old_len,c=files.length; i < c; i++) {
+ $item = self.createItem(files[i]).addClass('uploading').attr('_key', self.getKey(files[i]));
+ type = $item.attr('type');
+ $list = self.$file_list.filter('.'+type).append($item);
+
+ ($ob = $item.find('>button.ob'))
+ .data('html', $ob.html())
+ .html('');
+ }
+
+ self.updateCount();
+
+ setTimeout(function(){ uploader.cast('START'); }, 10);
+ }
+
+ function upload_onprogress(file) {
+ var $item = self.$file_list.find('li[_key='+self.getKey(file)+']');
+
+ $item.find('>button.ob span.bar').css('width', parseInt(file.loaded*100/file.size)+'%');
+ }
+
+ function upload_onfinishone(file) {
+ var $item = self.$file_list.find('li[_key='+self.getKey(file)+']'),
+ $ob = $item.find('>button.ob');
+
+ if ($item.attr('type') == 'img') {
+ }
+
+ $ob.html($ob.data('html')).parent().removeClass('uploading');
+ }
+
+ function upload_onfinish() {
+ var params = {}, primary, target_srl;
+
+ // document serial number
+ primary = editorRelKeys[seq].primary;
+
+ params = {
+ editor_sequence : $form.attr('editor_sequence'),
+ upload_target_srl : primary.value,
+ mid : current_mid
+ };
+
+ function callback(ret) {
+ if (!ret.files || !ret.files.item) return;
+ if (!$.isArray(ret.files.item)) ret.files.item = [ret.files.item];
+ if (!primary.value && ret.upload_target_srl) primary.value = ret.upload_target_srl;
+
+ var i, c, f, k, $item;
+ for(i=0,c=ret.files.item.length; i < c; i++) {
+ f = ret.files.item[i];
+ k = f.source_filename.toLowerCase()+'-'+f.file_size;
+
+ $item = self.$file_list.find('li[_key='+k+']');
+
+ if ($item.attr('type') == 'img') {
+ $item.find('button.ob > img')
+ .load(function(){
+ if(this.width > this.height){
+ $(this).css('width', '100%');
+ } else {
+ $(this).css('height', '100%');
+ }
+ })
+ .attr('src', f.download_url);
+ }
+ $item.attr('file_srl', f.file_srl).data('url', f.download_url);
+ }
+ }
+
+ $.exec_xml('file', 'getFileList', params, callback, ['error', 'message', 'files', 'left_size', 'editor_sequence', 'upload_target_srl', 'upload_status']);
+ }
+
+ uploader = xe.createUploader(
+ this.$modal_box.find('button.at'),
+ {
+ url : request_uri+'index.php',
+ dropzone : this.$modal_box.find('div.iBody'),
+ params : params,
+ onselect : file_onselect,
+ onprogress : upload_onprogress,
+ onfinishone : upload_onfinishone,
+ onfinish : upload_onfinish
+ }
+ );
+
+ this.$modal_box.data('uploader', uploader);
+ }
+ },
+ API_HIDE_FILE_MODAL : function() {
+ this.$modal_box.hide();
+ this.$attach_list.hide();
+
+ // unregister ESC hotkey
+ $(document).unbind('keydown', this.esc_fn);
+
+ if (this.selection) {
+ try { this.selection.select(); } catch(e){};
+ }
+ },
+ /**
+ * @brief Add file to the list
+ * @params Array of file info. file info = { id : 0, name : '', path : '', size : 0 };
+ */
+ API_ADD_FILE_LIST : function(sender, params) {
+ },
+ /**
+ * @brief Insert a file into the rich editor
+ */
+ API_INSERT_FILE_INTO : function(sender, params) {
+ var type = params[0], url = params[1], name = params[2], code, sel = this.selection || this.oApp.getSelection();
+
+ if (!sel) return;
+ if (type == 'img') {
+ code = '
';
+ } else {
+ code = ''+name+'';
+ }
+
+ sel.pasteHTML(code);
+ },
+ /**
+ * @brief Delete a file
+ */
+ API_DELETE_FILE : function(sender, params) {
+ var self = this, file_srl = params[0], $item = this.$file_list.find('li[file_srl='+file_srl+']'), i, c;
+
+ function callback(ret){
+ if (ret && ret.error && ret.error == 0) {
+ $item.remove();
+
+ self.updateCount();
+ }
+ }
+
+ $.exec_xml('file', 'procFileDelete', {file_srls:file_srl, editor_sequence:1}, callback);
+ }
+});
+/**
+ * }}}
+ */
+
+/**
+ * {{{ URL
+ */
+URL = xe.createPlugin('URL', {
+ sel : null,
+ $btn : null,
+ $layer : null,
+ $text : null,
+ $btns : null,
+ $chk : null,
+
+ init : function(){},
+ activate : function() {
+ var self = this, app = this.oApp, $tb = app.$toolbar;
+
+ if (!$tb) return;
+
+ this.$btn = $tb.find('button.ur')
+ .mousedown(function(){ self.cast('TOGGLE_URL_LAYER'); return false; });
+
+ this.$layer = this.$btn.next('.lr')
+ .mousedown(function(event){ event.stopPropagation() });
+
+ this.$text = this.$layer.find('input:text')
+ .keypress(function(event){ if(event.keyCode == 13) self.$btns.eq(0).click() })
+ .focus(function(){ this.select(); });
+
+ this.$chk = this.$layer.find('input:checkbox');
+
+ this.$btns = this.$layer.find('button.btn')
+ .each(function(i){
+ var $this = $(this);
+
+ if (i) {
+ $this.click(function(){ self.cast('HIDE_URL_LAYER'); });
+ } else {
+ $this.click(function(){
+ var url = self.$text.val(), newWin = self.$chk[0].checked;
+
+ self.cast('EXEC_URL', [url, newWin]);
+ self.cast('HIDE_URL_LAYER');
+ });
+ }
+ });
+ },
+ deactivate : function() {
+ if (this.$btn) this.$btn.unbind('mousedown');
+ if (this.$layer) this.$layer.unbind('mousedown').find('input,button').unbind();
+ },
+ /**
+ * @brief Insert an url
+ * @param url String url
+ * @param newWin Boolean indicates new window link
+ */
+ API_EXEC_URL : function(sender, params) {
+ var sel = this.sel || this.oApp.getSelection(), url = params[0], newWin = params[1], newUrl;
+
+ if (!sel) return;
+
+ if (sel.collapsed && url) {
+
+ } else {
+ sel.select();
+ this.cast('EXEC_COMMAND', [url?'createlink':'unlink', false, (newWin&&url?'xeed://':'')+url]);
+
+ if (newWin && url) {
+ this.oApp.$richedit.find('a[href^=xeed://]').attr('href', url).attr('target', '_blank');
+ }
+ }
+ this.cast('HIDE_URL_LAYER');
+ },
+ API_SHOW_URL_LAYER : function() {
+ var sel, $node;
+
+ if (!this.$layer || this.$layer.hasClass('open')) return;
+ if (!(sel = this.oApp.getSelection())) return;
+
+ this.sel = sel; // save selection
+ this.$btn.parent().addClass('active');
+ this.$layer.addClass('open');
+
+ $node = $(sel.commonAncestorContainer);
+ if (!$node.is('a')) $node = $node.parentsUntil('.'+_xr_).filter('a');
+ if ($node.length) {
+ this.$text.val( $node.attr('href') );
+ if ($node.attr('target') == '_blank') this.$chk[0].checked = true;
+ } else {
+ this.$text.val('http://');
+ this.$chk[0].checked = false;
+ }
+
+ this.$text.focus().select();
+ },
+ API_HIDE_URL_LAYER : function() {
+ if (!this.$layer || !this.$layer.hasClass('open')) return;
+
+ this.$btn.parent().removeClass('active');
+ this.$layer.removeClass('open');
+ },
+ API_TOGGLE_URL_LAYER : function() {
+ if (!this.$layer) return;
+
+ this.cast( (this.$layer.hasClass('open')?'HIDE':'SHOW')+'_URL_LAYER' );
+ },
+ API_HIDE_ALL_LAYER : function() {
+ if (this.$layer && this.$layer.hasClass('open')) this.cast('HIDE_URL_LAYER');
+ }
+});
+/**
+ * }}}
+ */
+
+/**
+ * {{{ Table
+ */
+Table = xe.createPlugin('Table', {
+ $btns : null,
+ $layer : null,
+ $table : null, // preview table
+ $th_btns : null,
+ $unit_btns : null,
+ selection : null,
+
+ selector : '.xeed_selected_cell',
+ cell_selector : '',
+ cmd : {
+ cm : 'MERGE_CELLS',
+ cs : 'SPLIT_CELLS',
+ rs : 'SPLIT_ROWS'
+ },
+
+ init : function(){
+ this.$btns = {};
+ this.cell_selector = 'td'+this.selector+',th'+this.selector;
+ },
+ activate : function() {
+ var self = this, $tb = this.oApp.$toolbar, $layer, $fieldset;
+
+ if (!$tb) return;
+
+ // tool buttons
+ this.$btns.te = $tb.find('button.te').mousedown(function(){ self.cast('TOGGLE_TABLE_LAYER'); return false; });
+ $.each(this.cmd, function(key) {
+ var $btn = $tb.find('button.'+key);
+
+ self.$btns[key] = $btn;
+ self.cast('REGISTER_COMMAND', [$btn[0], '', this]);
+ });
+
+ // layer, preview table
+ this.$layer = $layer = this.$btns.te.next('.lr').mousedown(function(event){ event.stopPropagation() });
+ this.$table = $layer.find('fieldset.pv table:first');
+
+ // caption setting
+ $fieldset = $layer.find('fieldset.cn');
+ this.$caption_txt = $fieldset.find('input:text')
+ .keydown(function(){ self.$table.find('>caption').text( this.value ); })
+ .focus(function(){ $(this).prev('label').hide(); })
+ .blur(function(){
+ if(!this.value) $(this).prev('label').show();
+ $(this).keydown();
+ });
+ this.$caption_pos = $fieldset.find('button')
+ .click(function(){
+ var $li = $(this[_pn_]), pos, align, $table, $caption;
+
+ $table = self.$table;
+ $caption = $table.find('>caption');
+
+ $li.parent().children('li').removeClass('selected').end().end().addClass('selected');
+
+ pos = self.parseCaptionPos($li.attr('class'));
+
+ (pos.vert == 'top')?$table.prepend($caption):$table.append($caption);
+ $table.css('caption-side', pos.vert);
+ $caption.css('text-align', pos.align);
+
+ return false;
+ });
+
+ // header setting
+ this.$th_btns = $layer.find('fieldset.th button').click(function(){ self.setHeader(this); return false; });
+
+ // px or percent
+ this.$unit_btns = $layer.find('fieldset.wh button').click(function(){
+ self.$unit_btns.removeClass('selected');
+ $(this).addClass('selected');
+ return false;
+ });
+
+ // apply, cancel
+ $btns = $layer.find('div.btnArea button');
+ $btns.eq(0).click(function(){
+ var cfg = {rows:3, cols:3, width:'100%', caption_text:'', caption_pos:'tc', header:'no'}, $fieldset, $input;
+
+ // get table properties
+ $fieldset = $layer.find('fieldset.cn');
+ cfg.caption_pos = $fieldset.find('li.selected').attr('class').replace(/ ?selected ?/, '');
+ cfg.caption_text = $fieldset.find('input:text').val();
+
+ cfg.header = $layer.find('fieldset.th li.selected').attr('class').replace(/ ?selected ?/, '');
+
+ $fieldset = $layer.find('fieldset.wh');
+ $input = $fieldset.find('input:text');
+ cfg.cols = $input.eq(0).val() - 0;
+ cfg.rows = $input.eq(1).val() - 0;
+ cfg.width = $input.eq(2).val() - 0 + $fieldset.find('button.selected').text();
+
+ self.cast('EXEC_TABLE', [cfg]);
+ self.cast('HIDE_TABLE_LAYER');
+
+ return false;
+ });
+ $btns.eq(1).click(function(){ self.cast('HIDE_TABLE_LAYER'); return false; });
+ },
+ deactivate : function() {
+ var self = this;
+
+ // buttons
+ if(this.$btns.te) this.$btns.te.unbind('mousedown');
+ $.each(this.$btns, function(key) {
+ self.cast('UNREGISTER_COMMAND', [this]);
+ });
+ this.$btns = {};
+
+ if (this.$layer) this.$layer.unbind().find('input,button').unbind();
+
+ },
+ parseCaptionPos : function(str) {
+ var ret = {vert:'', align:''};
+
+ str = $.trim(str.replace(/selected/g, ''));
+
+ ret.vert = (str.indexOf('b') < 0)?'top':'bottom';
+
+ if (str.indexOf('c') >= 0) ret.align = 'center';
+ else if (str.indexOf('r') >= 0) ret.align = 'right';
+ else ret.align = 'left';
+
+ return ret;
+ },
+ setHeader : function(btn) {
+ var $btn = $(btn), $li = $btn.parent('li'), $table = this.$table, selector;
+
+ if (!$btn.is('button') || !$li.length) return;
+
+ switch($li.attr('class')) {
+ case 'no': selector = ''; break;
+ case 'lt': selector = 'tr > td:nth-child(1)'; break;
+ case 'tp': selector = 'tr:first td'; break;
+ case 'bh': selector = 'tr:first td, tr > td:nth-child(1)'; break;
+ }
+
+ // conver td to th
+ $table.find('th').replaceWith('TD | ');
+ if (selector) $table.find(selector).replaceWith('TH | ');
+
+ // select this button
+ this.$th_btns.parent('li').removeClass('selected');
+ $li.addClass('selected');
+ },
+ /**
+ * @brief Insert a table with a configuration
+ * @param Object contains table properties
+ */
+ API_EXEC_TABLE : function(sender, params) {
+ var sel = this.selection, cfg, html, caption, $rich = this.oApp.$richedit;
+
+ if (!sel) return;
+
+ cfg = $.extend({
+ caption_pos : 'tc',
+ caption_text : '',
+ cols : 3,
+ rows : 3,
+ header : 'no',
+ width : '100%'
+ }, params[0]);
+
+ cfg.cp_side = (cfg.caption_pos.indexOf('t') > -1)?'top':'bottom';
+
+ if (cfg.caption_pos.indexOf('c') > -1) cfg.cp_align = 'center';
+ else if (cfg.caption_pos.indexOf('r') > -1) cfg.cp_align = 'right';
+ else cfg.cp_align = 'left';
+
+ if (cfg.caption_text) {
+ caption = ''+cfg.caption_text+'';
+ } else {
+ caption = '';
+ }
+
+ // create table code
+ html = '';
+ // top caption
+ if (cfg.cp_side == 'top') html += caption;
+
+ for(i=0; i < cfg.rows; i++) {
+
+ html += '';
+ for(j=0; j < cfg.cols; j++) {
+ if ( (cfg.header == 'bh' && !(i*j)) || (cfg.header == 'tp' && !i) || (cfg.header == 'lt' && !j)) {
+ html += '| TH | ';
+ } else {
+ html += 'TD | ';
+ }
+ }
+ html += '
';
+ }
+
+ // bottom caption
+ if (cfg.cp_side == 'bottom') html += caption;
+
+ html += '
';
+
+ // insert table
+ $rich.focus();
+ sel.pasteHTML(html);
+ sel.collapseToEnd();
+ },
+ API_SHOW_TABLE_LAYER : function(sender, params) {
+ var $layer = this.$layer;
+
+ if (!$layer || $layer.hasClass('open')) return;
+
+ this.selection = this.oApp.getSelection();
+ $layer.addClass('open').parent('li').addClass('active');
+ },
+ API_HIDE_TABLE_LAYER : function(sender, params) {
+ var $layer = this.$layer;
+
+ if (!$layer || !$layer.hasClass('open')) return;
+ if (this.selection) this.selection.select();
+
+ this.selection = null;
+ $layer.removeClass('open').parent('li').removeClass('active');
+ },
+ API_TOGGLE_TABLE_LAYER : function(sender, params) {
+ if (!this.$layer) return;
+ this.cast( (this.$layer.hasClass('open')?'HIDE':'SHOW') + '_TABLE_LAYER' );
+ },
+ API_HIDE_ALL_LAYER : function(sender, params) {
+ if (sender != this) this.cast('HIDE_TABLE_LAYER');
+ },
+ API_MERGE_CELLS : function(sender, params) {
+ var self = this, html = '', $cell = this.oApp.$richedit.find(this.cell_selector);
+
+ // if no selected cell then quit
+ if (!cell.length) return;
+
+ // merge content of all cells
+ $cell.each(function(){ html += this.innerHTML; }).eq(0).html(html);
+
+ // save undo point
+ this.cast('SAVE_UNDO_POINT');
+ },
+ API_SPLIT_BY_COL : function(sender, params) {
+ var cell = this.oApp.$richedit.find(this.cell_selector);
+
+ if (!$cell.length) $cell = this.$current_cell;
+
+ // save undo point
+ this.cast('SAVE_UNDO_POINT');
+ },
+ API_SPLIT_BY_ROW : function(sender, params) {
+ var $cell = this.oApp.$richedit.find(this.cell_selector);
+
+ if (!$cell.length) $cell = this.$current_cell;
+
+ // save undo point
+ this.cast('SAVE_UNDO_POINT');
+ }/*,
+ API_ON_CHANGE_NODE : function(sender, params) {
+ var self = this, node = params[0], $node, state;
+
+ state = {
+ cs : 'disable',
+ rs : 'disable',
+ cm : 'disable'
+ };
+
+ if (node) {
+ sel = this.oApp.getSelection();
+
+ if (sel.collapsed) {
+ $node = $(node).parentsUntil('.'+_xr_).filter('td,th');
+
+ if ($node.length) {
+ state.cs = state.rs = 'normal';
+ if ($node.is('.selected')) state.cm = 'normal';
+ }
+ }
+ }
+
+ $.each(this.$btns, function(key){
+ self.cast('SET_COMMAND_STATE', [this[0], state[key]]);
+ });
+ }
+ */
+});
+/**
+ * }}}
+ */
+
+/**
+ * {{{ AutoSave
+ */
+AutoSave = xe.createPlugin('AutoSave', {
+ _enable : false,
+ $bar : null,
+ $text : null,
+
+ init : function(){ },
+ activate : function() {
+ var app = this.oApp;
+
+ // set default option
+ app.setDefault('use_autosave', false);
+ this._enable = app.getOption('use_autosave');
+
+ this.$bar = this.oApp.$root.find('div.time').hide();
+ },
+ deactivate : function() {
+ },
+ API_EXEC_AUTOSAVE : function() {
+ },
+ API_ENABLE_AUTOSAVE : function(sender, params) {
+ }
+});
+/**
+ * }}}
+ */
+
+/**
+ * {{{ Clear
+ */
+Clear = xe.createPlugin('Clear', {
+ $btn : null,
+ _keypress : null,
+ _mousemove : null,
+ init : function(){ },
+ activate : function() {
+ var self = this, app = this.oApp;
+
+ this.$btn = this.oApp.$toolbar.find('button.er');
+ if (this.$btn.length) {
+ this.cast('REGISTER_COMMAND', [this.$btn[0], '', 'EXEC_CLEAR']);
+ }
+ },
+ deactivate : function() {
+ if (this.$btn.length) {
+ this.cast('UNREGISTER_COMMAND', [this.$btn[0], '']);
+ }
+ },
+ API_EXEC_CLEAR : function() {
+ var sel = this.oApp.getSelection(), $div, node, par, content, after;
+
+ if (!sel || sel.collapsed) return;
+
+ // get content
+ content = sel.extractContents();
+ sel.select();
+
+ // TODO : get nearest parent
+ node = sel.startContainer;
+ par = null;
+ while(par = node[_pn_]) {
+ if (rx_block.test(par.nodeName) || rx_root.test(par.className)) {
+ break;
+ }
+
+ node = par;
+ }
+
+ $div = $('').appendTo(document).append(content);
+ content = $div.remove().text();
+
+ sel.setEndAfter(par.lastChild);
+ after = sel.extractContents();
+
+ content = document.createTextNode(content);
+ par.appendChild(content);
+ par.appendChild(after);
+ }
+});
+/**
+ * }}}
+ */
+
+/**
+ * {{{ Find
+ */
+FindReplace = xe.createPlugin('FindReplace', {
+ init : function() {
+ },
+ activate : function() {
+ },
+ deactivate : function() {
+ },
+ API_EXEC_FIND : function() {
+ },
+ API_SHOW_FINDREPLACE_LAYER : function() {
+ },
+ API_SHOW_FINDREPLACE_LAYER : function() {
+ },
+ API_SHOW_FINDREPLACE_LAYER : function() {
+ },
+ API_HIDE_ALL_LAYER : function() {
+ }
+});
+/**
+ * }}}
+ */
+
+/**
+ * {{{ XHTML Transitional scheme
+ * This code is from the tinyMCE project.
+ */
+var XHTMLT = {};
+(function(){
+ function unpack(lookup, data) {
+ function replace(value) {
+ return value.replace(/[A-Z]+/g, function(key) {
+ return replace(lookup[key]);
+ });
+ };
+
+ // Unpack lookup
+ $.each(lookup, function(key){ lookup[key] = replace(this) });
+
+ // Unpack and parse data into object map
+ replace(data).replace(/#/g, '#text').replace(/(\w+)\[([^\]]+)\]/g, function(str, name, children) {
+ var i, map = {};
+
+ children = children.split(/\|/);
+
+ for (i = children.length - 1; i >= 0; i--)
+ map[children[i]] = 1;
+
+ XHTMLT[name] = map;
+ });
+ };
+
+ // This is the XHTML 1.0 transitional elements with it's children packed to reduce it's size
+ // we will later include the attributes here and use it as a default for valid elements but it
+ // requires us to rewrite the serializer engine
+ unpack({
+ Z : '#|H|K|N|O|P',
+ Y : '#|X|form|R|Q',
+ X : 'p|T|div|U|W|isindex|fieldset|table',
+ W : 'pre|hr|blockquote|address|center|noframes',
+ U : 'ul|ol|dl|menu|dir',
+ ZC : '#|p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q',
+ T : 'h1|h2|h3|h4|h5|h6',
+ ZB : '#|X|S|Q',
+ S : 'R|P',
+ ZA : '#|a|G|J|M|O|P',
+ R : '#|a|H|K|N|O',
+ Q : 'noscript|P',
+ P : 'ins|del|script',
+ O : 'input|select|textarea|label|button',
+ N : 'M|L',
+ M : 'em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym',
+ L : 'sub|sup',
+ K : 'J|I',
+ J : 'tt|i|b|u|s|strike',
+ I : 'big|small|font|basefont',
+ H : 'G|F',
+ G : 'br|span|bdo',
+ F : 'object|applet|img|map|iframe'
+ }, 'script[]' +
+ 'style[]' +
+ 'object[#|param|X|form|a|H|K|N|O|Q]' +
+ 'param[]' +
+ 'p[S]' +
+ 'a[Z]' +
+ 'br[]' +
+ 'span[S]' +
+ 'bdo[S]' +
+ 'applet[#|param|X|form|a|H|K|N|O|Q]' +
+ 'h1[S]' +
+ 'img[]' +
+ 'map[X|form|Q|area]' +
+ 'h2[S]' +
+ 'iframe[#|X|form|a|H|K|N|O|Q]' +
+ 'h3[S]' +
+ 'tt[S]' +
+ 'i[S]' +
+ 'b[S]' +
+ 'u[S]' +
+ 's[S]' +
+ 'strike[S]' +
+ 'big[S]' +
+ 'small[S]' +
+ 'font[S]' +
+ 'basefont[]' +
+ 'em[S]' +
+ 'strong[S]' +
+ 'dfn[S]' +
+ 'code[S]' +
+ 'q[S]' +
+ 'samp[S]' +
+ 'kbd[S]' +
+ 'var[S]' +
+ 'cite[S]' +
+ 'abbr[S]' +
+ 'acronym[S]' +
+ 'sub[S]' +
+ 'sup[S]' +
+ 'input[]' +
+ 'select[optgroup|option]' +
+ 'optgroup[option]' +
+ 'option[]' +
+ 'textarea[]' +
+ 'label[S]' +
+ 'button[#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]' +
+ 'h4[S]' +
+ 'ins[#|X|form|a|H|K|N|O|Q]' +
+ 'h5[S]' +
+ 'del[#|X|form|a|H|K|N|O|Q]' +
+ 'h6[S]' +
+ 'div[#|X|form|a|H|K|N|O|Q]' +
+ 'ul[li]' +
+ 'li[#|X|form|a|H|K|N|O|Q]' +
+ 'ol[li]' +
+ 'dl[dt|dd]' +
+ 'dt[S]' +
+ 'dd[#|X|form|a|H|K|N|O|Q]' +
+ 'menu[li]' +
+ 'dir[li]' +
+ 'pre[ZA]' +
+ 'hr[]' +
+ 'blockquote[#|X|form|a|H|K|N|O|Q]' +
+ 'address[S|p]' +
+ 'center[#|X|form|a|H|K|N|O|Q]' +
+ 'noframes[#|X|form|a|H|K|N|O|Q]' +
+ 'isindex[]' +
+ 'fieldset[#|legend|X|form|a|H|K|N|O|Q]' +
+ 'legend[S]' +
+ 'table[caption|col|colgroup|thead|tfoot|tbody|tr]' +
+ 'caption[S]' +
+ 'col[]' +
+ 'colgroup[col]' +
+ 'thead[tr]' +
+ 'tr[th|td]' +
+ 'th[#|X|form|a|H|K|N|O|Q]' +
+ 'form[#|X|a|H|K|N|O|Q]' +
+ 'noscript[#|X|form|a|H|K|N|O|Q]' +
+ 'td[#|X|form|a|H|K|N|O|Q]' +
+ 'tfoot[tr]' +
+ 'tbody[tr]' +
+ 'area[]' +
+ 'base[]' +
+ 'body[#|X|form|a|H|K|N|O|Q]'
+ );
+})();
+/**
+ * }}}
+ */
+
+/**
+ * {{{ Selection and Range Control class
+ * This code is a modified version of HuskyRange, the SmartEditor project.
+ */
+
+/**
+ * {{{ @class W3CDOMRange
+ * @brief A cross-browser implementation of W3C's DOM Range
+ */
+function W3CDOMRange(){ this.init(); };
+$.extend(W3CDOMRange.prototype, {
+ init : function() {
+ this.collapsed = true;
+ this[_ca_] = d.body;
+ this[_ec_] = d.body;
+ this[_eo_] = 0;
+ this[_sc_] = d.body;
+ this[_so_] = 0;
+ },
+ cloneContents : function(){
+ var oClonedContents = d.createDocumentFragment();
+ var oTmpContainer = d.createDocumentFragment();
+
+ var aNodes = this._getNodesInRange();
+
+ if(aNodes.length < 1) return oClonedContents;
+
+ var oClonedContainers = this._constructClonedTree(aNodes, oTmpContainer);
+
+ // oTopContainer = aNodes[aNodes.length-1].parentNode and this is not part of the initial array and only those child nodes should be cloned
+ var oTopContainer = oTmpContainer.firstChild;
+
+ if(oTopContainer){
+ var elCurNode = oTopContainer.firstChild, elNextNode;
+
+ while(elCurNode){
+ elNextNode = elCurNode[_ns_];
+ oClonedContents.appendChild(elCurNode);
+ elCurNode = elNextNode;
+ }
+ }
+
+ oClonedContainers = this._splitTextEndNodes({oStartContainer: oClonedContainers[_osc_], iStartOffset: this[_so_],
+ oEndContainer: oClonedContainers[_oec_], iEndOffset: this[_eo_]});
+
+ if(oClonedContainers[_osc_] && oClonedContainers[_osc_][_ps_])
+ dp(oClonedContainers[_osc_]).removeChild(oClonedContainers[_osc_][_ps_]);
+
+ if(oClonedContainers[_oec_] && oClonedContainers[_oec_][_ns_])
+ dp(oClonedContainers[_oec_]).removeChild(oClonedContainers[_oec_][_ns_]);
+
+ return oClonedContents;
+ },
+
+ _constructClonedTree : function(aNodes, oClonedParentNode){
+ var oClonedStartContainer = null;
+ var oClonedEndContainer = null;
+
+ var oStartContainer = this[_sc_];
+ var oEndContainer = this[_ec_];
+
+ _recurConstructClonedTree = function(aAllNodes, iCurIdx, oParentNode, oClonedParentNode){
+
+ if(iCurIdx < 0) return iCurIdx;
+
+ var iChildIdx = iCurIdx-1;
+
+ var oCurNodeCloneWithChildren = aAllNodes[iCurIdx].cloneNode(false);
+
+ if(aAllNodes[iCurIdx] == oStartContainer) oClonedStartContainer = oCurNodeCloneWithChildren;
+ if(aAllNodes[iCurIdx] == oEndContainer) oClonedEndContainer = oCurNodeCloneWithChildren;
+
+ while(iChildIdx >= 0 && dp(aAllNodes[iChildIdx]) == aAllNodes[iCurIdx]){
+ iChildIdx = this._recurConstructClonedTree(aAllNodes, iChildIdx, aAllNodes[iCurIdx], oCurNodeCloneWithChildren, oClonedStartContainer, oClonedEndContainer);
+ }
+
+ // this may trigger an error message in IE when an erroneous script is inserted
+ oClonedParentNode.insertBefore(oCurNodeCloneWithChildren, oClonedParentNode.firstChild);
+
+ return iChildIdx;
+ };
+
+ aNodes[aNodes.length] = dp(aNodes[aNodes.length-1]);
+ _recurConstructClonedTree(aNodes, aNodes.length-1, aNodes[aNodes.length-1], oClonedParentNode);
+
+ return {oStartContainer: oClonedStartContainer, oEndContainer: oClonedEndContainer};
+ },
+
+ cloneRange : function(){
+ return this._copyRange(new W3CDOMRange(d));
+ },
+
+ _copyRange : function(oClonedRange){
+ oClonedRange.collapsed = this.collapsed;
+ oClonedRange[_ca_] = this[_ca_];
+ oClonedRange[_ec_] = this[_ec_];
+ oClonedRange[_eo_] = this[_eo_];
+ oClonedRange[_sc_] = this[_sc_];
+ oClonedRange[_so_] = this[_so_];
+ oClonedRange._document = d;
+
+ return oClonedRange;
+ },
+
+ collapse : function(toStart){
+ if(toStart){
+ this[_ec_] = this[_sc_];
+ this[_eo_] = this[_so_];
+ }else{
+ this[_sc_] = this[_ec_];
+ this[_so_] = this[_eo_];
+ }
+
+ this._updateRangeInfo();
+ },
+
+ compareBoundaryPoints : function(how, sourceRange){
+ switch(how){
+ case W3CDOMRange.START_TO_START:
+ return this._compareEndPoint(this[_sc_], this[_so_], sourceRange[_sc_], sourceRange[_so_]);
+ case W3CDOMRange.START_TO_END:
+ return this._compareEndPoint(this[_ec_], this[_eo_], sourceRange[_sc_], sourceRange[_so_]);
+ case W3CDOMRange.END_TO_END:
+ return this._compareEndPoint(this[_ec_], this[_eo_], sourceRange[_ec_], sourceRange[_eo_]);
+ case W3CDOMRange.END_TO_START:
+ return this._compareEndPoint(this[_sc_], this[_so_], sourceRange[_ec_], sourceRange[_eo_]);
+ }
+ },
+
+ _findBody : function(oNode){
+ if(!oNode) return null;
+ while(oNode){
+ if(oNode[_nn_].toUpperCase() == "BODY") return oNode;
+ oNode = dp(oNode);
+ }
+ return null;
+ },
+
+ _compareEndPoint : function(oContainerA, iOffsetA, oContainerB, iOffsetB){
+ var iIdxA, iIdxB;
+
+ if(!oContainerA || this._findBody(oContainerA) != d.body){
+ oContainerA = d.body;
+ iOffsetA = 0;
+ }
+
+ if(!oContainerB || this._findBody(oContainerB) != d.body){
+ oContainerB = d.body;
+ iOffsetB = 0;
+ }
+
+ var compareIdx = function(iIdxA, iIdxB){
+ // iIdxX == -1 when the node is the commonAncestorNode
+ // if iIdxA == -1
+ // -> [[...]]...
+ // if iIdxB == -1
+ // -> ...[[...]]
+ if(iIdxB == -1) iIdxB = iIdxA+1;
+ if(iIdxA < iIdxB) return -1;
+ if(iIdxA == iIdxB) return 0;
+ return 1;
+ };
+
+ var oCommonAncestor = this._getCommonAncestorContainer(oContainerA, oContainerB);
+
+ // ================================================================================================================================================
+ // Move up both containers so that both containers are direct child nodes of the common ancestor node. From there, just compare the offset
+ // Add 0.5 for each contaienrs that has "moved up" since the actual node is wrapped by 1 or more parent nodes and therefore its position is somewhere between idx & idx+1
+ // NODE1NODE2
NODE3
+ // The position of NODE2 in COMMON_ANCESTOR is somewhere between after NODE1(idx1) and before NODE3(idx2), so we let that be 1.5
+
+ // container node A in common ancestor container
+ var oNodeA = oContainerA;
+ if(oNodeA != oCommonAncestor){
+ while((oTmpNode = dp(oNodeA)) != oCommonAncestor){oNodeA = oTmpNode;}
+
+ iIdxA = this._getPosIdx(oNodeA)+0.5;
+ }else iIdxA = iOffsetA;
+
+ // container node B in common ancestor container
+ var oNodeB = oContainerB;
+ if(oNodeB != oCommonAncestor){
+ while((oTmpNode = dp(oNodeB)) != oCommonAncestor){oNodeB = oTmpNode;}
+
+ iIdxB = this._getPosIdx(oNodeB)+0.5;
+ }else iIdxB = iOffsetB;
+
+ return compareIdx(iIdxA, iIdxB);
+ },
+
+ _getCommonAncestorContainer : function(oNode1, oNode2){
+ var oComparingNode = oNode2;
+
+ while(oNode1){
+ while(oComparingNode){
+ if(oNode1 == oComparingNode) return oNode1;
+ oComparingNode = dp(oComparingNode);
+ }
+ oComparingNode = oNode2;
+ oNode1 = dp(oNode1);
+ }
+
+ return d.body;
+ },
+
+ deleteContents : function(){
+ if(this.collapsed) return;
+
+ this._splitTextEndNodesOfTheRange();
+
+ var aNodes = this._getNodesInRange();
+
+ if(aNodes.length < 1) return;
+
+ var oPrevNode = aNodes[0][_ps_];
+ while(oPrevNode && this._isBlankTextNode(oPrevNode)) oPrevNode = oPrevNode[_ps_];
+
+ var oNewStartContainer, iNewOffset;
+ if(!oPrevNode){
+ oNewStartContainer = dp(aNodes[0]);
+ iNewOffset = 0;
+ }
+
+ for(var i=0; i oNode.nodeValue.length) iOffset = oNode.nodeValue.length;
+ }else{
+ if(iOffset > dc(oNode).length) iOffset = dc(oNode).length;
+ }
+
+ return iOffset;
+ },
+
+
+ setEnd : function(refNode, offset){
+ offset = this._endsNodeValidation(refNode, offset);
+
+ this[_ec_] = refNode;
+ this[_eo_] = offset;
+ if(!this[_sc_] || this._compareEndPoint(this[_sc_], this[_so_], this[_ec_], this[_eo_]) != -1) this.collapse(false);
+
+ this._updateRangeInfo();
+ },
+
+ setEndAfter : function(refNode){
+ if(!refNode) throw new Error("INVALID_NODE_TYPE_ERR in setEndAfter");
+
+ if(refNode.tagName == "BODY"){
+ this.setEnd(refNode, dc(refNode).length);
+ return;
+ }
+ this.setEnd(dp(refNode), this._getPosIdx(refNode)+1);
+ },
+
+ setEndBefore : function(refNode){
+ if(!refNode) throw new Error("INVALID_NODE_TYPE_ERR in setEndBefore");
+
+ if(refNode.tagName == "BODY"){
+ this.setEnd(refNode, 0);
+ return;
+ }
+
+ this.setEnd(dp(refNode), this._getPosIdx(refNode));
+ },
+
+ setStart : function(refNode, offset){
+ offset = this._endsNodeValidation(refNode, offset);
+
+ this[_sc_] = refNode;
+ this[_so_] = offset;
+
+ if(!this[_ec_] || this._compareEndPoint(this[_sc_], this[_so_], this[_ec_], this[_eo_]) != -1) this.collapse(true);
+ this._updateRangeInfo();
+ },
+
+ setStartAfter : function(refNode){
+ if(!refNode) throw new Error("INVALID_NODE_TYPE_ERR in setStartAfter");
+
+ if(refNode.tagName == "BODY"){
+ this.setStart(refNode, dc(refNode).length);
+ return;
+ }
+
+ this.setStart(dp(refNode), this._getPosIdx(refNode)+1);
+ },
+
+ setStartBefore : function(refNode){
+ if(!refNode) throw new Error("INVALID_NODE_TYPE_ERR in setStartBefore");
+
+ if(refNode.tagName == "BODY"){
+ this.setStart(refNode, 0);
+ return;
+ }
+ this.setStart(dp(refNode), this._getPosIdx(refNode));
+ },
+
+ surroundContents : function(newParent){
+ newParent.appendChild(this.extractContents());
+ this.insertNode(newParent);
+ this.selectNode(newParent);
+ },
+
+ toString : function(){
+ return $('').append(this.cloneContents()).text();
+ },
+
+ _isBlankTextNode : function(oNode){
+ if(oNode[_nt_] == 3 && oNode.nodeValue == "") return true;
+ return false;
+ },
+
+ _getPosIdx : function(refNode){
+ var idx = 0;
+ for(var node = refNode[_ps_]; node; node = node[_ps_]) idx++;
+
+ return idx;
+ },
+
+ _updateRangeInfo : function(){
+ if(!this[_sc_]){
+ this.init();
+ return;
+ }
+
+ this.collapsed = this._isCollapsed(this[_sc_], this[_so_], this[_ec_], this[_eo_]);
+
+ this[_ca_] = this._getCommonAncestorContainer(this[_sc_], this[_ec_]);
+ },
+
+ _isCollapsed : function(oStartContainer, iStartOffset, oEndContainer, iEndOffset){
+ var bCollapsed = false;
+
+ if(oStartContainer == oEndContainer && iStartOffset == iEndOffset){
+ bCollapsed = true;
+ }else{
+ var oActualStartNode = this._getActualStartNode(oStartContainer, iStartOffset);
+ var oActualEndNode = this._getActualEndNode(oEndContainer, iEndOffset);
+
+ // Take the parent nodes on the same level for easier comparison when they're next to each other
+ // eg) From
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ // , it's easier to compare the position of B and D rather than C and F because they are siblings
+ //
+ // If the range were collapsed, oActualEndNode will precede oActualStartNode by doing this
+ oActualStartNode = this._getNextNode(this._getPrevNode(oActualStartNode));
+ oActualEndNode = this._getPrevNode(this._getNextNode(oActualEndNode));
+
+ if(oActualStartNode && oActualEndNode && oActualEndNode.tagName != "BODY" &&
+ (this._getNextNode(oActualEndNode) == oActualStartNode || (oActualEndNode == oActualStartNode && this._isBlankTextNode(oActualEndNode)))
+ )
+ bCollapsed = true;
+ }
+
+ return bCollapsed;
+ },
+
+ _splitTextEndNodesOfTheRange : function(){
+ var oEndPoints = this._splitTextEndNodes({oStartContainer: this[_sc_], iStartOffset: this[_so_],
+ oEndContainer: this[_ec_], iEndOffset: this[_eo_]});
+
+ this[_sc_] = oEndPoints[_osc_];
+ this[_so_] = oEndPoints[_iso_];
+
+ this[_ec_] = oEndPoints[_oec_];
+ this[_eo_] = oEndPoints[_ieo_];
+ },
+
+ _splitTextEndNodes : function(oEndPoints){
+ oEndPoints = this._splitStartTextNode(oEndPoints);
+ oEndPoints = this._splitEndTextNode(oEndPoints);
+
+ return oEndPoints;
+ },
+
+ _splitStartTextNode : function(oEndPoints){
+ var oStartContainer = oEndPoints[_osc_];
+ var iStartOffset = oEndPoints[_iso_];
+
+ var oEndContainer = oEndPoints[_oec_];
+ var iEndOffset = oEndPoints[_ieo_];
+
+ if(!oStartContainer) return oEndPoints;
+ if(oStartContainer[_nt_] != 3) return oEndPoints;
+ if(iStartOffset == 0) return oEndPoints;
+
+ if(oStartContainer.nodeValue.length <= iStartOffset) return oEndPoints;
+
+ var oLastPart = oStartContainer.splitText(iStartOffset);
+
+ if(oStartContainer == oEndContainer){
+ iEndOffset -= iStartOffset;
+ oEndContainer = oLastPart;
+ }
+ oStartContainer = oLastPart;
+ iStartOffset = 0;
+
+ return {oStartContainer: oStartContainer, iStartOffset: iStartOffset, oEndContainer: oEndContainer, iEndOffset: iEndOffset};
+ },
+
+ _splitEndTextNode : function(oEndPoints){
+ var oStartContainer = oEndPoints[_osc_];
+ var iStartOffset = oEndPoints[_iso_];
+
+ var oEndContainer = oEndPoints[_oec_];
+ var iEndOffset = oEndPoints[_ieo_];
+
+ if(!oEndContainer) return oEndPoints;
+ if(oEndContainer[_nt_] != 3) return oEndPoints;
+
+ if(iEndOffset >= oEndContainer.nodeValue.length) return oEndPoints;
+ if(iEndOffset == 0) return oEndPoints;
+
+ oEndContainer.splitText(iEndOffset);
+
+ return {oStartContainer: oStartContainer, iStartOffset: iStartOffset, oEndContainer: oEndContainer, iEndOffset: iEndOffset};
+ },
+
+ _getNodesInRange : function(){
+ if(this.collapsed) return [];
+
+ var oStartNode = this._getActualStartNode(this[_sc_], this[_so_]);
+ var oEndNode = this._getActualEndNode(this[_ec_], this[_eo_]);
+
+ return this._getNodesBetween(oStartNode, oEndNode);
+ },
+
+ _getActualStartNode : function(oStartContainer, iStartOffset){
+ var oStartNode = oStartContainer;;
+
+ if(oStartContainer[_nt_] == 3){
+ if(iStartOffset >= oStartContainer.nodeValue.length){
+ oStartNode = this._getNextNode(oStartContainer);
+ if(oStartNode.tagName == "BODY") oStartNode = null;
+ }else{
+ oStartNode = oStartContainer;
+ }
+ }else{
+ if(iStartOffset < dc(oStartContainer).length){
+ oStartNode = dc(oStartContainer)[iStartOffset];
+ }else{
+ oStartNode = this._getNextNode(oStartContainer);
+ if(oStartNode.tagName == "BODY") oStartNode = null;
+ }
+ }
+
+ return oStartNode;
+ },
+
+ _getActualEndNode : function(oEndContainer, iEndOffset){
+ var oEndNode = oEndContainer;
+
+ if(iEndOffset == 0){
+ oEndNode = this._getPrevNode(oEndContainer);
+ if(oEndNode.tagName == "BODY") oEndNode = null;
+ }else if(oEndContainer[_nt_] == 3){
+ oEndNode = oEndContainer;
+ }else{
+ oEndNode = dc(oEndContainer)[iEndOffset-1];
+ }
+
+ return oEndNode;
+ },
+
+ _getNextNode : function(oNode){
+ if(!oNode || oNode.tagName == "BODY") return d.body;
+
+ if(oNode[_ns_]) return oNode[_ns_];
+
+ return this._getNextNode(dp(oNode));
+ },
+
+ _getPrevNode : function(oNode){
+ if(!oNode || oNode.tagName == "BODY") return d.body;
+
+ if(oNode[_ps_]) return oNode[_ps_];
+
+ return this._getPrevNode(dp(oNode));
+ },
+
+ // includes partially selected
+ // for , _getNodesBetween(b, c) will yield to b, "a" and c
+ _getNodesBetween : function(oStartNode, oEndNode){
+ var aNodesBetween = [];
+ this._nNodesBetweenLen = 0;
+
+ if(!oStartNode || !oEndNode) return aNodesBetween;
+
+ this._recurGetNextNodesUntil(oStartNode, oEndNode, aNodesBetween);
+ return aNodesBetween;
+ },
+
+ _recurGetNextNodesUntil : function(oNode, oEndNode, aNodesBetween){
+ if(!oNode) return false;
+
+ if(!this._recurGetChildNodesUntil(oNode, oEndNode, aNodesBetween)) return false;
+
+ var oNextToChk = oNode[_ns_];
+
+ while(!oNextToChk){
+ if(!(oNode = dp(oNode))) return false;
+
+ aNodesBetween[this._nNodesBetweenLen++] = oNode;
+
+ if(oNode == oEndNode) return false;
+
+ oNextToChk = oNode[_ns_];
+ }
+
+ return this._recurGetNextNodesUntil(oNextToChk, oEndNode, aNodesBetween);
+ },
+
+ _recurGetChildNodesUntil : function(oNode, oEndNode, aNodesBetween){
+ if(!oNode) return false;
+
+ var bEndFound = false;
+ var oCurNode = oNode;
+ if(oCurNode.firstChild){
+ oCurNode = oCurNode.firstChild;
+ while(oCurNode){
+ if(!this._recurGetChildNodesUntil(oCurNode, oEndNode, aNodesBetween)){
+ bEndFound = true;
+ break;
+ }
+ oCurNode = oCurNode[_ns_];
+ }
+ }
+
+ aNodesBetween[this._nNodesBetweenLen++] = oNode;
+
+ if(bEndFound) return false;
+ if(oNode == oEndNode) return false;
+
+ return true;
+ }
+});
+
+W3CDOMRange.START_TO_START = 0;
+W3CDOMRange.START_TO_END = 1;
+W3CDOMRange.END_TO_END = 2;
+W3CDOMRange.END_TO_START = 3;
+/**
+ * }}} W3CDOMRange
+ */
+
+/**
+ * {{{ @class HuskyRange
+ * @brief A cross-browser function that implements all of the W3C's DOM Range specification and some more
+ */
+var
+ HUSKY_BOOKMARK_START_ID_PREFIX = "husky_bookmark_start_",
+ HUSKY_BOOKMARK_END_ID_PREFIX = "husky_bookmark_end_",
+ rxLineBreaker = new RegExp("^("+this.sBlockElement+"|"+this.sBlockContainer+")$");
+
+function HuskyRange(){ this.init(); }
+$.extend(HuskyRange.prototype, W3CDOMRange.prototype, {
+ init : function(){
+ this.oSimpleSelection = new SimpleSelection();
+ this.selectionLoaded = this.oSimpleSelection.selectionLoaded;
+
+ W3CDOMRange.prototype.init.apply(this);
+ },
+
+ select : function(){
+ this.oSimpleSelection.selectRange(this);
+ },
+
+ setFromSelection : function(iNum){
+ this.setRange(this.oSimpleSelection.getRangeAt(iNum));
+ },
+
+ setRange : function(oW3CRange){
+ this.setStart(oW3CRange[_sc_], oW3CRange[_so_]);
+ this.setEnd(oW3CRange[_ec_], oW3CRange[_eo_]);
+ },
+
+ setEndNodes : function(oSNode, oENode){
+ this.setEndAfter(oENode);
+ this.setStartBefore(oSNode);
+ },
+
+ splitTextAtBothEnds : function(){
+ this._splitTextEndNodesOfTheRange();
+ },
+
+ getStartNode : function(){
+ if(this.collapsed){
+ if(this[_sc_][_nt_] == 3){
+ if(this[_so_] == 0) return this[_sc_];
+ if(this[_sc_].nodeValue.length <= this[_so_]) return null;
+ return this[_sc_];
+ }
+ return null;
+ }
+
+ if(this[_sc_][_nt_] == 3){
+ if(this[_so_] >= this[_sc_].nodeValue.length) return this._getNextNode(this[_sc_]);
+ return this[_sc_];
+ }else{
+ if(this[_so_] >= dc(this[_sc_]).length) return this._getNextNode(this[_sc_]);
+ return dc(this[_sc_])[this[_so_]];
+ }
+ },
+
+ getEndNode : function(){
+ if(this.collapsed) return this.getStartNode();
+
+ if(this[_ec_][_nt_] == 3){
+ if(this[_eo_] == 0) return this._getPrevNode(this[_ec_]);
+ return this[_ec_];
+ }else{
+ if(this[_eo_] == 0) return this._getPrevNode(this[_ec_]);
+ return dc(this[_ec_])[this[_eo_]-1];
+ }
+ },
+
+ getNodeAroundRange : function(bBefore, bStrict){
+ if(this.collapsed && this[_sc_] && this[_sc_][_nt_] == 3) return this[_sc_];
+ if(!this.collapsed || (this[_sc_] && this[_sc_][_nt_] == 3)) return this.getStartNode();
+
+ var oBeforeRange, oAfterRange, oResult;
+
+ if(this[_so_] >= dc(this[_sc_]).length)
+ oAfterRange = this._getNextNode(this[_sc_]);
+ else
+ oAfterRange = dc(this[_sc_])[this[_so_]];
+
+ if(this[_eo_] == 0)
+ oBeforeRange = this._getPrevNode(this[_ec_]);
+ else
+ oBeforeRange = dc(this[_ec_])[this[_eo_]-1];
+
+ if(bBefore){
+ oResult = oBeforeRange;
+ if(!oResult && !bStrict) oResult = oAfterRange;
+ }else{
+ oResult = oAfterRange;
+ if(!oResult && !bStrict) oResult = oBeforeRange;
+ }
+
+ return oResult;
+ },
+
+ _getXPath : function(elNode){
+ var sXPath = "";
+
+ while(elNode && elNode[_nt_] == 1){
+ sXPath = "/" + elNode.tagName+"["+this._getPosIdx4XPath(elNode)+"]" + sXPath;
+ elNode = dp(elNode);
+ }
+
+ return sXPath;
+ },
+
+ _getPosIdx4XPath : function(refNode){
+ var idx = 0;
+ for(var node = refNode[_ps_]; node; node = node[_ps_])
+ if(node.tagName == refNode.tagName) idx++;
+
+ return idx;
+ },
+
+ // this was written specifically for XPath Bookmark and it may not perform correctly for general purposes
+ _evaluateXPath : function(sXPath, oDoc){
+ sXPath = sXPath.substring(1, sXPath.length-1);
+ var aXPath = sXPath.split(/\//);
+ var elNode = oDoc.body;
+
+ for(var i=2; i -1 && elContainer){
+ var aChildNodes = dc(elContainer);
+ var elNode = null;
+
+ var nIdx = nTextNodeIdx;
+ var nOffsetLeft = nOffset;
+
+ while((elNode = aChildNodes[nIdx]) && elNode[_nt_] == 3 && elNode.nodeValue.length < nOffsetLeft){
+ nOffsetLeft -= elNode.nodeValue.length;
+ nIdx++;
+ }
+
+ elContainer = dc(elContainer)[nIdx];
+ nOffset = nOffsetLeft;
+ }
+
+ if(!elContainer){
+ elContainer = d.body;
+ nOffset = 0;
+ }
+ return {elContainer: elContainer, nOffset: nOffset};
+ },
+
+ // this was written specifically for XPath Bookmark and it may not perform correctly for general purposes
+ getXPathBookmark : function(){
+ var nTextNodeIdx1 = -1;
+ var htEndPt1 = {elContainer: this[_sc_], nOffset: this[_so_]};
+ var elNode1 = this[_sc_];
+ if(elNode1[_nt_] == 3){
+ htEndPt1 = this._getFixedStartTextNode();
+ nTextNodeIdx1 = this._getPosIdx(htEndPt1.elContainer);
+ elNode1 = dp(elNode1);
+ }
+ var sXPathNode1 = this._getXPath(elNode1);
+ var oBookmark1 = {sXPath:sXPathNode1, nTextNodeIdx:nTextNodeIdx1, nOffset: htEndPt1.nOffset};
+
+ var nTextNodeIdx2 = -1;
+ var htEndPt2 = {elContainer: this[_ec_], nOffset: this[_eo_]};
+ var elNode2 = this[_ec_];
+ if(elNode2[_nt_] == 3){
+ htEndPt2 = this._getFixedEndTextNode();
+ nTextNodeIdx2 = this._getPosIdx(htEndPt2.elContainer);
+ elNode2 = dp(elNode2);
+ }
+ var sXPathNode2 = this._getXPath(elNode2);
+ var oBookmark2 = {sXPath:sXPathNode2, nTextNodeIdx:nTextNodeIdx2, nOffset: htEndPt2.nOffset};
+
+ return [oBookmark1, oBookmark2];
+ },
+
+ moveToXPathBookmark : function(aBookmark){
+ if(!aBookmark) return;
+
+ var oBookmarkInfo1 = this._evaluateXPathBookmark(aBookmark[0]);
+ var oBookmarkInfo2 = this._evaluateXPathBookmark(aBookmark[1]);
+
+ if(!oBookmarkInfo1["elContainer"] || !oBookmarkInfo2["elContainer"]) return;
+
+ this[_sc_] = oBookmarkInfo1["elContainer"];
+ this[_so_] = oBookmarkInfo1["nOffset"];
+
+ this[_ec_] = oBookmarkInfo2["elContainer"];
+ this[_eo_] = oBookmarkInfo2["nOffset"];
+ },
+
+ _getFixedTextContainer : function(elNode, nOffset){
+ while(elNode && elNode[_nt_] == 3 && elNode[_ps_] && elNode[_ps_][_nt_] == 3){
+ nOffset += elNode[_ps_].nodeValue.length;
+ elNode = elNode[_ps_];
+ }
+
+ return {elContainer:elNode, nOffset:nOffset};
+ },
+
+ _getFixedStartTextNode : function(){
+ return this._getFixedTextContainer(this[_sc_], this[_so_]);
+ },
+
+ _getFixedEndTextNode : function(){
+ return this._getFixedTextContainer(this[_ec_], this[_eo_]);
+ },
+
+ placeStringBookmark : function(){
+ var sTmpId = (new Date()).getTime(), oInsertionPoint, oEndMarker, oStartMarker;
+
+ (oInsertionPoint = this.cloneRange()).collapseToEnd();
+ oInsertionPoint.insertNode( $('').attr('id', HUSKY_BOOKMARK_END_ID_PREFIX+sTmpId)[0] );
+
+ (oInsertionPoint = this.cloneRange()).collapseToStart();
+ oInsertionPoint.insertNode( $('').attr('id', HUSKY_BOOKMARK_START_ID_PREFIX+sTmpId)[0] );
+
+ this.moveToBookmark(sTmpId);
+
+ return sTmpId;
+ },
+
+ cloneRange : function(){
+ return this._copyRange(new HuskyRange());
+ },
+
+ moveToBookmark : function(vBookmark){
+ if(typeof(vBookmark) != "object")
+ this.moveToStringBookmark(vBookmark);
+ else
+ this.moveToXPathBookmark(vBookmark);
+ },
+
+ moveToStringBookmark : function(sBookmarkID){
+ var oStartMarker = $('#'+HUSKY_BOOKMARK_START_ID_PREFIX+sBookmarkID)[0];
+ var oEndMarker = $('#'+HUSKY_BOOKMARK_END_ID_PREFIX+sBookmarkID)[0];
+
+ if(!oStartMarker || !oEndMarker) return;
+
+ this.setEndBefore(oEndMarker);
+ this.setStartAfter(oStartMarker);
+ },
+
+ removeStringBookmark : function(sBookmarkID){
+ $(
+ '#' + HUSKY_BOOKMARK_START_ID_PREFIX + sBookmarkID + ',' +
+ '#' + HUSKY_BOOKMARK_END_ID_PREFIX + sBookmarkID
+ ).remove();
+ },
+
+ collapseToStart : function(){
+ this.collapse(true);
+ },
+
+ collapseToEnd : function(){
+ this.collapse(false);
+ },
+
+ createAndInsertNode : function(sTagName){
+ tmpNode = d.createElement(tagName);
+ this.insertNode(tmpNode);
+ return tmpNode;
+ },
+
+ getNodes : function(bSplitTextEndNodes, fnFilter){
+ if(bSplitTextEndNodes) this._splitTextEndNodesOfTheRange();
+
+ var aAllNodes = this._getNodesInRange();
+ var aFilteredNodes = [];
+
+ if(!fnFilter) return aAllNodes;
+
+ for(var i=0; i= 0) return true;
+
+ if(bIncludePartlySelected){
+ if(startToEnd == 1) return false;
+ if(endToStart == -1) return false;
+ return true;
+ }
+
+ return false;
+ },
+
+ isNodeInRange : function(oNode, bIncludePartlySelected, bContentOnly){
+ var oTmpRange = new HuskyRange();
+
+ if(bContentOnly && oNode.firstChild){
+ oTmpRange.setStartBefore(oNode.firstChild);
+ oTmpRange.setEndAfter(oNode.lastChild);
+ }else{
+ oTmpRange.selectNode(oNode);
+ }
+
+ return this.isRangeInRange(oTmpRange, !!bIncludePartlySelected);
+ },
+
+ pasteHTML : function(sHTML){
+ if(sHTML == ""){
+ this.deleteContents();
+ return;
+ }
+
+ var oTmpDiv = $('').html(sHTML)[0];
+ var oFirstNode = oTmpDiv.firstChild;
+ var oLastNode = oTmpDiv.lastChild;
+
+ var clone = this.cloneRange();
+ var sBM = clone.placeStringBookmark();
+
+ while(oTmpDiv.lastChild) this.insertNode(oTmpDiv.lastChild);
+
+ this.setEndNodes(oFirstNode, oLastNode);
+
+ // delete the content later as deleting it first may mass up the insertion point
+ // eg)
[A]BCD
---paste O---> O
BCD
+ clone.moveToBookmark(sBM);
+ clone.deleteContents();
+ clone.removeStringBookmark(sBM);
+ },
+
+ toString : function(){
+ this.toString = W3CDOMRange.prototype.toString;
+ return this.toString();
+ },
+
+ toHTMLString : function(){
+ return $('
').append(this.cloneContents()).html();
+ },
+
+ findAncestorByTagName : function(sTagName){
+ var oNode = this[_ca_];
+ while(oNode && oNode.tagName != sTagName) oNode = dp(oNode);
+
+ return oNode;
+ },
+
+ selectNodeContents : function(oNode){
+ if(!oNode) return;
+
+ var oFirstNode = oNode.firstChild?oNode.firstChild:oNode;
+ var oLastNode = oNode.lastChild?oNode.lastChild:oNode;
+
+ if(oFirstNode[_nt_] == 3)
+ this.setStart(oFirstNode, 0);
+ else
+ this.setStartBefore(oFirstNode);
+
+ if(oLastNode[_nt_] == 3)
+ this.setEnd(oLastNode, oLastNode.nodeValue.length);
+ else
+ this.setEndAfter(oLastNode);
+ },
+
+ styleRange : function(oStyle, oAttribute, sNewSpanMarker){
+ var aStyleParents = this._getStyleParentNodes(sNewSpanMarker), c = aStyleParents.length, i;
+ if(c < 1) return;
+
+ for(i=0; i < c; i++) {
+ if (oStyle) $(aStyleParents[i]).css(oStyle);
+ if (oAttribute) $(aStyleParents[i]).attr(oAttribute);
+ }
+
+ this.setStartBefore(aStyleParents[0]);
+ this.setEndAfter(aStyleParents[aStyleParents.length-1]);
+ },
+
+ _getStyleParentNodes : function(sNewSpanMarker){
+ this._splitTextEndNodesOfTheRange();
+
+ var oSNode = this.getStartNode();
+ var oENode = this.getEndNode();
+
+ var aAllNodes = this._getNodesInRange();
+ var aResult = [];
+ var nResult = 0;
+
+ var oNode, oTmpNode, iStartRelPos, iEndRelPos, $span, iSIdx, iEIdx;
+ var nInitialLength = aAllNodes.length;
+ var arAllBottmNodes = array_filter(aAllNodes, function(v){return (!v.firstChild);});
+
+ for(var i=0; i
').parent()[0];
+
+ if(sNewSpanMarker) $span.attr(sNewSpanMarker, "true");
+ }
+
+ this.setStartBefore(oSNode);
+ this.setEndAfter(oENode);
+
+ return aResult;
+ },
+
+ _getVeryFirstChild : function(oNode){
+ if(oNode.firstChild) return this._getVeryFirstChild(oNode.firstChild);
+ return oNode;
+ },
+
+ _getVeryLastChild : function(oNode){
+ if(oNode.lastChild) return this._getVeryLastChild(oNode.lastChild);
+ return oNode;
+ },
+
+ _getFirstRealChild : function(oNode){
+ var oFirstNode = oNode.firstChild;
+ while(oFirstNode && oFirstNode[_nt_] == 3 && oFirstNode.nodeValue == "") oFirstNode = oFirstNode[_ns_];
+
+ return oFirstNode;
+ },
+
+ _getLastRealChild : function(oNode){
+ var oLastNode = oNode.lastChild;
+ while(oLastNode && oLastNode[_nt_] == 3 && oLastNode.nodeValue == "") oLastNode = oLastNode[_ps_];
+
+ return oLastNode;
+ },
+
+ _getVeryFirstRealChild : function(oNode){
+ var oFirstNode = this._getFirstRealChild(oNode);
+ if(oFirstNode) return this._getVeryFirstRealChild(oFirstNode);
+ return oNode;
+ },
+ _getVeryLastRealChild : function(oNode){
+ var oLastNode = this._getLastRealChild(oNode);
+ if(oLastNode) return this._getVeryLastChild(oLastNode);
+ return oNode;
+ },
+
+ _getLineStartInfo : function(node){
+ var frontEndFinal = null;
+ var frontEnd = node;
+ var lineBreaker = node;
+ var bParentBreak = true;
+
+ // vertical(parent) search
+ function getLineStart(node){
+ if(!node) return;
+ if(frontEndFinal) return;
+
+ if(rxLineBreaker.test(node.tagName)){
+ lineBreaker = node;
+ frontEndFinal = frontEnd;
+
+ bParentBreak = true;
+
+ return;
+ }else{
+ frontEnd = node;
+ }
+
+ getFrontEnd(node[_ps_]);
+
+ if(frontEndFinal) return;
+ getLineStart(dp(node));
+ }
+
+ // horizontal(sibling) search
+ function getFrontEnd(node){
+ if(!node) return;
+ if(frontEndFinal) return;
+
+ if(rxLineBreaker.test(node.tagName)){
+ lineBreaker = node;
+ frontEndFinal = frontEnd;
+
+ bParentBreak = false;
+ return;
+ }
+
+ if(node.firstChild && node.tagName != "TABLE"){
+ var curNode = node.lastChild;
+ while(curNode && !frontEndFinal){
+ getFrontEnd(curNode);
+
+ curNode = curNode[_ps_];
+ }
+ }else{
+ frontEnd = node;
+ }
+
+ if(!frontEndFinal){
+ getFrontEnd(node[_ps_]);
+ }
+ }
+
+ getLineStart(node);
+
+ return {oNode: frontEndFinal, oLineBreaker: lineBreaker, bParentBreak: bParentBreak};
+ },
+
+ _getLineEndInfo : function(node){
+ var backEndFinal = null;
+ var backEnd = node;
+ var lineBreaker = node;
+ var bParentBreak = true;
+
+ // vertical(parent) search
+ function getLineEnd(node){
+ if(!node) return;
+ if(backEndFinal) return;
+
+ if(rxLineBreaker.test(node.tagName)){
+ lineBreaker = node;
+ backEndFinal = backEnd;
+
+ bParentBreak = true;
+
+ return;
+ }else{
+ backEnd = node;
+ }
+
+ getBackEnd(node[_ns_]);
+ if(backEndFinal) return;
+
+ getLineEnd(dp(node));
+ }
+
+ // horizontal(sibling) search
+ function getBackEnd(node){
+ if(!node) return;
+ if(backEndFinal) return;
+
+ if(rxLineBreaker.test(node.tagName)){
+ lineBreaker = node;
+ backEndFinal = backEnd;
+
+ bParentBreak = false;
+
+ return;
+ }
+
+ if(node.firstChild && node.tagName != "TABLE"){
+ var curNode = node.firstChild;
+ while(curNode && !backEndFinal){
+ getBackEnd(curNode);
+
+ curNode = curNode[_ns_];
+ }
+ }else{
+ backEnd = node;
+ }
+
+ if(!backEndFinal){
+ getBackEnd(node[_ns_]);
+ }
+ }
+
+ getLineEnd(node);
+
+ return {oNode: backEndFinal, oLineBreaker: lineBreaker, bParentBreak: bParentBreak};
+ },
+
+ getLineInfo : function(){
+ var oSNode = this.getStartNode();
+ var oENode = this.getEndNode();
+
+ // the range is currently collapsed
+ if(!oSNode) oSNode = this.getNodeAroundRange(true, true);
+ if(!oENode) oENode = this.getNodeAroundRange(true, true);
+
+ var oStart = this._getLineStartInfo(oSNode);
+ var oStartNode = oStart.oNode;
+ var oEnd = this._getLineEndInfo(oENode);
+ var oEndNode = oEnd.oNode;
+
+ var iRelativeStartPos = this._compareEndPoint(dp(oStartNode), this._getPosIdx(oStartNode), this[_ec_], this[_eo_]);
+ var iRelativeEndPos = this._compareEndPoint(dp(oEndNode), this._getPosIdx(oEndNode)+1, this[_sc_], this[_so_]);
+
+ if(!(iRelativeStartPos <= 0 && iRelativeEndPos >= 0)){
+ oSNode = this.getNodeAroundRange(false, true);
+ oENode = this.getNodeAroundRange(false, true);
+ oStart = this._getLineStartInfo(oSNode);
+ oEnd = this._getLineEndInfo(oENode);
+ }
+
+ return {oStart: oStart, oEnd: oEnd};
+ }
+});
+/**
+ * }}} HuskyRange
+ */
+
+/**
+ * {{{ @class SimpleSelection
+ * @brief Cross-browser selection function
+ */
+function SimpleSelection(win){
+ this.init();
+
+ if(!this._oSelection) this.selectionLoaded = false;
+};
+$.extend(SimpleSelection.prototype, {
+ selectionLoaded : true,
+ selectRange : function(oRng) {
+ this.selectNone();
+ this.addRange(oRng);
+ }
+});
+
+function SimpleSelectionImpl_FF() { };
+$.extend(SimpleSelectionImpl_FF.prototype, {
+ init : function() {
+ this._oSelection = window.getSelection();
+ },
+ getRangeAt : function(iNum) {
+ iNum = iNum || 0;
+
+ try{
+ var oFFRange = this._oSelection.getRangeAt(iNum);
+ }catch(e){return new W3CDOMRange();}
+
+ return this._FFRange2W3CRange(oFFRange);
+ },
+ addRange : function(oW3CRange){
+ var oFFRange = this._W3CRange2FFRange(oW3CRange);
+ this._oSelection.addRange(oFFRange);
+ },
+ selectNone : function() {
+ this._oSelection.removeAllRanges();
+ },
+ _FFRange2W3CRange : function(oFFRange){
+ var oW3CRange = new W3CDOMRange();
+ oW3CRange.setStart(oFFRange[_sc_], oFFRange[_so_]);
+ oW3CRange.setEnd(oFFRange[_ec_], oFFRange[_eo_]);
+ return oW3CRange;
+ },
+ _W3CRange2FFRange : function(oW3CRange){
+ var oFFRange = d.createRange();
+ oFFRange.setStart(oW3CRange[_sc_], oW3CRange[_so_]);
+ oFFRange.setEnd(oW3CRange[_ec_], oW3CRange[_eo_]);
+
+ return oFFRange;
+ }
+});
+
+function SimpleSelectionImpl_IE(){ };
+$.extend(SimpleSelectionImpl_IE.prototype, {
+ init : function() {
+ this._oSelection = d.selection;
+ },
+ getRangeAt : function(iNum) {
+ iNum = iNum || 0;
+
+ if(this._oSelection.type == "Control"){
+ var oW3CRange = new W3CDOMRange();
+ var oSelectedNode = this._oSelection.createRange().item(iNum);
+
+ // if the selction occurs in a different document, ignore
+ if(!oSelectedNode || oSelectedNode.ownerDocument != d) return oW3CRange;
+
+ oW3CRange.selectNode(oSelectedNode);
+
+ return oW3CRange;
+ }else{
+ var oSelectedNode = this._oSelection.createRangeCollection().item(iNum).parentElement();
+
+ // if the selction occurs in a different document, ignore
+ if(!oSelectedNode || oSelectedNode.ownerDocument != d){
+ var oW3CRange = new W3CDOMRange();
+ return oW3CRange;
+ }
+ return this._IERange2W3CRange(this._oSelection.createRangeCollection().item(iNum));
+ }
+ },
+ addRange : function(oW3CRange){
+ var oIERange = this._W3CRange2IERange(oW3CRange);
+ oIERange.select();
+ },
+ selectNone : function(){
+ this._oSelection.empty();
+ },
+ _W3CRange2IERange : function(oW3CRange){
+ var oStartIERange = this._getIERangeAt(oW3CRange[_sc_], oW3CRange[_so_]);
+ var oEndIERange = this._getIERangeAt(oW3CRange[_ec_], oW3CRange[_eo_]);
+ oStartIERange.setEndPoint("EndToEnd", oEndIERange);
+
+ return oStartIERange;
+ },
+ _getIERangeAt : function(oW3CContainer, iW3COffset){
+ var oIERange = d.body.createTextRange();
+
+ var oEndPointInfoForIERange = this._getSelectableNodeAndOffsetForIE(oW3CContainer, iW3COffset);
+
+ var oSelectableNode = oEndPointInfoForIERange.oSelectableNodeForIE;
+ var iIEOffset = oEndPointInfoForIERange.iOffsetForIE;
+
+ oIERange.moveToElementText(oSelectableNode);
+ oIERange.collapse(oEndPointInfoForIERange.bCollapseToStart);
+ oIERange.moveStart("character", iIEOffset);
+
+ return oIERange;
+ },
+ _getSelectableNodeAndOffsetForIE : function(oW3CContainer, iW3COffset){
+ var oIERange = d.body.createTextRange();
+
+ var oNonTextNode = null;
+ var aChildNodes = null;
+ var iNumOfLeftNodesToCount = 0;
+
+ if(oW3CContainer[_nt_] == 3){
+ oNonTextNode = dp(oW3CContainer);
+ aChildNodes = dc(oNonTextNode);
+ iNumOfLeftNodesToCount = aChildNodes.length;
+ }else{
+ oNonTextNode = oW3CContainer;
+ aChildNodes = dc(oNonTextNode);
+ iNumOfLeftNodesToCount = iW3COffset;
+ }
+
+ var oNodeTester = null;
+
+ var iResultOffset = 0;
+
+ var bCollapseToStart = true;
+
+ for(var i=0; i
=0) break;
+
+ oPrevNonTextNode = aChildNodes[i];
+ }
+
+ var pointRangeIdx = i;
+
+ if(pointRangeIdx != 0 && aChildNodes[pointRangeIdx-1][_nt_] == 3){
+ var oRgTextStart = d.body.createTextRange();
+ var oCurTextNode = null;
+ if(oPrevNonTextNode){
+ oRgTextStart.moveToElementText(oPrevNonTextNode);
+ oRgTextStart.collapse(false);
+ oCurTextNode = oPrevNonTextNode.nextSibling;
+ }else{
+ oRgTextStart.moveToElementText(oContainer);
+ oRgTextStart.collapse(true);
+ oCurTextNode = oContainer.firstChild;
+ }
+
+ var oRgTextsUpToThePoint = oRgOrigPoint.duplicate();
+ oRgTextsUpToThePoint.setEndPoint("StartToStart", oRgTextStart);
+
+ var textCount = oRgTextsUpToThePoint.text.length
+
+ while(textCount > oCurTextNode.nodeValue.length && oCurTextNode.nextSibling){
+ textCount -= oCurTextNode.nodeValue.length;
+ oCurTextNode = oCurTextNode.nextSibling;
+ }
+
+ // this will enforce IE to re-reference oCurTextNode
+ var oTmp = oCurTextNode.nodeValue;
+
+ if(bStartPt && oCurTextNode.nextSibling && oCurTextNode.nextSibling[_nt_] == 3 && textCount == oCurTextNode.nodeValue.length){
+ textCount -= oCurTextNode.nodeValue.length;
+ oCurTextNode = oCurTextNode.nextSibling;
+ }
+
+ oContainer = oCurTextNode;
+ offset = textCount;
+ }else{
+ oContainer = oRgOrigPoint.parentElement();
+ offset = pointRangeIdx;
+ }
+
+ return {"oContainer" : oContainer, "iOffset" : offset};
+ }
+});
+$.extend(SimpleSelection.prototype, ($.browser.msie?SimpleSelectionImpl_IE:SimpleSelectionImpl_FF).prototype);
+/**
+ * }}} SimpleSelection
+ */
+
+// {{{ DOMFix
+DOMFix = {
+ init : function(){
+ if ($.browser.msie || $.browser.opera) {
+ this[_cn_] = this._childNodes_Fix;
+ this[_pn_] = this._parentNode_Fix;
+ } else {
+ this[_cn_] = this._childNodes_Native;
+ this[_pn_] = this._parentNode_Native;
+ }
+ },
+
+ _parentNode_Native : function(elNode){
+ return elNode[_pn_];
+ },
+
+ _parentNode_Fix : function(elNode){
+ if(!elNode) return elNode;
+
+ while(elNode[_ps_]){ elNode = elNode[_ps_]; }
+
+ return elNode[_pn_];
+ },
+
+ _childNodes_Native : function(elNode){
+ return elNode[_cn_];
+ },
+
+ _childNodes_Fix : function(elNode){
+ var aResult = null;
+ var nCount = 0;
+
+ if(elNode){
+ var aResult = [];
+ elNode = elNode.firstChild;
+ while(elNode){
+ aResult[nCount++] = elNode;
+ elNode=elNode[_ns_];
+ }
+ }
+
+ return aResult;
+ }
+};
+DOMFix.init();
+dp = DOMFix[_pn_];
+dc = DOMFix[_cn_];
+// }}}
+
+/**
+ * }}} Selection
+ */
+
+/**
+ * {{{ Utility functions
+ */
+// bind a function to the host object
+function bind(obj, func) {
+ return function(){ return func.apply(obj, arguments); };
+};
+
+// check block element
+function is_block(el) {
+ return (el && el[_nt_] == 1 && rx_block.test(el[_nn_]));
+};
+
+// is defined?
+function is_def(v){ return typeof(v)!='undefined'; };
+
+// is string?
+function is_str(v){ return typeof(v)=='string'; };
+
+// filter
+function array_filter(arr, fn) {
+ var ret=[], i, c;
+
+ for(i=0,c=arr.length; i < c; i++) {
+ if (fn(arr[i])) ret.push(arr[i]);
+ }
+
+ return ret;
+};
+
+// node index
+function node_index(node) {
+ var cs = node[_pn_][_cn_], c, i;
+
+ for(i=0,c=cs.length; i < c; i++) {
+ if (cs[i] === node) return i;
+ }
+
+ return -1;
+};
+
+/**
+ * }}}
+ */
+
+// global context
+if (xe.Xeed) xe.Xeed = $.extend(Xeed, xe.Xeed);
+else xe.Xeed = Xeed;
+
+xe.W3CDOMRange = W3CDOMRange;
+xe.HuskyRange = HuskyRange;
+
+// run callback functions
+if ($.isArray(xe.Xeed.callbacks) && xe.Xeed.callbacks.length) {
+ while(fn = xe.Xeed.callbacks.shift()) fn();
+}
+
+})(jQuery);
diff --git a/modules/editor/skins/xeed/js/xeed.stub.js b/modules/editor/skins/xeed/js/xeed.stub.js
new file mode 100644
index 000000000..24012e219
--- /dev/null
+++ b/modules/editor/skins/xeed/js/xeed.stub.js
@@ -0,0 +1,85 @@
+(function($){
+
+var ver = '0.9.0';
+
+$.fn.xeed = function(options) {
+ var $this = this.eq(0), $head = $('head'), templates = {}, DEFAULT = 'default', isCSSLoaded, isScriptLoaded;
+ var opts = $.extend({
+ ui : DEFAULT
+ }, options);
+
+ // Load Editor UI
+ function loadUI(ui, fnError){
+ if (templates[ui]) {
+ // load CSS
+ if (!isCSSLoaded) {
+ $head
+ .append('')
+ .append('');
+
+ loadCSS = true;
+ }
+
+ // insert HTML
+ $this.before(templates[ui]).hide();
+
+ // load script
+ setTimeout(loadScript, 0);
+
+ return;
+ }
+
+ $.ajax({
+ url : xeed_path + 'template.php?ui=' + ui,
+ dataType : 'text',
+ cache : false,
+ success : function(data){
+ var html = processTemplate(data);
+ templates[ui] = html;
+ loadUI(ui);
+ }
+ });
+ };
+
+ // Load Xeed core script
+ function loadScript(){
+ function callback() {
+ var xeed = new xe.Xeed($this, opts);
+
+ $this.data('xeed', xeed);
+ xe.registerApp(xeed);
+
+ isScriptLoaded = true;
+ }
+
+ if (!xe.Xeed) xe.Xeed = {};
+ if (!xe.Xeed.callbacks) xe.Xeed.callbacks = [];
+
+ xe.Xeed.version = ver;
+
+ if (isScriptLoaded) callback();
+ else xe.Xeed.callbacks.push(callback);
+
+ setTimeout(function(){
+ var scr = document.createElement('script');
+ scr.src = xeed_path+'xeed.js?ver='+ver+'&_cache='+Math.round(Math.random()*1000); // XXX : cache_code
+ scr.type = 'text/javascript';
+
+ $head[0].appendChild(scr);
+ }, 0);
+ };
+
+ // TODO : Process a template
+ function processTemplate(data) {
+ return data;
+ };
+
+ if (!this.data('xeed')) {
+ this.data('xeed', 1);
+ loadUI(opts.ui);
+ }
+
+ return this;
+};
+
+})(jQuery);
diff --git a/modules/editor/skins/xeed/skin.xml b/modules/editor/skins/xeed/skin.xml
new file mode 100644
index 000000000..386e4b930
--- /dev/null
+++ b/modules/editor/skins/xeed/skin.xml
@@ -0,0 +1,12 @@
+
+
+ XEED Skin
+
+ XpressEditor based on SmartEditor Basic by 행복한고니
+
+ 0.5.0
+ 2009-03-22
+
+ 행복한고니
+
+
diff --git a/modules/editor/skins/xeed/xd.css b/modules/editor/skins/xeed/xd.css
new file mode 100644
index 000000000..5bb5d2473
--- /dev/null
+++ b/modules/editor/skins/xeed/xd.css
@@ -0,0 +1,426 @@
+/* XEED Start */
+
+/* Reset */
+html,
+body{ height:100%;}
+.xd{ display:none; position:relative; border:1px solid #b5b5b5; o-verflow:hidden; z-index:1; *zoom:1; }
+.xd .tool ul,
+.xd .mode ul{ margin:0; padding:0; list-style:none;}
+.xd .tool button,
+.xd .mode button,
+.xdmw button{ position:relative; border:0; margin:0; padding:0; background-color:transparent; overflow:visible; cursor:pointer;}
+.xd .tool em{ font-style:normal;}
+.xd .tool img{ border:0;}
+.xd .tool .itx{ border:1px solid #c7c7c7; background:transparent; padding:2px 4px; _margin:-1px 0; height:15px; font-size:12px; vertical-align:top;}
+.xd .tool .itx.hover,
+.xd .tool .itx.focus{ border-color:#999; box-shadow:0 0 2px #bbb; -moz-box-shadow:0 0 2px #bbb; -webkit-box-shadow:0 0 2px #bbb;}
+.xd .tool .icx,
+.xd .mode .icx,
+.xdmw .icx{ width:13px; height:13px; vertical-align:middle; margin:0 2px 0 0; padding:0;}
+.xd .tool .btn,
+.xdmw .btn{ border:1px solid #b5b5b5; font-size:12px; padding:0 4px; height:21px; _height:20px; vertical-align:top; cursor:pointer;}
+.xd .tool fieldset{ border:0; margin:0; padding:0;}
+.xd .tool label,
+.xd .mode label,
+.xdmw label{ cursor:pointer;}
+.xd .tool .strong,
+.xdmw .strong{ font-weight:bold;}
+
+/* Background */
+.xd .tool,
+.xd .mode,
+.xd .resize,
+.xd .tool .mo,
+.xd .t1 .u1,
+.xd .t1 .u1 .tb,
+.xd .t1 .u2,
+.xd .t2 .tb,
+.xd .t2 .btn,
+.xd .t2 .u3 .lr li.active button,
+.xd .t2 .u3 .lr li.hover button,
+.xd .t2 .u6 .sc .lr .li ul,
+.xd .t2 .u6 .te .lr .cn li button,
+.xd .t2 .u6 .te .lr .th li button,
+.xdmw .iHead,
+.xdmw .iFoot,
+.xdmw .iHead h2,
+.xdmw .iFoot .btn,
+.xdmw .al .label,
+.xdmw .al .sn .ctr,
+.xdmw .al .file li .ob,
+.xdmw .al .sn .task .all,
+.xdmw .al .sn .task .insert{ background-image:url(xd.gif); background-repeat:no-repeat;}
+
+/* Font Family */
+.xd .tool .t1 .tb,
+.xd .tool .t2 .hx .lr button,
+.xd .tool .t2 .u6 .sc .lr .tab,
+.xd .edit{ font-family:Dotum, ë‹ì›€, Tahoma, Geneva, sans-serif;}
+.xd .tool .itx,
+.xd .tool .t2 .u6 .te .lr .cn li button,
+.xd .tool .t2 .u6 .te .lr .wh td button,
+.xd .mode ul button,
+.xd .mode .autoResize label,
+.xdmw .item .iHead h2 em,
+.xdmw label{ font-family:Tahoma, Geneva, sans-serif;}
+
+/* Hidden Text */
+.xd .tool .mo span,
+.xd .tool .t2 .tb span,
+.xd .tool .t2 .u3 .cr .lr li button span,
+.xd .tool .t2 .u6 .te .lr .cn button span,
+.xd .mode .resize span{ position:absolute; top:0; left:0; width:1px; height:1px; overflow:hidden; font-size:0; line-height:0; z-index:-1;}
+
+/* Label Input OverWrap */
+.xd .tool .liow{ position:relative; background:#fff;}
+.xd .tool .liow label{ position:absolute; top:4px; left:6px; white-space:nowrap;}
+.xd .tool .liow .itx{ position:relative; z-index:2; background:transparent;}
+
+/* Time */
+.xd .time{ font-size:12px; background:#fcfcfc; border-top:1px solid #ddd;}
+.xd .time p{ margin:0; padding:6px 10px; color:#666; text-align:center;}
+
+/* Mode */
+.xd .mode{ position:relative; z-index:1; background-position:0 -1140px; background-repeat:repeat-x; border-top:1px solid #b5b5b5; *zoom:1;}
+.xd .mode .resize{ width:100%; height:20px; cursor:n-resize; background-position:center -1105px; font-size:0; line-height:0;}
+.xd .mode .autoResize{ position:absolute; top:0; left:8px; font-size:11px; line-height:20px;}
+.xd .mode .autoResize label{ display:inline-block; height:20px;}
+.xd .mode ul{ position:absolute; top:-1px; right:10px; line-height:15px;}
+.xd .mode li{ position:relative; float:left; margin:0 -1px 0 0; border:1px solid #b5b5b5; background:#f4f4f4; vertical-align:top;}
+.xd .mode li.active{ border-top-color:#fcfcfc; background:#fcfcfc; z-index:2;}
+.xd .mode ul button{ padding:0 10px; font-size:11px;}
+
+/* Tool - Common */
+.xd .tool{ background-position:0 -1140px; background-repeat:repeat-x; background-color:#f4f4f4; position:relative; z-index:3;}
+.xd .tool .mo{ display:none; position:absolute; top:0; right:0; height:25px; width:16px; background-position:-300px -252px;}
+.xd .skip{ position:absolute; display:block; top:0; left:0; width:1px; height:1px; overflow:hidden; color:#333; text-indent:1em; text-decoration:none; font-size:12px;}
+.xd .skip:hover,
+.xd .skip:active,
+.xd .skip:focus{ position:relative; width:100%; height:auto; padding:5px 0;}
+
+/* Tool - 1 */
+.xd .t1{ position:relative; z-index:2; padding:0 15px 0 0; border-bottom:1px solid #e0dedf; *zoom:1;}
+.xd .t1:after{ content:""; display:block; clear:both;}
+.xd .t1 .u1,
+.xd .t1 .u2{ background-repeat:repeat-x; background-position:0 -1140px; position:relative; left:-1px; float:left; border:1px solid #e0dedf; border-bottom:0; border-left:0; margin:-1px 0 0 0;}
+.xd .t1.c2 .u2{ display:none;}
+.xd .t1.ca .u1,
+.xd .t1.ca .u2{ display:block;}
+.xd .t1 .ti{ float:left;}
+.xd .t1 .ti.active{ background-color:#e8e8e8; border:1px solid #e0dedf; border-top:0; border-bottom:0; margin:0 -1px;}
+.xd .t1 .tb{ display:inline-block; height:26px; line-height:26px; font-size:11px; text-decoration:none; vertical-align:top; color:#000;}
+.xd .t1 .tb span{ display:inline-block; padding:0 5px; white-space:nowrap; text-shadow:1px 1px 0 #f8f8f8;}
+.xd .t1 .u1 .tb span{ padding-left:21px;}
+.xd .t1 .u1 .tb em{ color:#ccc;}
+.xd .t1 .u1 .tb em strong{ color:#f60;}
+.xd .t1 .u1 .al a{ background-position:6px -253px; cursor:pointer;}
+.xd .t1 .u1 .img .tb{ background-position:6px -283px;}
+.xd .t1 .u1 .mov .tb{ background-position:6px -313px;}
+.xd .t1 .u1 .file .tb{ background-position:6px -343px;}
+
+/* Tool - 2 */
+.xd .t2{ position:relative; z-index:2; border-top:1px solid #e0dedf; border-bottom:1px solid #b5b5b5; padding:5px 10px 0 5px; margin:-1px 0 0 0; *zoom:1;}
+.xd .t2:after{ content:""; display:block; clear:both;}
+.xd .t2 .mo{ top:3px;}
+.xd .t2 .u1,
+.xd .t2 .u2,
+.xd .t2 .u3,
+.xd .t2 .u4,
+.xd .t2 .u5,
+.xd .t2 .u6{ float:left; position:relative; margin:0 5px 0 0; padding:0 0 5px 0; *zoom:1;}
+.xd .t2 .u1:after,
+.xd .t2 .u2:after,
+.xd .t2 .u3:after,
+.xd .t2 .u4:after,
+.xd .t2 .u5:after,
+.xd .t2 .u6:after{ content:""; display:block; clear:both;}
+.xd .t2.c23456 .u2,
+.xd .t2.c23456 .u3,
+.xd .t2.c23456 .u4,
+.xd .t2.c23456 .u5,
+.xd .t2.c23456 .u6,
+.xd .t2.c3456 .u3,
+.xd .t2.c3456 .u4,
+.xd .t2.c3456 .u5,
+.xd .t2.c3456 .u6,
+.xd .t2.c456 .u4,
+.xd .t2.c456 .u5,
+.xd .t2.c456 .u6,
+.xd .t2.c56 .u5,
+.xd .t2.c56 .u6,
+.xd .t2.c6 .u6{ display:none;}
+.xd .t2.ca .u1,
+.xd .t2.ca .u2,
+.xd .t2.ca .u3,
+.xd .t2.ca .u4,
+.xd .t2.ca .u5,
+.xd .t2.ca .u6{ display:block;}
+.xd .t2 .ti{ float:left; border:1px solid #b5b5b5; margin:0 -1px 0 0; font-size:0; position:relative;}
+.xd .t2 .ti.hover{ border-color:#666; z-index:100; box-shadow:0 0 2px #bbb; -moz-box-shadow:0 0 2px #bbb; -webkit-box-shadow:0 0 2px #bbb;}
+.xd .t2 .ti.active{ border-color:#333; z-index:100;}
+.xd .t2 .tb{ width:20px; height:19px; line-height:19px; overflow:visible; _overflow-y:hidden; vertical-align:top; background-position:0 -60px;}
+.xd .t2 .u2 .tb{ width:auto; height:19px; line-height:19px; font-size:12px; padding:0 12px 0 4px; background-position:right 0; text-shadow:1px 1px 0 #eee;}
+.xd .t2 .u2 .active .tb{ background-position:right -20px;}
+.xd .t2 .disable{ opacity:.3; filter:alpha(opacity=30); -ms-filter:"alpha(opacity=30)"; border-color:#b5b5b5 !important; box-shadow:none !important; -moz-box-shadow:none !important; -webkit-box-shadow:none !important;}
+.xd .t2 .disable .tb{ cursor:default; -ms-filter:inherit;}
+.xd .t2 .ud .tb{ background-position:0 -40px;}
+.xd .t2 .rd .tb{ background-position:-20px -40px;}
+.xd .t2 .bd .tb{ background-position:-40px -40px;}
+.xd .t2 .ue .tb{ background-position:-60px -40px;}
+.xd .t2 .ic .tb{ background-position:-80px -40px;}
+.xd .t2 .se .tb{ background-position:-100px -40px;}
+.xd .t2 .cr .tb{ background-position:-120px -40px;}
+.xd .t2 .sp .tb{ background-position:-140px -40px;}
+.xd .t2 .sb .tb{ background-position:-160px -40px;}
+.xd .t2 .al .tb{ background-position:-180px -40px;}
+.xd .t2 .ac .tb{ background-position:-200px -40px;}
+.xd .t2 .ar .tb{ background-position:-220px -40px;}
+.xd .t2 .aj .tb{ background-position:-240px -40px;}
+.xd .t2 .ol .tb{ background-position:-260px -40px;}
+.xd .t2 .ul .tb{ background-position:-280px -40px;}
+.xd .t2 .od .tb{ background-position:-300px -40px;}
+.xd .t2 .id .tb{ background-position:-320px -40px;}
+.xd .t2 .qm .tb{ background-position:-340px -40px;}
+.xd .t2 .bx .tb{ background-position:-360px -40px;}
+.xd .t2 .er .tb{ background-position:-380px -40px;}
+.xd .t2 .ur .tb{ background-position:-400px -40px;}
+.xd .t2 .sc .tb{ background-position:-420px -40px;}
+.xd .t2 .te .tb{ background-position:-440px -40px;}
+.xd .t2 .cm .tb{ background-position:-460px -40px;}
+.xd .t2 .cs .tb{ background-position:-480px -40px;}
+.xd .t2 .rs .tb{ background-position:-500px -40px;}
+.xd .t2 .tc .tb{ background-position:-520px -40px;}
+.xd .t2 .fr .tb{ background-position:-540px -40px;}
+.xd .t2 .active .tb.ud{ background-position:0 -60px;}
+.xd .t2 .active .tb.rd{ background-position:-20px -60px;}
+.xd .t2 .active .tb.bd{ background-position:-40px -60px;}
+.xd .t2 .active .tb.ue{ background-position:-60px -60px;}
+.xd .t2 .active .tb.ic{ background-position:-80px -60px;}
+.xd .t2 .active .tb.se{ background-position:-100px -60px;}
+.xd .t2 .active .tb.cr{ background-position:-120px -60px;}
+.xd .t2 .active .tb.sp{ background-position:-140px -60px;}
+.xd .t2 .active .tb.sb{ background-position:-160px -60px;}
+.xd .t2 .active .tb.al{ background-position:-180px -60px;}
+.xd .t2 .active .tb.ac{ background-position:-200px -60px;}
+.xd .t2 .active .tb.ar{ background-position:-220px -60px;}
+.xd .t2 .active .tb.aj{ background-position:-240px -60px;}
+.xd .t2 .active .tb.ol{ background-position:-260px -60px;}
+.xd .t2 .active .tb.ul{ background-position:-280px -60px;}
+.xd .t2 .active .tb.od{ background-position:-300px -60px;}
+.xd .t2 .active .tb.id{ background-position:-320px -60px;}
+.xd .t2 .active .tb.qm{ background-position:-340px -60px;}
+.xd .t2 .active .tb.bx{ background-position:-360px -60px;}
+.xd .t2 .active .tb.er{ background-position:-380px -60px;}
+.xd .t2 .active .tb.ur{ background-position:-400px -60px;}
+.xd .t2 .active .tb.sc{ background-position:-420px -60px;}
+.xd .t2 .active .tb.te{ background-position:-440px -60px;}
+.xd .t2 .active .tb.cm{ background-position:-460px -60px;}
+.xd .t2 .active .tb.cs{ background-position:-480px -60px;}
+.xd .t2 .active .tb.rs{ background-position:-500px -60px;}
+.xd .t2 .active .tb.tc{ background-position:-520px -60px;}
+.xd .t2 .active .tb.fr{ background-position:-540px -60px;}
+
+/* Layer */
+.xd .t2 .lr{ display:none; position:absolute; left:-1px; top:19px; padding:10px; font-size:12px; border:1px solid #bbb; background-color:#fafafa; *zoom:1; box-shadow:0 0 2px #bbb; -moz-box-shadow:0 0 2px #bbb; -webkit-box-shadow:0 0 2px #bbb;}
+.xd .t2 .lr:after{ content:""; display:block; clear:both;}
+.xd .t2 .lr.open{ display:block;}
+
+/* Layer - u2 (hx, ff, fs, lh) */
+.xd .t2 .u2 .lr{ padding:2px;}
+.xd .t2 .u2 .lr li.hover{ background:#ebebeb;}
+.xd .t2 .u2 .lr button{ display:block; white-space:nowrap; text-align:left; font-size:12px; padding:2px 10px;}
+.xd .t2 .u2 .hx .lr button{ font-weight:bold;}
+.xd .t2 .u2 .lr .h1 button{ font-size:24px;}
+.xd .t2 .u2 .lr .h2 button{ font-size:20px;}
+.xd .t2 .u2 .lr .h3 button{ font-size:18px;}
+.xd .t2 .u2 .lr .h4 button{ font-size:16px;}
+.xd .t2 .u2 .lr .h5 button{ font-size:14px;}
+.xd .t2 .u2 .lr .h6 button{ font-size:12px;}
+
+/* Layer - u3 (cr) */
+.xd .t2 .u3 .cr .lr fieldset{ position:relative; margin:0 0 8px 0;}
+.xd .t2 .u3 .cr .lr legend{ font-size:12px; font-weight:bold; margin:0; padding:0; display:block; line-height:normal;}
+.xd .t2 .u3 .cr .lr ul{ width:204px; position:relative; margin:0; *zoom:1;}
+.xd .t2 .u3 .cr .lr ul:after{ content:""; display:block; clear:both;}
+.xd .t2 .u3 .cr .lr li{ position:relative; float:left; margin:0 1px 1px 0; font-size:0; line-height:0;}
+.xd .t2 .u3 .cr .lr li button{ display:block; position:relative; overflow:hidden; width:11px; height:11px; vertical-align:top;}
+.xd .t2 .u3 .cr .lr li.active button,
+.xd .t2 .u3 .cr .lr li.hover button{ background-position:-330px -260px;}
+.xd .t2 .u3 .cr .lr .itx{ display:block; clear:both; width:193px; text-transform:uppercase;}
+.xd .t2 .u3 .cr .lr .btn{ position:relative; width:203px;}
+.xd .t2 .u3 .cr .lr .pv p{ margin:0; padding:8px 10px; width:181px; background:#fff; border:1px dotted #ccc;}
+.xd .t2 .u3 .cr .lr .pv span{ display:inline-block; line-height:1.5;}
+
+/* Layer - u6 (ur) */
+.xd .t2 .u6 .ur .lr{ width:260px; left:auto; right:-1px;}
+.xd .t2 .u6 .ur .lr fieldset{ *zoom:1;}
+.xd .t2 .u6 .ur .lr fieldset:after{ content:""; display:block; clear:both;}
+.xd .t2 .u6 .ur .lr .itx{ display:block; width:250px; margin:0 0 4px 0; background:#fff;}
+.xd .t2 .u6 .ur .lr .nw{ float:left; line-height:21px; white-space:nowrap;}
+.xd .t2 .u6 .ur .lr .btnArea{ float:right; white-space:nowrap;}
+.xd .t2 .u6 .ur .lr .btn{ *margin:0 0 0 4px;}
+
+/* Layer - u6 (sc) */
+.xd .t2 .u6 .sc .lr{ width:402px; height:222px; left:auto; right:-1px;}
+.xd .t2 .u6 .sc .lr .li{ float:left; margin:0 -1px 0 0;}
+.xd .t2 .u6 .sc .lr .tab{ position:relative; font-size:11px; height:23px; _line-height:18px; padding:0 4px; border:1px solid #d0d0d0; background:#f5f5f5; z-index:10;}
+.xd .t2 .u6 .sc .lr .active .tab{ border-bottom-color:#fff; background:#fff;}
+.xd .t2 .u6 .sc .lr .li ul{ display:none; position:absolute; left:10px; top:32px; width:400px; height:171px; border:1px solid #d0d0d0; background-position:0 -80px; z-index:1; *zoom:1;}
+.xd .t2 .u6 .sc .lr .li ul:after{ content:""; display:block; clear:both;}
+.xd .t2 .u6 .sc .lr .active ul{ display:block;}
+.xd .t2 .u6 .sc .lr .li li{ position:relative; top:-1px; float:left; margin:0 -1px -1px 0;}
+.xd .t2 .u6 .sc .lr .li li button{ width:21px; height:20px; overflow:hidden;}
+.xd .t2 .u6 .sc .lr .li li:hover button{ border:1px solid #666;}
+.xd .t2 .u6 .sc .lr fieldset{ position:absolute; top:212px; left:10px;}
+.xd .t2 .u6 .sc .lr .itx{ width:296px; padding-right:100px; background:#fff; _margin:-1px 0;}
+.xd .t2 .u6 .sc .lr .btnArea{ position:absolute; top:0; right:0;}
+.xd .t2 .u6 .sc .lr .btn{ float:left; margin:0 0 0 -1px; display:inline;}
+
+/* Layer - u6 (te) */
+.xd .t2 .u6 .te .lr{ left:auto; right:-1px;}
+.xd .t2 .u6 .te .lr fieldset{ position:relative; width:280px; padding:0 0 10px 0;}
+.xd .t2 .u6 .te .lr legend{ font-size:12px; font-weight:bold; margin:0; padding:10px 0 0 0; display:block; line-height:normal;}
+.xd .t2 .u6 .te .lr .pv{ border-bottom:1px solid #ddd;}
+.xd .t2 .u6 .te .lr .pv legend{ padding:0;}
+.xd .t2 .u6 .te .lr .pv .xdcs{ background:#fff; line-height:1.5; margin:0 -10px; padding:1px 10px; box-shadow:inset 0 0 3px #999; -moz-box-shadow:inset 0 0 3px #999; -webkit-box-shadow:inset 0 0 3px #999;}
+.xd .t2 .u6 .te .lr .cn{ border-bottom:1px solid #ddd;}
+.xd .t2 .u6 .te .lr .cn .liow label{ top:7px;}
+.xd .t2 .u6 .te .lr .cn .itx{ width:215px; padding:5px 60px 5px 4px;}
+.xd .t2 .u6 .te .lr .cn ul{ position:absolute; z-index:2; width:51px; top:2px; right:2px; *right:1px; font-size:0; *zoom:1;}
+.xd .t2 .u6 .te .lr .cn ul:after{ content:""; display:block; clear:both;}
+.xd .t2 .u6 .te .lr .cn li{ float:left; margin:0 0 1px 1px; display:inline;}
+.xd .t2 .u6 .te .lr .cn li button{ width:16px; height:11px; overflow:hidden; text-align:center; background-color:#eee;}
+.xd .t2 .u6 .te .lr .cn li.selected button{ background-color:#ccc;}
+.xd .t2 .u6 .te .lr .cn li.tl button{ background-position:-360px -255px;}
+.xd .t2 .u6 .te .lr .cn li.tc button{ background-position:-382px -255px;}
+.xd .t2 .u6 .te .lr .cn li.tr button{ background-position:-404px -255px;}
+.xd .t2 .u6 .te .lr .cn li.bl button{ background-position:-360px -264px;}
+.xd .t2 .u6 .te .lr .cn li.bc button{ background-position:-382px -264px;}
+.xd .t2 .u6 .te .lr .cn li.br button{ background-position:-404px -264px;}
+.xd .t2 .u6 .te .lr .th{ border-bottom:1px solid #ddd;}
+.xd .t2 .u6 .te .lr .th ul{ *zoom:1;}
+.xd .t2 .u6 .te .lr .th ul:after{ content:""; display:block; clear:both;}
+.xd .t2 .u6 .te .lr .th li{ float:left;}
+.xd .t2 .u6 .te .lr .th li button{ font-size:12px; width:70px; height:55px;}
+.xd .t2 .u6 .te .lr .th li.selected{ background-color:#ddd;}
+.xd .t2 .u6 .te .lr .th li.no button{ background-position:-283px -275px;}
+.xd .t2 .u6 .te .lr .th li.lt button{ background-position:-433px -275px;;}
+.xd .t2 .u6 .te .lr .th li.tp button{ background-position:-283px -335px;;}
+.xd .t2 .u6 .te .lr .th li.bh button{ background-position:-433px -335px;;}
+.xd .t2 .u6 .te .lr .th li span{ display:inline-block; padding-top:26px;}
+.xd .t2 .u6 .te .lr .wh{ border-bottom:1px solid #ddd;}
+.xd .t2 .u6 .te .lr .wh table{ border:0; width:100%;}
+.xd .t2 .u6 .te .lr .wh th{ border:0; text-align:right; padding:0; white-space:nowrap; font-weight:normal;}
+.xd .t2 .u6 .te .lr .wh td{ border:0; padding:0 0 0 10px; border-spacing:0;}
+.xd .t2 .u6 .te .lr .wh td button{ font-size:12px; width:23px; height:21px; color:#666;}
+.xd .t2 .u6 .te .lr .wh td button.selected{ border:1px solid #ccc; background-color:#767676; color:#fff; font-weight:bold;}
+.xd .t2 .u6 .te .lr .wh .itx{ width:60px; *margin-right:4px; background-color:#fff;}
+.xd .t2 .u6 .te .lr .btnArea{ position:relative; text-align:right; white-space:nowrap; padding:10px 0 0 0;}
+.xd .t2 .u6 .te .lr .btnArea .btn{ *margin-left:4px;}
+
+/* Layer - u6 (fr) */
+.xd .t2 .u6 .fr .lr{ left:auto; right:-1px;}
+.xd .t2 .u6 .fr .lr fieldset{ position:relative;}
+.xd .t2 .u6 .fr .lr legend{ font-size:12px; font-weight:bold; margin:0; display:block; line-height:normal;}
+.xd .t2 .u6 .fr .lr .itx{ width:120px; padding-right:70px;}
+.xd .t2 .u6 .fr .lr .find{ border-bottom:1px solid #ddd; padding:0 0 10px 0;}
+.xd .t2 .u6 .fr .lr .find .btn{ position:absolute; top:0; right:0; z-index:3;}
+.xd .t2 .u6 .fr .lr .replace legend{ padding:10px 0 0 0;}
+.xd .t2 .u6 .fr .lr .replace .liow{ margin:0 0 2px 0;}
+.xd .t2 .u6 .fr .lr .replace .liow .btn{ position:absolute; top:0; right:0; z-index:3;}
+.xd .t2 .u6 .fr .lr .frAll{ width:196px; margin:5px 0 0 0;}
+
+/* Edit */
+.xd .edit{ height:400px; overflow:auto; position:relative; z-index:2; *zoom:1; background:#fff; box-shadow:inset 0 0 3px #999; -moz-box-shadow:inset 0 0 3px #999; -webkit-box-shadow:inset 0 0 3px #999;}
+.xd .edit .xdcs{ position:relative; overflow:hidden !important; padding:20px 25px; cursor:text;}
+.xd .edit .xdsc{ border:0; padding:20px 25px; overflow:auto; background:transparent; font:12px "Courier New", Courier, monospace;}
+
+/* XDMW(XD Modal Windowed Layer) */
+.xdmw{ display:none; position:fixed; _position:absolute; top:0; left:0; width:100%; height:100%; z-index:1000;}
+.xdmw .bg{ position:absolute; top:0; left:0; width:100%; height:100%; background:#000; opacity:.5; filter:alpha(opacity=50); -ms-filter:"alpha(opacity=50)";}
+.xdmw .bg iframe{ border:0; width:100%; height:100%; display:none; _display:block; filter:alpha(opacity=0);}
+.xdmw .fg{ position:relative; top:10%; margin:0 auto; padding:37px 0 41px 0; width:80%; height:70%; background:#fff; border:3px solid #666; box-shadow:0 0 10px #000; -moz-box-shadow:0 0 10px #000; -webkit-box-shadow:0 0 10px #000;}
+.xdmw .item{ position:relative; width:100%; height:100%; font-size:12px;}
+.xdmw .item img{ border:0;}
+.xdmw .item .iHead{ position:absolute; top:-37px; left:0; width:100%; background-position:0 -1140px; background-repeat:repeat-x; border-bottom:1px solid #ccc; font-size:12px; padding:10px 0; *zoom:1;}
+.xdmw .item .iHead:after{ content:""; display:block; clear:both;}
+.xdmw .item .iHead h2{ float:left; _display:inline; margin:0 0 0 20px; padding:0 0 0 20px; background-position:0 -257px; font-size:12px; height:16px; line-height:16px;}
+.xdmw .item .iHead h2 em{ font-weight:normal; font-style:normal; color:#ccc;}
+.xdmw .item .iHead h2 strong{ color:#f60;}
+.xdmw .item .iBody{ position:relative; width:100%; height:100%; overflow:auto;}
+.xdmw .item .iBody .sn{ margin:0 20px; padding:20px 0; *zoom:1;}
+.xdmw .item .iBody .sn:after{ content:""; display:block; clear:both;}
+.xdmw .item .iFoot{ position:absolute; width:100%; height:21px; line-height:21px; left:0; bottom:-41px; padding:10px 0; border-top:1px solid #ccc; background-position:0 -1140px; background-repeat:repeat-x;}
+.xdmw .item .iFoot .btnArea{ margin:0 20px; *zoom:1;}
+.xdmw .item .iFoot .btnArea:after{ content:""; display:block; clear:both;}
+.xdmw .item .iFoot .btnArea.fl{ float:left;}
+.xdmw .item .iFoot .btnArea.fr{ float:right;}
+
+/* XDMW - AL(Attached List) */
+.xdmw .al{ display:none;}
+.xdmw .al.open{ display:block;}
+.xdmw .al .label{ width:275px; line-height:30px; font-weight:bold; margin:0; padding:0 0 0 24px;}
+.xdmw .al .img .label{ background-position:0 -282px;}
+.xdmw .al .mov .label{ background-position:0 -312px;}
+.xdmw .al .file .label{ background-position:0 -342px;}
+.xdmw .al .sn{ position:relative; border-bottom:1px solid #ddd;}
+.xdmw .al .sn ul{ margin:0; padding:0; *zoom:1;}
+.xdmw .al .sn ul:after{ content:""; display:block; clear:both;}
+.xdmw .al .sn li{ position:relative; display:inline-block; *display:inline; *zoom:1; vertical-align:top; margin:0 10px 10px 0;}
+.xdmw .al .sn .ob{ position:relative; display:block; text-decoration:none; white-space:nowrap; font-size:0; width:100px; height:100px; margin:0 0 5px 0; border:1px solid #ddd; text-align:center; line-height:98px;}
+.xdmw .al .sn .ob img{ vertical-align:middle;}
+.xdmw .al .sn .ob.ed{ background-color:#eee;}
+.xdmw .al .sn .ctr{ position:absolute; display:block; left:0; border:1px solid #bbb; font-size:12px; white-space:nowrap; background-position:0 -1140px; background-color:#f4f4f4; opacity:0; filter:alpha(opacity=0); -ms-filter:"alpha(opacity=0)"; box-shadow:0 0 2px #bbb; -moz-box-shadow:0 0 2px #bbb; -webkit-box-shadow:0 0 2px #bbb;}
+.xdmw .al .sn .ctr.show{opacity:1; filter:alpha(opacity=100); -ms-filter:"alpha(opacity=100)";}
+.xdmw .al .sn .ins{ width:100px; height:100px; top:0;}
+.xdmw .al .sn .del{ height:26px; top:74px; padding:0 5px;}
+.xdmw .al .sn .task{ position:absolute; top:27px; right:0; margin:0;}
+.xdmw .al .sn .task button{ padding:0 0 0 15px; font-size:12px;}
+.xdmw .al .sn .task .all{ background-position:-586px -260px;}
+.xdmw .al .sn .task .insert{ background-position:-586px -288px;}
+.xdmw .al .sn .task .up{ padding:0; border-bottom:1px solid;}
+.xdmw .al .sn .progress{ display:none; width:80px; background:#ddd;}
+.xdmw .al .sn .progress .bar{ display:block; background:#999; height:6px;}
+.xdmw .al .sn .uploading .progress{ display:inline-block;}
+.xdmw .al .sn .nofile{ display:none; position:absolute; top:27px; right:0; margin:0; text-align:center; color:#767676;}
+.xdmw .al .sn .nofile .up{ font-size:12px; border-bottom:1px solid;}
+.xdmw .al .none .task{ display:none;}
+.xdmw .al .none .nofile{ display:block;}
+.xdmw .al .file .ob{ text-indent:-900px;}
+.xdmw .al .file .uploading .ob{ text-indent:0px; background-image:none;}
+.xdmw .al .file .pdf{ background-position:0 -400px;}
+.xdmw .al .file .doc,
+.xdmw .al .file .docx{ background-position:-100px -400px;}
+.xdmw .al .file .hwp{ background-position:-200px -400px;}
+.xdmw .al .file .ppt,
+.xdmw .al .file .ppt{ background-position:-300px -400px;}
+.xdmw .al .file .pps{ background-position:-400px -400px;}
+.xdmw .al .file .txt{ background-position:-500px -400px;}
+.xdmw .al .file .rtf{ background-position:0 -500px;}
+.xdmw .al .file .xls,
+.xdmw .al .file .xlsx{ background-position:-100px -500px;}
+.xdmw .al .file .png{ background-position:-200px -500px;}
+.xdmw .al .file .jpg{ background-position:-300px -500px;}
+.xdmw .al .file .gif{ background-position:-400px -500px;}
+.xdmw .al .file .bmp{ background-position:-500px -500px;}
+.xdmw .al .file .tif{ background-position:0 -600px;}
+.xdmw .al .file .raw{ background-position:-100px -600px;}
+.xdmw .al .file .avi{ background-position:-200px -600px;}
+.xdmw .al .file .wmv{ background-position:-300px -600px;}
+.xdmw .al .file .mov{ background-position:-400px -600px;}
+.xdmw .al .file .mpg{ background-position:-500px -600px;}
+.xdmw .al .file .flv{ background-position:0 -700px;}
+.xdmw .al .file .divx{ background-position:-100px -700px;}
+.xdmw .al .file .mp3{ background-position:-200px -700px;}
+.xdmw .al .file .wma{ background-position:-300px -700px;}
+.xdmw .al .file .wav{ background-position:-400px -700px;}
+.xdmw .al .file .aac{ background-position:-500px -700px;}
+.xdmw .al .file .flac{ background-position:0 -800px;}
+.xdmw .al .file .psd{ background-position:-100px -800px;}
+.xdmw .al .file .ai{ background-position:-200px -800px;}
+.xdmw .al .file .svg{ background-position:-300px -800px;}
+.xdmw .al .file .xml{ background-position:-400px -800px;}
+.xdmw .al .file .html{ background-position:-500px -800px;}
+.xdmw .al .file .css{ background-position:0 -900px;}
+.xdmw .al .file .iso{ background-position:-100px -900px;}
+.xdmw .al .file .zip{ background-position:-200px -900px;}
diff --git a/modules/editor/skins/xeed/xd.gif b/modules/editor/skins/xeed/xd.gif
new file mode 100644
index 000000000..6fcf634f1
Binary files /dev/null and b/modules/editor/skins/xeed/xd.gif differ
diff --git a/modules/editor/skins/xeed/xdcs.css b/modules/editor/skins/xeed/xdcs.css
new file mode 100644
index 000000000..2a89435a3
--- /dev/null
+++ b/modules/editor/skins/xeed/xdcs.css
@@ -0,0 +1,22 @@
+@charset "utf-8";
+/* XDCS = Xpress eDitor Content Style */
+.xdcs{ font-size:12px; line-height:1.5; overflow:hidden !important; *zoom:1;}
+.xdcs .h1{ font-size:24px; margin:1em 0;}
+.xdcs .h2{ font-size:20px; margin:1em 0;}
+.xdcs .h3{ font-size:18px; margin:1em 0;}
+.xdcs .h4{ font-size:16px; margin:1em 0;}
+.xdcs .h5{ font-size:14px; margin:1em 0;}
+.xdcs .h6{ font-size:12px; margin:1em 0;}
+.xdcs .bq{ border-left:3px solid #ccc; padding-left:1em; margin:1em 0;}
+.xdcs .bx{ border:1px dotted #999; margin:1em 0; padding:0 2em; background:#fafafa;}
+.xdcs .ts,
+.xdcs .ts th,
+.xdcs .ts td{ border:1px solid #bbb;}
+.xdcs .ts caption{ font-weight:bold;}
+.xdcs .ts th,
+.xdcs .ts td{ padding:2px 5px;}
+.xdcs .ts{ border-collapse:collapse; margin:1em 0;}
+.xdcs .ts th{ text-align:left; background-color:#fafafa;}
+.xdcs .ts td{ text-align:left;}
+.xdcs .toc{ margin:1em 0; border:1px solid #ddd; background:#fafafa;}
+.xdcs .toc ul ul{ padding-left:1em; margin:0;}