From: Michael J. Rubinsky Date: Thu, 23 Apr 2009 20:05:43 +0000 (-0400) Subject: Refactor the new, pretty, autocomplete code (preparing to move it to X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=cd8fb63e0e035c36c5638e75495d71ef3c1c77ce;p=horde.git Refactor the new, pretty, autocomplete code (preparing to move it to Horde's scope): - Don't force client code to constrcut the DOM structure need for this to work. All we need to do is add a single input element, and attach it to the autocompleter. - Allow overriding of CSS Classes and DOM ids used to allow for mulitple scopes/multiple autocompleters on the same page. --- diff --git a/kronolith/index.php b/kronolith/index.php index 682c7fe7f..a2f0efabe 100644 --- a/kronolith/index.php +++ b/kronolith/index.php @@ -65,12 +65,5 @@ require KRONOLITH_TEMPLATES . '/index/index.inc'; Kronolith::includeScriptFiles(); Kronolith::outputInlineScript(); $notification->notify(array('listeners' => array('javascript'))); -Kronolith_Imple::factory('TagAutoCompleter', - array('box' => 'kronolithTagACBox', - 'triggerId' => 'kronolithTagACTrigger', - 'tags' => 'kronolithEventTags', - 'container' => 'kronolithTagACContainer' - //'existing' => array('tag1', 'tag2'), - //'debug' => true - )); +Kronolith_Imple::factory('TagAutoCompleter', array('triggerId' => 'tags', 'pretty' => true)); echo "\n"; diff --git a/kronolith/js/src/kronolith.js b/kronolith/js/src/kronolith.js index 99863ebba..49d73c70a 100644 --- a/kronolith/js/src/kronolith.js +++ b/kronolith/js/src/kronolith.js @@ -1184,7 +1184,7 @@ KronolithCore = { e.stop(); return; } else if (elt.hasClassName('kronolithEventTag')) { - $('kronolithTagACTrigger').kronolithTagger.addNewTagNode(elt.getText()); + $('tags').tagger.addNewTagNode(elt.getText()); e.stop(); return; } @@ -1238,7 +1238,7 @@ KronolithCore = { RedBox.onDisplay = null; }; - $('kronolithTagACTrigger').kronolithTagger.init(); + $('tags').tagger.init(); $('kronolithEventForm').enable(); $('kronolithEventForm').reset(); this.doAction('ListTopTags', {}, this._topTags); @@ -1294,7 +1294,7 @@ KronolithCore = { $('kronolithEventStartTime').value = ev.st; $('kronolithEventEndDate').value = ev.ed; $('kronolithEventEndTime').value = ev.et; - $('kronolithTagACTrigger').kronolithTagger.init(ev.tg); + $('tags').tagger.init(ev.tg); if (ev.r) { // @todo: refine $A($('kronolithEventRecurrence').options).find(function(option) { diff --git a/kronolith/js/src/taggerAutoCompleter.js b/kronolith/js/src/taggerAutoCompleter.js index 4b0802654..b9cc122f1 100644 --- a/kronolith/js/src/taggerAutoCompleter.js +++ b/kronolith/js/src/taggerAutoCompleter.js @@ -3,15 +3,46 @@ var KronolithTagger = Class.create({ { this.p = params; - trigger = $(this.p.trigger); + // Array to hold the currently selected tags to ease with removing + // them, assuring no duplicates etc.. + this.selectedTags = []; + + // The outter most box, the "fake" input element. + if (typeof this.p.box == 'undefined') { + this.p.box = this.p.box = 'HordeACBox'; + } + if (typeof this.p.boxClass == 'undefined') { + this.p.boxClass = 'hordeACBox'; + } + + // The class for the ul. li will have a *Item and *Member class + // added to them. + if (typeof this.p.listClass == 'undefined') { + this.p.listClass = 'hordeACList'; + } + + // Class for the actual input element. + if (typeof this.p.growingInputClass == 'undefined') { + this.p.growingInputClass = 'hordeACTrigger'; + } + if (typeof this.p.triggerContainer == 'undefined') { + this.p.triggerContainer = 'hordeACTriggerContainer'; + } + + // p.tags is now the hidden input field, while p.trigger will + // contain the "real" input element's name attribute. + this.p.tags = this.p.trigger; + this.p.trigger = this.p.trigger + 'real'; + + this.buildStructure(); + + var trigger = $(this.p.trigger); trigger.observe('keydown', this._onKeyDown.bindAsEventListener(this)); // Bind this object to the trigger so we can call it's methods // from client code. - trigger.kronolithTagger = this; - - // Make sure the right dom elements are styled correctly. - $(this.p.container).addClassName('kronolithACListItem kronolithTagACContainer'); + //trigger.kronolithTagger = this; + $(this.p.tags).tagger = this; // Make sure the p.tags element is hidden if (!this.p.debug) { @@ -25,7 +56,8 @@ var KronolithTagger = Class.create({ $(this.p.box).observe('click', function() {$(this.p.trigger).focus()}.bindAsEventListener(this)); // Create the underlaying Autocompleter - new Ajax.Autocompleter(params.trigger, params.resultsId, params.uri, params.params); + this.p.uri = this.p.uri + '/input=' + this.p.trigger; + new Ajax.Autocompleter(this.p.trigger, this.p.trigger + '_results', this.p.uri, this.p.params); // Prepopulate the tags and the container elements? if (typeof this.p.existing != 'undefined') { @@ -38,8 +70,8 @@ var KronolithTagger = Class.create({ { // TODO: Resize the trigger field to fill the current line? // Clear any existing values - if (this.p.selectedTags.length) { - $('kronolithTagACBox').select('li.kronolithTagACListItem').each(function(item) {this.removeTagNode(item) }.bind(this)); + if (this.selectedTags.length) { + $(this.p.box).select('li.' + this.p.listClass + 'Item').each(function(item) {this.removeTagNode(item) }.bind(this)); } // Clear the hidden tags field @@ -54,6 +86,39 @@ var KronolithTagger = Class.create({ }, + buildStructure: function() + { + // Build the outter box + var box = new Element('div', {id: this.p.box, 'class': this.p.boxClass}); + + // The results div - where the autocomplete choices are placed. + var results = new Element('div', {id: this.p.trigger + '_results', 'class': 'autocomplete'}); + + // The list - where the choosen items are placed as
  • nodes + var list = new Element('ul', {'class': this.p.listClass}); + + // The input element and the
  • wraper + var inputListItem = new Element('li', {'class': this.p.listClass + 'Member', + 'id': this.p.triggerContainer}); + + var growingInput = new Element('input', {'class': this.p.growingInputClass, + 'id': this.p.trigger, + 'name': this.p.trigger, + 'autocomplete':'off'}); + + inputListItem.update(growingInput); + list.update(inputListItem); + box.update(list); + box.insert(results); + + // Replace the single input element with the new structure and + // move the old element into the structure while making sure it's + // hidden. (Use the long form to play nice with Opera) + oldTrigger = Element.replace($(this.p.tags), box); + box.insert(oldTrigger); + + }, + _onKeyDown: function(e) { // Check for a comma @@ -81,17 +146,17 @@ var KronolithTagger = Class.create({ addNewTagNode: function(value) { // Don't add if it's already present. - for (var x = 0, len = this.p.selectedTags.length; x < len; x++) { - if (this.p.selectedTags[x] == value) { + for (var x = 0, len = this.selectedTags.length; x < len; x++) { + if (this.selectedTags[x] == value) { return; } } - var newTag = new Element('li', {class: 'kronolithACListItem kronolithTagACListItem'}).update(value); - var x = new Element('img', {class: 'kronolithTagACRemove', src:this.p.URI_IMG_HORDE + "/delete-small.png"}); + var newTag = new Element('li', {class: this.p.listClass + 'Member ' + this.p.listClass + 'Item'}).update(value); + var x = new Element('img', {class: 'hordeACItemRemove', src:this.p.URI_IMG_HORDE + "/delete-small.png"}); x.observe('click', this._removeTagHandler.bindAsEventListener(this)); newTag.insert(x); - $(this.p.container).insert({before: newTag}); + $(this.p.triggerContainer).insert({before: newTag}); $(this.p.trigger).value = ''; // Add to hidden input field. @@ -102,15 +167,15 @@ var KronolithTagger = Class.create({ } // ...and keep the selectedTags array up to date. - this.p.selectedTags.push(value); + this.selectedTags.push(value); }, removeTagNode: function(item) { var value = item.collectTextNodesIgnoreClass('informal'); - for (var x = 0, len = this.p.selectedTags.length; x < len; x++) { - if (this.p.selectedTags[x] == value) { - this.p.selectedTags.splice(x, 1); + for (var x = 0, len = this.selectedTags.length; x < len; x++) { + if (this.selectedTags[x] == value) { + this.selectedTags.splice(x, 1); } } item.remove(); @@ -120,6 +185,6 @@ var KronolithTagger = Class.create({ { item = Event.element(e).up(); this.removeTagNode(item); - $(this.p.tags).value = this.p.selectedTags.join(','); + $(this.p.tags).value = this.selectedTags.join(','); } }); \ No newline at end of file diff --git a/kronolith/lib/Imple/TagAutoCompleter.php b/kronolith/lib/Imple/TagAutoCompleter.php index 0014c6cff..638d85383 100644 --- a/kronolith/lib/Imple/TagAutoCompleter.php +++ b/kronolith/lib/Imple/TagAutoCompleter.php @@ -33,7 +33,7 @@ class Kronolith_Imple_TagAutoCompleter extends Kronolith_Imple /** * Attach the Imple object to a javascript event. - * Assume that if the 'box' parameter is empty then we want a + * If the 'pretty' parameter is empty then we want a * traditional autocompleter, otherwise we get a spiffy pretty one. * */ @@ -43,10 +43,12 @@ class Kronolith_Imple_TagAutoCompleter extends Kronolith_Imple parent::attach(); Horde::addScriptFile('autocomplete.js', 'horde', true); - if ($pretty = !empty($this->_params['box'])) { + if ($pretty = !empty($this->_params['pretty'])) { Horde::addScriptFile('taggerAutoCompleter.js', 'kronolith', true); + $this->_params['uri'] = Horde::url($GLOBALS['registry']->get('webroot', 'kronolith') . '/imple.php?imple=TagAutoCompleter', true); + } else { + $this->_params['uri'] = Horde::url($GLOBALS['registry']->get('webroot', 'kronolith') . '/imple.php?imple=TagAutoCompleter/input=' . rawurlencode($this->_params['triggerId']), true); } - $this->_params['uri'] = Horde::url($GLOBALS['registry']->get('webroot', 'kronolith') . '/imple.php?imple=TagAutoCompleter/input=' . rawurlencode($this->_params['triggerId']), true); if (!$pretty) { $params = array( @@ -69,13 +71,9 @@ class Kronolith_Imple_TagAutoCompleter extends Kronolith_Imple $params[] = '{' . implode(',', $js_params) . '}'; if ($pretty) { - $js_vars = array('box' => $this->_params['box'], - 'resultsId' => $this->_params['resultsId'], + $js_vars = array('boxClass' => 'hordeACBox kronolithLongField', 'uri' => $this->_params['uri'], - 'selectedTags' => array(), 'trigger' => $this->_params['triggerId'], - 'tags' => $this->_params['tags'], - 'container' => $this->_params['container'], 'URI_IMG_HORDE' => $registry->getImageDir('horde'), 'params' => $params); @@ -83,7 +81,7 @@ class Kronolith_Imple_TagAutoCompleter extends Kronolith_Imple $js_vars['existing'] = $this->_params['existing']; } - $script = array('new KronolithTagger(' . Horde_Serialize::serialize($js_vars, Horde_Serialize::JSON, NLS::getCharset()) . ')'); + $script = array('new KronolithTagger(' . Horde_Serialize::serialize($js_vars, Horde_Serialize::JSON, NLS::getCharset()) . ')'); } else { $script = array('new Ajax.Autocompleter(' . implode(',', $params) . ')'); } diff --git a/kronolith/templates/index/index.inc b/kronolith/templates/index/index.inc index 1876281c2..53c89ad21 100644 --- a/kronolith/templates/index/index.inc +++ b/kronolith/templates/index/index.inc @@ -174,14 +174,8 @@

    -
    -
      -
    • -
    -
    -
    - - + +
    diff --git a/kronolith/themes/screen.css b/kronolith/themes/screen.css index d55aa9b06..ba747766a 100644 --- a/kronolith/themes/screen.css +++ b/kronolith/themes/screen.css @@ -552,28 +552,28 @@ body.kronolithAjax { /* to be removed */ div.c1 { - color: #336600; + color: #336600; } div.c2 { - color: #1a5587; + color: #1a5587; } div.c3 { - color: #d38a23; + color: #d38a23; } div.c4 { - color: #4b2a16; + color: #4b2a16; } div.c1.kronolithEventFull { - background-color: #336600 !important; + background-color: #336600 !important; } div.c2.kronolithEventFull { - background-color: #1a5587 !important; + background-color: #1a5587 !important; } div.c3.kronolithEventFull { - background-color: #d38a23 !important; + background-color: #d38a23 !important; } div.c4.kronolithEventFull { - background-color: #4b2a16 !important; + background-color: #4b2a16 !important; } #kronolithAddEvents span { @@ -1280,61 +1280,6 @@ li.panel-tags { text-decoration: underline; } -#kronolithTagACBox { - background:white none repeat scroll 0 0; - border:1px solid #666699; - cursor:default; - /*overflow:hidden;*/ - text-align:left; - min-height: 58px; -} - -.kronolithTagACList { - display:block; - overflow:hidden; - width:100%; - cursor:default; - list-style-type:none; - margin:0; - padding:0; -} - -.kronolithACListItem { - display:inline-block; - font-size:90%; - padding:2px 1px 1px 2px; - height: 17px; - white-space:nowrap; -} - -.kronolithTagACListItem { - background-color:lightblue; - border:1px solid #C0C0C0; - -moz-border-radius-bottomleft:4px; - -moz-border-radius-bottomright:4px; - -moz-border-radius-topleft:4px; - -moz-border-radius-topright:4px; - margin:5px; -} - -#kronolithTagACTrigger { - background:transparent none repeat scroll 0 0; - border:0 none; - display:inline; - position:static; - width:80px; -} - -.kronolithTagACContainer { - height:18px; - margin:2px; - padding:2px 2px 2px 0; -} - -.kronolithTagACRemove:hover { - cursor:pointer; -} - #hordeAlerts div.kronolith-sticky, #hordeAlerts div.kronolith-sticky { background-color: #ebe20c; background-image: url("graphics/warning.png");