/*global window:true, define:true, document:true */
/*
* Aloha Editor
* Author & Copyright (c) 2010 Gentics Software GmbH
* aloha-sales@gentics.com
* Licensed unter the terms of http://www.aloha-editor.com/license.html
*/
define(
['aloha',
'aloha/jquery',
'aloha/plugin',
'aloha/floatingmenu',
'i18n!characterpicker/nls/i18n',
'i18n!aloha/nls/i18n',
'css!characterpicker/css/characterpicker.css'],
function (Aloha, jQuery, Plugin, FloatingMenu, i18n, i18nCore) {
var
GENTICS = window.GENTICS;
function CharacterOverlay(onSelectCallback) {
var self = this;
self.$node = jQuery('
');
// don't let the mousedown bubble up. otherwise there won't be an activeEditable
self.$node.mousedown(function (e) {
return false;
});
self.onSelectCallback = onSelectCallback;
self.$tbody = self.$node.find('tbody');
self.$node.appendTo(jQuery('body'));
self._initHideOnDocumentClick();
self._initHideOnEsc();
self._initCursorFocus(onSelectCallback);
self._initEvents();
}
CharacterOverlay.prototype = {
/**
* Show the character overlay at the insert button's position
* @param insertButton insert button
*/
show: function (insertButton) {
var self = this;
// position the overlay relative to the insert-button
self.$node.css(jQuery(insertButton).offset());
self.$node.show();
// focus the first character
self.$node.find('.focused').removeClass('focused');
jQuery(self.$node.find('td')[0]).addClass('focused');
},
/**
* Set the characters, that shall be selectable
* @param {string} characters characters in a string, separated by spaces
*/
setCharacters: function (characters) {
this._createCharacterButtons(characters);
},
_initHideOnDocumentClick: function () {
var self = this;
// if the user clicks somewhere outside of the layer, the layer should be closed
// stop bubbling the click on the create-dialog up to the body event
self.$node.click(function (e) {
e.stopPropagation();
});
// hide the layer if user clicks anywhere in the body
jQuery('body').click(function (e) {
var overlayVisibleAndNotTarget
= (self.$node.css('display') === 'table') &&
(e.target !== self.$node[0]) &&
// don't consider clicks to the 'show' button.
!jQuery(e.target).is('button.aloha-button-characterpicker');
if (overlayVisibleAndNotTarget) {
self.$node.hide();
}
});
},
_initHideOnEsc: function () {
var self = this;
// escape closes the overlay
jQuery(document).keyup(function (e) {
var overlayVisibleAndEscapeKeyPressed = (self.$node.css('display') === 'table') && (e.keyCode === 27);
if (overlayVisibleAndEscapeKeyPressed) {
self.$node.hide();
}
});
},
_initCursorFocus: function (onSelectCallback) {
var self = this;
// you can navigate through the character table with the arrow keys
// and select one with the enter key
var $current, $next, $prev, $nextRow, $prevRow;
var movements = {
13: function select() {
$current = self.$node.find('.focused');
self.$node.hide();
onSelectCallback($current.text());
},
37: function left() {
$current = self.$node.find('.focused');
$prev = $current.prev().addClass('focused');
if ($prev.length > 0) {
$current.removeClass('focused');
}
},
38: function up() {
$current = self.$node.find('.focused');
$prevRow = $current.parent().prev();
if ($prevRow.length > 0) {
$prev = jQuery($prevRow.children()[$current.index()]).addClass('focused');
if ($prev.length > 0) {
$current.removeClass('focused');
}
}
},
39: function right() {
$current = self.$node.find('.focused');
$next = $current.next().addClass('focused');
if ($next.length > 0) {
$current.removeClass('focused');
}
},
40: function down() {
$current = self.$node.find('.focused');
$nextRow = $current.parent().next();
if ($nextRow.length > 0) {
$next = jQuery($nextRow.children()[$current.index()]).addClass('focused');
if ($next.length > 0) {
$current.removeClass('focused');
}
}
}
};
jQuery(document).keydown(function (e) {
e.stopPropagation();
var isOverlayVisible = self.$node.css('display') === 'table';
if (isOverlayVisible) {
// check if there is a move-command for the pressed key
var moveCommand = movements[e.keyCode];
if (moveCommand) {
moveCommand();
return false;
}
}
});
},
_initEvents: function () {
var self = this;
// when the editable is deactivated, hide the layer
Aloha.bind('aloha-editable-deactivated', function (event, rangeObject) {
self.$node.hide();
});
},
_createCharacterButtons: function (characters) {
var self = this;
function htmlEntityToSingleCharacter(character) {
// isn't there any better way?
var textarea = document.createElement('textarea');
textarea.innerHTML = character;
return textarea.value;
}
function mkButton(c) {
var character = htmlEntityToSingleCharacter(c);
return jQuery('' + character + ' | ')
.mouseover(function () {
jQuery(this).addClass('mouseover');
})
.mouseout(function () {
jQuery(this).removeClass('mouseover');
})
.click(function (e) {
self.$node.hide();
self.onSelectCallback(character);
return false;
});
}
function addRow() {
return jQuery('
').appendTo(self.$tbody);
}
var characterList = jQuery.grep(
characters.split(' '),
function filterOutEmptyOnces(e) {
return e !== '';
}
);
var i = 0, char;
var $row;
// remove existing rows
self.$tbody.find('tr').remove();
while ((char = characterList[i])) {
// make a new row every 15 characters
if (((i % 15) === 0)) {
$row = addRow();
}
mkButton(char).appendTo($row);
i++;
}
}
};
return Plugin.create('characterpicker', {
_constructor: function () {
this._super('characterpicker');
},
languages: ['en'],
/**
* Default configuration
*/
config: '& " ¢ € £ ¥ © ® ™ ‰ µ · • … ′ ″ § ¶ ß ‹ › « » ‘ ’ “ ” ‚ „ < > ≤ ≥ – — ¯ ‾ ¤ ¦ ¨ ¡ ¿ ˆ ˜ ° − ± ÷ ⁄ × ¹ ² ³ ¼ ½ ¾ ƒ ∫ ∑ ∞ √ ∼ ≅ ≈ ≠ ≡ ∈ ∉ ∋ ∏ ∧ ∨ ¬ ∩ ∪ ∂ ∀ ∃ ∅ ∇ ∗ ∝ ∠ ´ ¸ ª º † ‡ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö Ø Œ Š Ù Ú Û Ü Ý Ÿ Þ à á â ã ä å æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö ø œ š ù ú û ü ý þ ÿ Α Β Γ Δ Ε Ζ Η Θ Ι Κ Λ Μ Ν Ξ Ο Π Ρ Σ Τ Υ Φ Χ Ψ Ω α β γ δ ε ζ η θ ι κ λ μ ν ξ ο π ρ ς σ τ υ φ χ ψ ω ℵ ϖ ℜ ϑ ϒ ℘ ℑ ← ↑ → ↓ ↔ ↵ ⇐ ⇑ ⇒ ⇓ ⇔ ∴ ⊂ ⊃ ⊄ ⊆ ⊇ ⊕ ⊗ ⊥ ⋅ ⌈ ⌉ ⌊ ⌋ 〈 〉 ◊ ♠ ♣ ♥ ♦',
init: function () {
var self = this;
self.insertButton = new Aloha.ui.Button({
'name': 'characterpicker',
'iconClass': 'aloha-button-characterpicker',
'size': 'small',
'onclick': function (element, event) {
self.characterOverlay.show(element.btnEl.dom);
},
'tooltip': i18n.t('button.addcharacter.tooltip'),
'toggle': false
});
FloatingMenu.addButton(
'Aloha.continuoustext',
self.insertButton,
i18nCore.t('floatingmenu.tab.insert'),
1
);
self.characterOverlay = new CharacterOverlay(self.onCharacterSelect);
// when an editable is activated, we get the config for the editable
Aloha.bind('aloha-editable-activated', function (event, data) {
var config = self.getEditableConfig(data.editable.obj);
// make a space separated string out of arrays
if (jQuery.isArray(config)) {
config = config.join(' ');
}
if (config) {
self.characterOverlay.setCharacters(config);
self.insertButton.show();
} else {
self.insertButton.hide();
}
});
},
/**
* insert a character after selecting it from the list
*/
onCharacterSelect: function (character) {
var self = this;
var range = Aloha.Selection.getRangeObject();
if (Aloha.activeEditable) {
var charNode = jQuery(document.createTextNode(character));
GENTICS.Utils.Dom.insertIntoDOM(
charNode,
range,
jQuery(Aloha.activeEditable.obj),
true
);
range.startContainer = range.endContainer = charNode.get(0);
range.startOffset = range.endOffset = charNode.length;
range.select();
}
}
});
});
// vim: noexpandtab