/*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