(function($) {
var BUE = window.BUE = window.BUE || {preset: {}, templates: {}, instances: [], preprocess: {}, postprocess: {}};
// Get editor settings from Drupal.settings and process preset textareas.
BUE.behavior = function(context) {
var set = Drupal.settings.BUE || null, tpls = BUE.templates, pset = BUE.preset;
if (set) {
$.each(set.templates, function (id, tpl) {
tpls[id] = tpls[id] || $.extend({}, tpl);
});
$.extend(pset, set.preset);
set.templates = {};
set.preset = {};
}
$.each(pset, function (tid, tplid) {
BUE.processTextarea($('#'+ tid, context).get(0), tplid);
});
// Fix enter key on textfields triggering button click.
$('input:text', context).bind('keydown.bue', BUE.eFixEnter);
};
// Integrate editor template into textarea T
BUE.processTextarea = function (T, tplid) {
if (!T || !BUE.templates[tplid] || !(T = $(T).filter('textarea')[0])) return false;
// Check visibility on the element-level only.
if (T.style.display == 'none' || T.style.visibility == 'hidden') return false;
if (T.bue) return T.bue;
var E = new BUE.instance(T, tplid);
!BUE.active || BUE.active.textArea.disabled ? E.activate() : E.accesskeys(false);
// Pre&post process.
for (var i in BUE.preprocess) BUE.preprocess[i](E, $);
for (var i in BUE.postprocess) BUE.postprocess[i](E, $);
return E;
};
// Create an editor instance
BUE.instance = function (T, tplid) {
var i = BUE.instances.length, E = T.bue = BUE.instances[i] = this;
E.index = i;
E.textArea = T;
E.tplid = tplid;
E.tpl = BUE.templates[tplid];
E.bindex = null;
E.safeToPreview = T.value.indexOf('<') == -1;
E.UI = BUE.$html(BUE.theme(tplid).replace(/\%n/g, i)).insertBefore(T).bind('keydown.bue', BUE.eUIKeydown);
E.buttons = $('.bue-button', E.UI).each(function(i, B) {
var arr = B.id.split('-');
$($.extend(B, {eindex: arr[1], bid: arr[3], bindex: i})).bind('click.bue', BUE.eButtonClick);
}).get();
$(T).bind('focus.bue', BUE.eTextareaFocus);
};
// Execute button's click event
BUE.buttonClick = function (eindex, bindex) { try {
var E = BUE.instances[eindex].activate();
var domB = E.buttons[bindex];
var tplB = E.tpl.buttons[domB.bid];
var content = tplB[1];
E.bindex = bindex;
E.dialog.close();
if (tplB[4]) {
tplB[4](E, $);
}
else if (content) {
var arr = content.split('%TEXT%');
if (arr.length == 2) E.tagSelection(arr[0], arr[1]);
else E.replaceSelection(arr.length == 1 ? content : arr.join(E.getSelection()), 'end');
}
!(domB.pops || domB.stayClicked) && E.focus();
} catch (e) {alert(e.name +': '+ e.message);}
return false;
};
// Return html for editor templates.
BUE.theme = function (tplid) {
var tpl = BUE.templates[tplid] || {html: ''}, html = '', sprite;
if (typeof tpl.html == 'string') return tpl.html;
// Load sprite
if (sprite = tpl.sprite) {
var surl = (new Image()).src = sprite.url, sunit = sprite.unit, sx1 = sprite.x1;
$(document.body).append('');
}
var access = $.browser.mozilla && 'Shift + Alt' || ($.browser.msie || window.chrome) && 'Alt', title, content, icon, key, func;
// Create html for buttons. B(0-title, 1-content, 2-icon or caption, 3-accesskey) and 4-function for js buttons
for (var B, isimg, src, type, btype, attr, alt, i = 0, s = 0; B = tpl.buttons[i]; i++) {
// Empty button.
if (B.length == 0) {
s++;
continue;
}
title = B[0], content = B[1], icon = B[2], key = B[3], func = null;
// Set button function
if (content.substr(0, 3) == 'js:') {
func = B[4] = new Function('E', '$', content.substr(3));
}
isimg = (/\.(png|gif|jpg)$/i).test(icon);
// Theme button.
if (title.substr(0, 4) == 'tpl:') {
html += func ? (func(null, $) || '') : content;
html += icon ? (''+ (isimg ? '' : icon) +'') : '';
continue;
}
// Text button
if (!isimg) {
type = 'button', btype = 'text', attr = 'value="'+ icon +'"';
}
else {
type = 'image';
// Sprite button
if (sprite) {
btype = 'sprite', attr = 'src="'+ sx1 +'" style="background-position: -'+ (s * sunit) +'px 0;"';
s++;
}
// Image button
else {
btype = 'image', attr = 'src="'+ tpl.iconpath +'/'+ icon +'"';
}
}
alt = title + (key ? '('+ key +')' : '');
title += access && key ? ' ('+ access +' + '+ key +')' : '';
html += '';
}
return tpl.html = '
'+ html +'
';
};
// Cross browser selection handling. 0-1=All, 2=IE, 3=Opera
BUE.mode = (window.getSelection || document.getSelection) ? ($.browser.opera ? 3 : 1) : (document.selection && document.selection.createRange ? 2 : 0 );
// New line standardization. At least make them represented by a single char.
BUE.text = BUE.processText = BUE.mode < 2 ? function (s) {return s.toString()} : function (s) {return s.toString().replace(/\r\n/g, '\n')};
// Create selection in a textarea
BUE.selMake = BUE.mode == 2 ? function (T, start, end) {
range = T.createTextRange();
range.collapse();
range.moveEnd('character', end);
range.moveStart('character', start);
range.select();
} :
BUE.mode == 3 ? function (T, start, end) {
var text = BUE.text(T.value), i = text.substring(0, start).split('\n').length, j = text.substring(start, end).split('\n').length;
T.setSelectionRange(start + i -1 , end + i + j - 2);
} :
function (T, start, end) {
T.setSelectionRange(start, end);
};
// Return the selection coordinates in a textarea
BUE.selPos = BUE.mode == 2 ? function (T) {
T.focus();
var orange = document.selection.createRange(), range = orange.duplicate();
range.moveToElementText(T);
range.setEndPoint('EndToEnd', orange);
var otext = orange.text, olen = otext.length, prelen = range.text.length - olen;
var start = prelen - (T.value.substr(0, prelen).split('\r\n').length - 1);
start && range.moveStart('character', start);
for (; range.compareEndPoints('StartToStart', orange) < 0; start++) {
range.moveStart('character', 1);
}
var end = start + olen - (otext.split('\r\n').length - 1);
for (; range.compareEndPoints('EndToStart', orange) > 0; end++) {
range.moveEnd('character', -1);
if (range.text.length != olen) break;
}
return {start: start, end: end};
} :
BUE.mode == 3 ? function (T) {
var start = T.selectionStart || 0, end = T.selectionEnd || 0, val = T.value;
var i = val.substring(0, start).split('\r\n').length, j = val.substring(start, end).split('\r\n').length;
return {start: start - i + 1, end: end - i - j + 2};
} :
function (T) {
return {start: T.selectionStart || 0, end: T.selectionEnd || 0}
};
// Enter key fixer for text fields
BUE.eFixEnter = function(e) {
e.keyCode == 13 && (BUE.enterKeyTime = new Date());
};
// Button click handler
BUE.eButtonClick = function(e) {
return !(BUE.enterKeyTime && new Date() - BUE.enterKeyTime < 500) && BUE.buttonClick(this.eindex, this.bindex);
};
// Textarea focus handler
BUE.eTextareaFocus = function(e) {
this.bue && !this.bue.dialog.esp && this.bue.activate();
};
// UI keydown handler
BUE.eUIKeydown = function(e) {
if (e.keyCode != 37 && e.keyCode != 39) return;
var len, E = BUE.instances[this.id.split('-').pop()];
if (E && (len = E.buttons.length)) {
var A = document.activeElement, i = Math.max(-1, (A && A.eindex == E.index ? A.bindex : -1) + e.keyCode - 38) + len;
E.buttons[i % len].focus();
}
};
// Html 2 jquery. Faster than $(html)
BUE.$html = function(s){
var div = document.createElement('div');
div.innerHTML = s;
return $(div.childNodes);
};
// Backward compatibility.
window.editor = window.editor || BUE;
// Initiate bueditor
$(document).ready(function () {
(Drupal.behaviors.BUE = BUE.behavior)(document);
});
})(jQuery);
// Bueditor instance methods
(function(E) {
// Focus on editor textarea.
E.focus = function () {
this.textArea.focus();
return this;
};
// Return textarea content
E.getContent = function () {
return BUE.text(this.textArea.value);
};
// Set textarea content
E.setContent = function (content) {
var T = this.textArea, st = T.scrollTop;
T.value = content;
T.scrollTop = st;
return this;
};
// Return selected text
E.getSelection = function () {
var pos = this.posSelection();
return this.getContent().substring(pos.start, pos.end);
};
// Replace selected text
E.replaceSelection = function (txt, cursor) {
var E = this, pos = E.posSelection(), content = E.getContent(), txt = BUE.text(txt);
var end = cursor == 'start' ? pos.start : pos.start+txt.length, start = cursor == 'end' ? end : pos.start;
E.setContent(content.substr(0, pos.start) + txt + content.substr(pos.end));
return E.makeSelection(start, end);
};
// Wrap selected text.
E.tagSelection = function (left, right, cursor) {
var E = this, pos = E.posSelection(), content = E.getContent();
var left = BUE.text(left), right = BUE.text(right), llen = left.length;
var end = cursor == 'start' ? pos.start+llen : pos.end+llen, start = cursor == 'end' ? end : pos.start+llen;
E.setContent(content.substr(0, pos.start) + left + content.substring(pos.start, pos.end) + right + content.substr(pos.end));
return E.makeSelection(start, end);
};
// Make a new selection
E.makeSelection = function (start, end) {
var E = this;
if (end === undefined || end < start) end = start;
BUE.selMake(E.textArea, start, end);
E.dialog.esp && (E.dialog.esp = {start: start, end: end}) || E.focus();
return E;
};
// Return selection coordinates.
E.posSelection = function () {
return this.dialog.esp || BUE.selPos(this.textArea);
};
// Enable/disable editor buttons
E.buttonsDisabled = function (state, bindex) {
for (var B, i=0; B = this.buttons[i]; i++) {
B.disabled = i == bindex ? !state : state;
}
return this;
};
// Make active/custom button stay clicked
E.stayClicked = function (state, bindex) {
var B = this.buttons[bindex === undefined ? this.bindex : bindex];
B && jQuery(B)[state ? 'addClass' : 'removeClass']('stay-clicked') && (B.stayClicked = state || false);
return this;
};
// Enable/disable button accesskeys
E.accesskeys = function (state) {
for (var B, i=0; B = this.buttons[i]; i++) {
B.accessKey = state ? this.tpl.buttons[B.bid][3] : '';
}
return this;
};
// Activate editor and make it BUE.active
E.activate = function() {
var E = this, A = BUE.active || null;
if (E == A) return E;
A && A.accesskeys(false) && E.accesskeys(true);
return BUE.active = E;
};
// Reserve dialog and quickPop
var pop = E.dialog = E.quickPop = BUE.dialog = BUE.quickPop = {};
pop.open = pop.close = function(){};
})(BUE.instance.prototype);