From: Michael M Slusarz Date: Thu, 16 Jul 2009 19:56:52 +0000 (-0600) Subject: SpellChecker imple moved to Horde_Ajax. X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=4f967f3aac6ef5bb1408a7de2ce0b520c0bbc71b;p=horde.git SpellChecker imple moved to Horde_Ajax. --- diff --git a/imp/js/src/KeyNavList.js b/imp/js/src/KeyNavList.js deleted file mode 100644 index 1d3d48e0f..000000000 --- a/imp/js/src/KeyNavList.js +++ /dev/null @@ -1,207 +0,0 @@ -/** - * Reuseable keyboard or mouse driven list component. Based on - * Scriptaculous' AutoCompleter. - * - * Copyright 2005-2009 The Horde Project (http://www.horde.org/) - * - * See the enclosed file COPYING for license information (GPL). If you - * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. - */ - -var KeyNavList = Class.create({ - // Vars used and defaulting to empty: - // active, entryCount - - initialize: function(element, options) - { - var clickfunc = this.onClick.bindAsEventListener(this), - entry, - i = 0, - overfunc = this.onHover.bindAsEventListener(this); - - this.element = $(element); - this.options = options || {}; - this.index = -1; - - this.entryCount = this.element.firstDescendant().childElements().size(); - for (; i < this.entryCount; i++) { - entry = this.getEntry(i); - entry.writeAttribute('autocompleteIndex', i); - entry.observe('click', clickfunc); - entry.observe('mouseover', overfunc); - } - - this.options.onShow = this.options.onShow || - function(elt) { new Effect.Appear(elt, { duration: 0.15 }); }; - this.options.onHide = this.options.onHide || - function(elt) { new Effect.Fade(elt, { duration: 0.15 }); }; - - this.element.observe('blur', this.onBlur.bind(this)); - document.observe('keypress', this.onKeyPress.bindAsEventListener(this)); - }, - - show: function() - { - this.active = true; - if (!this.element.visible()) { - this.options.onShow(this.element); - } - if (!this.iefix && - (navigator.appVersion.indexOf('MSIE') > 0) && - (this.element.getStyle('position') == 'absolute')) { - this.element.insert({ after: - ''}); - this.iefix = $(this.element.id + '_iefix'); - } - if (this.iefix) { - setTimeout(this.fixIEOverlapping.bind(this), 50); - } - }, - - fixIEOverlapping: function() - { - this.iefix.clonePosition(this.element).setStyle({ zIndex: 1 }).show(); - this.element.setStyle({ zIndex: 2 }); - }, - - hide: function() - { - this.active = false; - this.stopIndicator(); - if (this.element.visible()) { - this.options.onHide(this.element); - } - if (this.iefix) { - this.iefix.hide(); - } - }, - - startIndicator: function() - { - if (this.options.indicator) { - $(this.options.indicator).show(); - } - }, - - stopIndicator: function() - { - if (this.options.indicator) { - $(this.options.indicator).hide(); - } - }, - - onKeyPress: function(e) - { - if (!this.active) { - return; - } - - switch (e.keyCode) { - case Event.KEY_TAB: - case Event.KEY_RETURN: - this.selectEntry(); - e.stop(); - - case Event.KEY_ESC: - this.hide(); - this.active = false; - e.stop(); - return; - - case Event.KEY_LEFT: - case Event.KEY_RIGHT: - return; - - case Event.KEY_UP: - this.markPrevious(); - this.render(); - e.stop(); - return; - - case Event.KEY_DOWN: - this.markNext(); - this.render(); - e.stop(); - return; - } - }, - - onHover: function(e) - { - var element = e.findElement('LI'), - index = parseInt(element.readAttribute('autocompleteIndex')); - if (this.index != index) { - this.index = index; - this.render(); - } - e.stop(); - }, - - onClick: function(e) - { - var element = e.findElement('LI'); - this.index = parseInt(element.readAttribute('autocompleteIndex')); - this.selectEntry(); - this.hide(); - e.stop(); - }, - - onBlur: function() - { - setTimeout(this.hide.bind(this), 250); - this.active = false; - }, - - render: function() - { - if (this.entryCount > 0) { - for (var i = 0; i < this.entryCount; i++) { - [ this.getEntry(i) ].invoke(this.index == i ? 'addClassName' : 'removeClassName', 'selected'); - } - this.show(); - this.active = true; - } else { - this.hide(); - this.active = false; - } - }, - - markPrevious: function() - { - if (this.index > 0) { - this.index--; - } else { - this.index = this.entryCount - 1; - } - }, - - markNext: function() - { - if (this.index < this.entryCount - 1) { - this.index++; - } else { - this.index = 0; - } - }, - - getEntry: function(index) - { - return this.element.down().down(index); - }, - - getCurrentEntry: function() - { - return this.getEntry(this.index); - }, - - selectEntry: function() - { - this.active = false; - if (typeof this.options.onChoose == 'function') { - this.options.onChoose(this.getCurrentEntry()); - } - } - -}); diff --git a/imp/js/src/SpellChecker.js b/imp/js/src/SpellChecker.js deleted file mode 100644 index 8213989a4..000000000 --- a/imp/js/src/SpellChecker.js +++ /dev/null @@ -1,339 +0,0 @@ -/** - * This spell checker was inspired by work done by Garrison Locke, but - * was rewritten almost completely by Chuck Hagenbuch to use - * Prototype/Scriptaculous. - * - * Copyright 2005-2009 The Horde Project (http://www.horde.org/) - * - * See the enclosed file COPYING for license information (GPL). If you - * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. - */ - -var SpellChecker = Class.create({ - - // Vars used and defaulting to null: - // bad, choices, choicesDiv, curWord, htmlArea, htmlAreaParent, locale, - // localeChoices, onAfterSpellCheck, onBeforeSpellCheck, onNoError, - // reviewDiv, statusButton, statusClass, suggestions, target, url - options: {}, - resumeOnDblClick: true, - state: 'CheckSpelling', - - // url = (string) URL of specllchecker handler - // target = (string) DOM ID of message data - // statusButton = (string/element) DOM ID or element of the status button - // bs = (array) Button states - // locales = (array) List of locales - // sc = (string) Status class - initialize: function(url, target, statusButton, bs, locales, sc) - { - var d, popdown, ul; - - this.url = url; - this.target = target; - this.statusButton = $(statusButton); - this.buttonStates = bs; - this.statusClass = sc || ''; - - this.statusButton.observe('click', this.process.bindAsEventListener(this)); - this.options.onComplete = this.onComplete.bind(this); - - if (locales) { - d = new Element('DIV', { className: 'autocomplete', id: 'spellcheckpopdown' }).setStyle({ position: 'absolute' }).hide(); - ul = new Element('UL'); - $H(locales).each(function(pair) { - ul.insert({ bottom: new Element('LI', { lc: pair.key }).update(pair.value) }); - }); - d.insert({ bottom: ul }); - - this.localeChoices = new KeyNavList(d, { onChoose: this.setLocale.bindAsEventListener(this) }); - - popdown = new Element('SPAN', { className: 'spellcheckPopdownImg' }).observe('click', function() { - $('spellcheckpopdown').clonePosition(this.statusButton, { - setHeight: false, - setWidth: false, - offsetTop: this.statusButton.getHeight() - }); - this.localeChoices.show(); - }.bind(this)); - - this.statusButton.insert({ after: popdown }); - - $(document.body).insert(d); - } - - // We need to monitor clicks to know when to go out of - // showSuggestions mode. - document.observe('click', this.onClick.bindAsEventListener(this)); - - this.setStatus('CheckSpelling'); - }, - - setLocale: function(e) - { - this.locale = e.readAttribute('lc'); - }, - - targetValue: function() - { - var input = $(this.target); - return (Object.isUndefined(input.value)) ? input.innerHTML : input.value; - }, - - process: function(e) - { - switch (this.state) { - case 'CheckSpelling': - this.spellCheck(); - break; - - case 'ResumeEdit': - this.resume(); - break; - } - - e.stop(); - }, - - // noerror - (function) A callback function to run if no errors are - // identified. If not specified, will remain in spell check - // mode even if no errors are present. - spellCheck: function(noerror) - { - if (this.onBeforeSpellCheck) { - this.onBeforeSpellCheck(); - } - - var opts = Object.clone(this.options), - p = $H(), - url = this.url; - - this.setStatus('Checking'); - this.removeChoices(); - - this.onNoError = noerror; - - p.set(this.target, this.targetValue()); - opts.parameters = p.toQueryString(); - - if (this.locale) { - url += '/locale=' + this.locale; - } - if (this.htmlAreaParent) { - url += '/html=1'; - } - - new Ajax.Request(url, opts); - }, - - onClick: function(e) - { - // If the language dropdown is currently active, and the click - // got here, then we should hide the language dropdown. - if (this.localeChoices) { - this.localeChoices.hide(); - } - - // If we're not currently showing any choices, then we return - // and let the click go. - if (!this.choicesDiv) { - return true; - } - - // If the click was on the word that we're currently showing - // results for, then we do nothing and let the list be - // redisplayed by the link's own onclick handler. - var link = e.findElement('SPAN'); - if (link && link == this.curWord) { - return true; - } - - // The KeyNavList will have already handled any valid clicks, - // so that just leaves the rest of the window - in this case - // we want to hide the KeyNavList and reset curWord without - // changing anything. - this.removeChoices(); - this.curWord = null; - }, - - onComplete: function(request) - { - var bad, content, d, - i = 0, - input = $(this.target), - result = request.responseJSON; - - this.removeChoices(); - - if (Object.isUndefined(result)) { - this.setStatus('Error'); - return; - } - - this.suggestions = result.suggestions || []; - - if (this.onNoError && !this.suggestions.size()) { - this.setStatus('CheckSpelling'); - this.onNoError(); - return; - } - - bad = result.bad || []; - - content = this.targetValue(); - content = this.htmlAreaParent - ? content.replace(/\r?\n/g, '') - : content.replace(/\r?\n/g, '~~~').escapeHTML(); - - $A(bad).each(function(node) { - var re_text = '' + node + ''; - content = content.replace(new RegExp("(?:^|\\b)" + RegExp.escape(node) + "(?:\\b|$)", 'g'), re_text); - - // Go through and see if we matched anything inside a tag (i.e. - // class/spellcheckIncorrect is often matched if using a - // non-English lang). - content = content.replace(new RegExp("(<[^>]*)" + RegExp.escape(re_text) + "([^>]*>)", 'g'), '\$1' + node + '\$2'); - }, this); - - if (!this.reviewDiv) { - this.reviewDiv = new Element('div', { className: input.readAttribute('className') + ' spellcheck' }).setStyle({ overflow: 'auto' }); - if (this.resumeOnDblClick) { - this.reviewDiv.observe('dblclick', this.resume.bind(this)); - } - } - - d = input.getDimensions(); - this.reviewDiv.setStyle({ width: d.width + 'px', height: d.height + 'px'}); - - if (!this.htmlAreaParent) { - content = content.replace(/~~~/g, '
'); - } - this.reviewDiv.update(content); - - // Now attach results behavior to each link. - this.reviewDiv.select('span.spellcheckIncorrect').invoke('observe', 'click', this.showSuggestions.bindAsEventListener(this)); - - // Falsify links - this.reviewDiv.select('A').invoke('observe', 'click', Event.stop); - - if (this.htmlAreaParent) { - // Fix for Safari 3/fckeditor - Ticket #6909 - Element.hide(this.htmlArea); - $(this.htmlAreaParent).insert({ bottom: this.reviewDiv }); - } else { - input.hide().insert({ before: this.reviewDiv }); - } - - this.setStatus('ResumeEdit'); - }, - - showSuggestions: function(e) - { - var pos, - elt = e.element(), - ul = new Element('UL'); - - try { - pos = elt.viewportOffset(); - } catch (err) { - // Fix for Safari 3/fckeditor - Ticket #6909 - pos = [ e.pointerX(), e.pointerY() ]; - } - - this.removeChoices(); - this.choicesDiv = new Element('DIV', { className: 'autocomplete' }).setStyle({ left: pos[0] + 'px', top: (pos[1] + 16) + 'px' }).hide(); - this.curWord = elt; - - $A(this.suggestions[this.curWord.readAttribute('index')]).each(function(node) { - ul.insert({ bottom: new Element('LI').update(node) }); - }); - this.choicesDiv.insert({ bottom: ul }); - - this.choices = new KeyNavList(this.choicesDiv, { onChoose: function(elt) { this.replaceWord(elt, this.curWord); }.bind(this) }).show(); - $(document.body).insert(this.choicesDiv); - }, - - replaceWord: function(li, word) - { - word.update(li.innerHTML).writeAttribute({ className: 'spellcheckCorrected' }); - this.removeChoices(); - }, - - removeChoices: function() - { - if (this.choicesDiv) { - this.choicesDiv.remove(); - this.choicesDiv = null; - } - }, - - resume: function() - { - this.removeChoices(); - - if (!this.reviewDiv) { - return; - } - - var input = $(this.target), - t; - - this.reviewDiv.select('span.spellcheckIncorrect').each(function(n) { - n.replace(n.innerHTML); - }); - - // Unfalsify links - this.reviewDiv.select('A').invoke('stopObserving', 'click'); - - t = this.reviewDiv.innerHTML; - if (!this.htmlAreaParent) { - t = t.replace(/
/gi, '~~~').unescapeHTML().replace(/~~~/g, "\n"); - } - input.value = t; - input.disabled = false; - - if (this.resumeOnDblClick) { - this.reviewDiv.stopObserving('dblclick'); - } - this.reviewDiv.remove(); - this.reviewDiv = null; - - this.setStatus('CheckSpelling'); - - if (this.htmlAreaParent) { - // Fix for Safari 3/fckeditor - Ticket #6909 - Element.show(this.htmlArea); - } else { - input.show(); - } - - if (this.onAfterSpellCheck) { - this.onAfterSpellCheck(); - } - }, - - setStatus: function(state) - { - if (!this.statusButton) { - return; - } - - this.state = state; - switch (this.statusButton.tagName) { - case 'INPUT': - this.statusButton.value = this.buttonStates[state]; - break; - - case 'A': - this.statusButton.update(this.buttonStates[state]); - break; - } - this.statusButton.className = this.statusClass + ' spellcheck' + state; - }, - - isActive: function() - { - return (this.reviewDiv); - } - -}); diff --git a/imp/lib/Ajax/Imple/SpellChecker.php b/imp/lib/Ajax/Imple/SpellChecker.php deleted file mode 100644 index d0b1494c8..000000000 --- a/imp/lib/Ajax/Imple/SpellChecker.php +++ /dev/null @@ -1,110 +0,0 @@ - - * @package IMP - */ -class IMP_Ajax_Imple_SpellChecker extends Horde_Ajax_Imple_Base -{ - /** - * Constructor. - * - * @param array $params Configuration parameters. - *
-     * 'id' => TODO (optional)
-     * 'locales' => TODO (optional)
-     * 'states' => TODO (optional)
-     * 'targetId' => TODO (optional)
-     * 'triggerId' => TODO (optional)
-     * 
- */ - public function __construct($params = array()) - { - if (empty($params['id'])) { - $params['id'] = $this->_randomid(); - } - if (empty($params['targetId'])) { - $params['targetId'] = $this->_randomid(); - } - if (empty($params['triggerId'])) { - $params['triggerId'] = $params['targetId'] . '_trigger'; - } - if (empty($params['states'])) { - $params['states'] = '""'; - } else { - $params['states'] = Horde_Serialize::serialize($params['states'], Horde_Serialize::JSON, Horde_Nls::getCharset()); - } - if (empty($params['locales'])) { - $params['locales'] = array(); - foreach (array_keys(Horde_Nls::$config['spelling']) as $lcode) { - $params['locales'][$lcode] = Horde_Nls::$config['languages'][$lcode]; - } - } - asort($params['locales'], SORT_LOCALE_STRING); - $params['locales'] = Horde_Serialize::serialize($params['locales'], Horde_Serialize::JSON, Horde_Nls::getCharset()); - - parent::__construct($params); - } - - /** - */ - public function attach() - { - Horde::addScriptFile('prototype.js', 'horde', true); - Horde::addScriptFile('effects.js', 'horde', true); - Horde::addScriptFile('KeyNavList.js', 'imp', true); - Horde::addScriptFile('SpellChecker.js', 'imp', true); - - $url = $this->_getUrl('SpellChecker', 'imp', array('input' => $this->_params['targetId'])); - - IMP::addInlineScript($this->_params['id'] . ' = new SpellChecker("' . $url . '", "' . $this->_params['targetId'] . '", "' . $this->_params['triggerId'] . '", ' . $this->_params['states'] . ', ' . $this->_params['locales'] . ', \'widget\');', 'dom'); - } - - /** - */ - public function handle($args) - { - $spellArgs = array(); - - if (!empty($GLOBALS['conf']['spell']['params'])) { - $spellArgs = $GLOBALS['conf']['spell']['params']; - } - - if (isset($args['locale'])) { - $spellArgs['locale'] = $args['locale']; - } elseif (isset($GLOBALS['language'])) { - $spellArgs['locale'] = $GLOBALS['language']; - } - - /* Add local dictionary words. */ - try { - $result = Horde::loadConfiguration('spelling.php', 'ignore_list', 'horde'); - $spellArgs['localDict'] = $result; - } catch (Horde_Exception $e) {} - - if (!empty($args['html'])) { - $spellArgs['html'] = true; - } - - try { - $speller = Horde_SpellChecker::getInstance($GLOBALS['conf']['spell']['driver'], $spellArgs); - } catch (Horde_Exception $e) { - Horde::logMessage($e, __FILE__, __LINE__, PEAR_LOG_ERR); - return array(); - } - - try { - return $speller->spellCheck(Horde_Util::getPost($args['input'])); - } catch (Horde_Exception $e) { - Horde::logMessage($e, __FILE__, __LINE__, PEAR_LOG_ERR); - return array('bad' => array(), 'suggestions' => array()); - } - } - -} diff --git a/imp/lib/UI/Compose.php b/imp/lib/UI/Compose.php index 6f2cd6cb0..e14681f02 100644 --- a/imp/lib/UI/Compose.php +++ b/imp/lib/UI/Compose.php @@ -124,7 +124,7 @@ class IMP_UI_Compose ) ); - $imple = Horde_Ajax_Imple::getInstance(array('imp', 'SpellChecker'), $args); + $imple = Horde_Ajax_Imple::getInstance('SpellChecker', $args); $imple->attach(); } diff --git a/imp/themes/screen.css b/imp/themes/screen.css index f5384fce5..848120ea1 100644 --- a/imp/themes/screen.css +++ b/imp/themes/screen.css @@ -370,18 +370,6 @@ table.multipleMsgs td.msgheader { vertical-align: top; } -/* SpellChecker styles. */ -.spellcheckChecking, .spellcheckIncorrect { - color: #f00; -} -.spellcheckIncorrect, .spellcheckCorrected { - text-decoration: underline; - cursor: pointer; -} -.spellcheckCorrected { - color: #090; -} - /* Contacts styles. */ #contactstable #search { width: 150px;