Move JS files dependent on horde/Core into the package
authorMichael M Slusarz <slusarz@curecanti.org>
Tue, 31 Aug 2010 22:28:39 +0000 (16:28 -0600)
committerMichael M Slusarz <slusarz@curecanti.org>
Wed, 1 Sep 2010 00:30:57 +0000 (18:30 -0600)
25 files changed:
framework/Core/js/addressbooksprefs.js [new file with mode: 0644]
framework/Core/js/alarmprefs.js [new file with mode: 0644]
framework/Core/js/autocomplete.js [new file with mode: 0644]
framework/Core/js/calendar.js [new file with mode: 0644]
framework/Core/js/hordetree.js [new file with mode: 0644]
framework/Core/js/identityselect.js [new file with mode: 0644]
framework/Core/js/liquidmetal.js [new file with mode: 0644]
framework/Core/js/open_html_helper.js [new file with mode: 0644]
framework/Core/js/prefs.js [new file with mode: 0644]
framework/Core/js/prettyautocomplete.js [new file with mode: 0644]
framework/Core/js/sourceselect.js [new file with mode: 0644]
framework/Core/js/spellchecker.js [new file with mode: 0644]
framework/Core/package.xml
horde/js/addressbooksprefs.js [deleted file]
horde/js/alarmprefs.js [deleted file]
horde/js/autocomplete.js [deleted file]
horde/js/calendar.js [deleted file]
horde/js/hordetree.js [deleted file]
horde/js/identityselect.js [deleted file]
horde/js/liquidmetal.js [deleted file]
horde/js/open_html_helper.js [deleted file]
horde/js/prefs.js [deleted file]
horde/js/prettyautocomplete.js [deleted file]
horde/js/sourceselect.js [deleted file]
horde/js/spellchecker.js [deleted file]

diff --git a/framework/Core/js/addressbooksprefs.js b/framework/Core/js/addressbooksprefs.js
new file mode 100644 (file)
index 0000000..3600b6c
--- /dev/null
@@ -0,0 +1,71 @@
+/**
+ * Provides the javascript for managing addressbooks.
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Horde
+ * @package  Core
+ */
+
+var HordeAddressbooksPrefs = {
+
+    // Variables set by other code: fields, nonetext
+
+    updateSearchFields: function()
+    {
+        var tmp,
+            sv = $F('selected_sources'),
+            sf = $('search_fields_select');
+
+        sf.childElements().invoke('remove');
+
+        if (sv.size() == 1) {
+            tmp = this.fields.get(sv.first());
+            tmp.entries.each(function(o) {
+                var opt = new Option(o.label, o.name);
+                if (tmp.selected.include(o.name)) {
+                    opt.selected = true;
+                }
+                sf.insert(opt);
+            });
+        } else {
+            tmp = new Option(this.nonetext, '');
+            tmp.disabled = true;
+            sf.insert(tmp);
+        }
+    },
+
+    changeSearchFields: function()
+    {
+        var tmp,
+            out = $H(),
+            sv = $F('selected_sources');
+
+        if (sv.size() == 1) {
+            tmp = this.fields.get(sv.first());
+            tmp.selected = $F('search_fields_select');
+            this.fields.set(sv.first(), tmp);
+
+            this.fields.each(function(f) {
+                out.set(f.key, f.value.selected);
+            });
+
+            $('search_fields').setValue(Object.toJSON(out));
+        }
+    },
+
+    onDomLoad: function()
+    {
+        this.fields = $H(this.fields);
+
+        this.updateSearchFields();
+
+        $('search_fields_select').observe('change', this.changeSearchFields.bind(this));
+        $('selected_sources').observe('change', this.updateSearchFields.bind(this));
+        $('selected_sources').observe('HordeSourceSelectPrefs:remove', this.updateSearchFields.bind(this));
+    }
+
+};
+
+document.observe('dom:loaded', HordeAddressbooksPrefs.onDomLoad.bind(HordeAddressbooksPrefs));
diff --git a/framework/Core/js/alarmprefs.js b/framework/Core/js/alarmprefs.js
new file mode 100644 (file)
index 0000000..1a93b9d
--- /dev/null
@@ -0,0 +1,37 @@
+/**
+ * Provides the javascript for managing alarms.
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Horde
+ * @package  Core
+ */
+
+var HordeAlarmPrefs = {
+
+    // Variables defaulting to null: pref
+
+    updateParams: function()
+    {
+        [ 'notify', 'mail', 'sms' ].each(function(method) {
+            var p = $(method + 'Params');
+            if (p) {
+                if ($(this.pref).getValue().include(method)) {
+                    p.show();
+                } else {
+                    p.hide();
+                }
+            }
+        }, this);
+    },
+
+    onDomLoad: function()
+    {
+        $(this.pref).observe('change', this.updateParams.bind(this));
+        this.updateParams();
+    }
+
+};
+
+document.observe('dom:load', HordeAlarmPrefs.onDomLoad.bind(HordeAlarmPrefs));
diff --git a/framework/Core/js/autocomplete.js b/framework/Core/js/autocomplete.js
new file mode 100644 (file)
index 0000000..6834be7
--- /dev/null
@@ -0,0 +1,333 @@
+/**
+ * autocomplete.js - A javascript library which implements autocomplete.
+ * Requires prototype.js v1.6.0.2+, scriptaculous v1.8.0+ (effects.js),
+ * and keynavlist.js.
+ *
+ * Adapted from script.aculo.us controls.js v1.8.0
+ *   (c) 2005-2007 Thomas Fuchs, Ivan Krstic, and Jon Tirsen
+ *   Contributors: Richard Livsey, Rahul Bhargava, Rob Wills
+ *   http://script.aculo.us/
+ *
+ * The original script was freely distributable under the terms of an
+ * MIT-style license.
+ *
+ * Usage:
+ * ------
+ * TODO: options = autoSelect, frequency, minChars, onSelect, onShow, onType,
+ *                 paramName, tokens
+ *
+ * Copyright 2007-2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Horde
+ * @package  Core
+ */
+
+var Autocompleter = {};
+
+Autocompleter.Base = Class.create({
+    baseInitialize: function(elt, opts)
+    {
+        this.elt = elt = $(elt);
+        this.changed = false;
+        this.observer = null;
+        this.oldval = $F(elt);
+        this.opts = Object.extend({
+            frequency: 0.4,
+            indicator: null,
+            minChars: 1,
+            onSelect: Prototype.K,
+            onShow: Prototype.K,
+            onType: Prototype.K,
+            filterCallback: Prototype.K,
+            paramName: elt.readAttribute('name'),
+            tokens: [],
+            keydownObserver: this.elt
+        }, (this._setOptions ? this._setOptions(opts) : (opts || {})));
+
+        // Force carriage returns as token delimiters anyway
+        if (!this.opts.tokens.include('\n')) {
+            this.opts.tokens.push('\n');
+        }
+
+        elt.writeAttribute('autocomplete', 'off');
+        elt.observe("keydown", this._onKeyDown.bindAsEventListener(this));
+    },
+
+    _onKeyDown: function(e)
+    {
+        var a = document.activeElement;
+
+        if (Object.isUndefined(a) || a == this.elt) {
+            switch (e.keyCode) {
+            case 0:
+                if (!Prototype.Browser.WebKit) {
+                    break;
+                }
+                // Fall-through
+
+            // Ignore events caught by KevNavList
+            case Event.KEY_DOWN:
+            case Event.KEY_ESC:
+            case Event.KEY_RETURN:
+            case Event.KEY_TAB:
+            case Event.KEY_UP:
+                return;
+            }
+
+            this.changed = true;
+
+            if (this.observer) {
+                clearTimeout(this.observer);
+            }
+
+            this.observer = this.onObserverEvent.bind(this).delay(this.opts.frequency);
+        }
+    },
+
+    updateChoices: function(choices)
+    {
+        var a = document.activeElement, c = [], re;
+
+        if (this.changed ||
+            (Object.isUndefined(a) || a != this.elt)) {
+            return;
+        }
+
+        if (this.opts.indicator) {
+            $(this.opts.indicator).hide();
+        }
+
+        choices = this.opts.filterCallback(choices);
+        if (!choices.size()) {
+            if (this.knl) {
+                this.knl.hide();
+            }
+            this.getNewVal(this.lastentry);
+        } else if (choices.size() == 1 && this.opts.autoSelect) {
+            this.onSelect(choices.first());
+            if (this.knl) {
+                this.knl.hide();
+            }
+        } else {
+            re = new RegExp("(" + this.getToken() + ")", "i");
+
+            choices.each(function(n) {
+                c.push({
+                    l: n.escapeHTML().gsub(re, '<strong>#{1}</strong>'),
+                    v: n
+                });
+            });
+
+            if (!this.knl) {
+                this.knl = new KeyNavList(this.elt, { onChoose: this.onSelect.bind(this),
+                                                        onShow: this.opts.onShow.bind(this),
+                                                     domParent: this.opts.domParent,
+                                               keydownObserver: this.opts.keydownObserver});
+            }
+
+            this.knl.show(c);
+        }
+    },
+
+    onObserverEvent: function()
+    {
+        this.changed = false;
+
+        var entry = this.getToken();
+
+        if (entry.length >= this.opts.minChars) {
+            entry = this.opts.onType(entry);
+        }
+
+        if (entry.length) {
+            if (this.opts.indicator) {
+                $(this.opts.indicator).show();
+            }
+            this.lastentry = entry;
+            this.getUpdatedChoices(entry);
+        } else if (this.knl) {
+            this.knl.hide();
+        }
+    },
+
+    getToken: function()
+    {
+        var bounds = this.getTokenBounds();
+        return $F(this.elt).substring(bounds[0], bounds[1]).strip();
+    },
+
+    getTokenBounds: function()
+    {
+        var diff, i, index, l, offset, tp,
+            t = this.opts.tokens,
+            value = $F(this.elt),
+            nextTokenPos = value.length,
+            prevTokenPos = -1,
+            boundary = Math.min(nextTokenPos, this.oldval.length);
+
+        if (value.strip().empty()) {
+            return [ -1, 0 ];
+        }
+
+        diff = boundary;
+        for (i = 0; i < boundary; ++i) {
+            if (value[i] != this.oldval[i]) {
+                diff = i;
+                break;
+            }
+        }
+
+        offset = (diff == this.oldval.length ? 1 : 0);
+
+        for (index = 0, l = t.length; index < l; ++index) {
+            tp = value.lastIndexOf(t[index], diff + offset - 1);
+            if (tp > prevTokenPos) {
+                prevTokenPos = tp;
+            }
+            tp = value.indexOf(t[index], diff + offset);
+            if (tp != -1 && tp < nextTokenPos) {
+                nextTokenPos = tp;
+            }
+        }
+        return [ prevTokenPos + 1, nextTokenPos ];
+    },
+
+    onSelect: function(entry)
+    {
+        if (entry) {
+            this.elt.setValue(this.opts.onSelect(this.getNewVal(entry))).focus();
+            if (this.knl) {
+                this.knl.markSelected();
+            }
+        }
+    },
+
+    getNewVal: function(entry)
+    {
+        var bounds = this.getTokenBounds(), newval, v, ws;
+
+        if (bounds[0] == -1) {
+            newval = entry;
+        } else {
+            v = $F(this.elt);
+            newval = v.substr(0, bounds[0]);
+            ws = v.substr(bounds[0]).match(/^\s+/);
+            if (ws) {
+                newval += ws[0];
+            }
+            newval += entry + v.substr(bounds[1]);
+        }
+
+        this.oldval = newval;
+
+        return newval;
+    }
+
+});
+
+Ajax.Autocompleter = Class.create(Autocompleter.Base, {
+
+    initialize: function(element, url, opts)
+    {
+        this.baseInitialize(element, opts);
+        this.opts = Object.extend(this.opts, {
+            asynchronous: true,
+            onComplete: this._onComplete.bind(this),
+            defaultParams: $H(this.opts.parameters)
+        });
+        this.url = url;
+        this.cache = $H();
+    },
+
+    getUpdatedChoices: function(t)
+    {
+        var p,
+            o = this.opts,
+            c = this.cache.get(t);
+
+        if (c) {
+            this.updateChoices(c);
+        } else {
+            p = Object.clone(o.defaultParams);
+            p.set(o.paramName, t);
+            o.parameters = p.toQueryString();
+            new Ajax.Request(this.url, o);
+        }
+    },
+
+    _onComplete: function(request)
+    {
+        this.updateChoices(this.cache.set(this.getToken(), request.responseJSON));
+    }
+});
+
+Autocompleter.Local = Class.create(Autocompleter.Base, {
+
+    initialize: function(element, arr, opts)
+    {
+        this.baseInitialize(element, opts);
+        this.opts.arr = arr;
+    },
+
+    getUpdatedChoices: function(entry)
+    {
+        var choices,
+            csort = [],
+            entry_len = entry.length,
+            i = 0,
+            o = this.opts;
+
+        if (o.ignoreCase) {
+            entry = entry.toLowerCase();
+        }
+
+        choices = o.arr.findAll(function(t) {
+            if (i == o.choices) {
+                throw $break;
+            }
+
+            if (o.ignoreCase) {
+                t = t.toLowerCase();
+            }
+            t = t.unescapeHTML();
+
+            var pos = t.indexOf(entry);
+            if (pos != -1 &&
+                ((pos == 0 && t.length != entry_len) ||
+                 (entry_len >= o.partialChars &&
+                  o.partialSearch &&
+                  (o.fullSearch || /\s/.test(t.substr(pos - 1, 1)))))) {
+                ++i;
+                return true;
+            }
+            return false;
+        }, this);
+
+        if (o.score) {
+            choices.each(function(term) {
+                csort.push({ s: LiquidMetal.score(term, entry), t: term });
+            }.bind(this));
+            // Sort the terms
+            csort.sort(function(a, b) { return b.s - a.s; });
+            choices = csort.pluck('t');
+        }
+
+        this.updateChoices(choices);
+    },
+
+    _setOptions: function(opts)
+    {
+        return Object.extend({
+            choices: 10,
+            fullSearch: false,
+            ignoreCase: true,
+            partialChars: 2,
+            partialSearch: true,
+            score: false
+        }, opts || {});
+    }
+
+});
diff --git a/framework/Core/js/calendar.js b/framework/Core/js/calendar.js
new file mode 100644 (file)
index 0000000..fc390ec
--- /dev/null
@@ -0,0 +1,335 @@
+/**
+ * Horde javascript calendar widget.
+ *
+ * Custom Events:
+ * --------------
+ * Horde_Calendar:select
+ *   params: Date object
+ *   Fired when a date is selected.
+ *
+ * Horde_Calendar:selectMonth
+ *   params: Date object
+ *   Fired when a month is selected.
+ *
+ * Horde_Calendar:selectWeek
+ *   params: Date object
+ *   Fired when a week is selected.
+ *
+ * Horde_Calendar:selectYear
+ *   params: Date object
+ *   Fired when a year is selected.
+ *
+ * @category Horde
+ * @package  Core
+ */
+
+var Horde_Calendar =
+{
+    // Variables set externally: click_month, click_week, click_year,
+    //                           firstDayOfWeek, fullweekdays, months,
+    //                           weekdays
+    // Variables defaulting to null: date, month, openDate, trigger, year
+
+    open: function(trigger, data)
+    {
+        var date = data ? data : new Date();
+
+        this.openDate = date.getTime();
+        this.trigger = $(trigger);
+        this.draw(this.openDate, true);
+    },
+
+    /**
+     * Days in the month (month is a zero-indexed javascript month).
+     */
+    daysInMonth: function(month, year)
+    {
+        switch (month) {
+        case 3:
+        case 5:
+        case 8:
+        case 10:
+            return 30;
+
+        case 1:
+            return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
+                ? 29
+                : 28;
+
+        default:
+            return 31;
+        }
+    },
+
+    weeksInMonth: function(month, year)
+    {
+        var firstWeekDays, weeks,
+            firstOfMonth = (new Date(year, month, 1)).getDay();
+
+        if ((this.firstDayOfWeek == 1 && firstOfMonth == 0) ||
+            (this.firstDayOfWeek == 0 && firstOfMonth == 6)) {
+            firstWeekDays = 7 - firstOfMonth + this.firstDayOfWeek;
+            weeks = 1;
+        } else {
+            firstWeekDays = this.firstDayOfWeek - firstOfMonth;
+            weeks = 0;
+        }
+
+        firstWeekDays %= 7;
+
+        return Math.ceil((this.daysInMonth(month, year) - firstWeekDays) / 7) + weeks;
+    },
+
+    // http://javascript.about.com/library/blstdweek.htm
+    weekOfYear: function(d)
+    {
+        var newYear = new Date(d.getFullYear(), 0, 1),
+            day = newYear.getDay();
+        if (this.firstDayOfWeek != 0) {
+            day = ((day + (7 - this.firstDayOfWeek)) % 7);
+        }
+        return Math.ceil((((d - newYear) / 86400000) + day + 1) / 7);
+    },
+
+    draw: function(timestamp, init)
+    {
+        this.date = new Date(timestamp);
+        this.month = this.date.getMonth();
+        this.year = this.date.getFullYear();
+
+        var cell, i, i_max, p, row, startOfView, vp,
+            count = 1,
+            div = $('hordeCalendar'),
+            tbody = div.down('TBODY'),
+
+            // Requires init above
+            daysInMonth = this.daysInMonth(this.month, this.year),
+            daysInView = this.weeksInMonth(this.month, this.year) * 7,
+            firstOfMonth = (new Date(this.year, this.month, 1)).getDay(),
+
+            // Cache today and open date.
+            today = new Date(),
+            today_year = today.getFullYear(),
+            today_month = today.getMonth(),
+            today_day = today.getDate(),
+            open = new Date(this.openDate),
+            open_year = open.getFullYear(),
+            open_month = open.getMonth(),
+            open_day = open.getDate();
+
+        if (this.firstDayOfWeek == 0) {
+            startOfView = 1 - firstOfMonth;
+        } else {
+            // @TODO Adjust this for days other than Monday.
+            startOfView = (firstOfMonth == 0)
+                ? -5
+                : 2 - firstOfMonth;
+        }
+
+        div.down('.hordeCalendarYear').update(this.year);
+        div.down('.hordeCalendarMonth').update(this.months[this.month]);
+        tbody.update('');
+
+        for (i = startOfView, i_max = startOfView + daysInView; i < i_max; ++i) {
+            if (count == 1) {
+                row = new Element('TR');
+                if (this.click_week) {
+                    row.insert(new Element('TD').insert(new Element('A', { className: 'hordeCalendarWeek' }).insert(this.weekOfYear(new Date(this.year, this.month, (i < 1) ? 1 : i)))));
+                }
+            }
+
+            cell = new Element('TD');
+
+            if (i < 1 || i > daysInMonth) {
+                cell.addClassName('hordeCalendarEmpty');
+                row.insert(cell);
+            } else {
+                if (today_year == this.year &&
+                    today_month == this.month &&
+                    today_day == i) {
+                    cell.writeAttribute({ className: 'hordeCalendarToday' });
+                }
+
+                if (open_year == this.year &&
+                    open_month == this.month &&
+                    open_day == i) {
+                    cell.addClassName('hordeCalendarCurrent');
+                }
+
+                row.insert(cell.insert(new Element('A', { className: 'hordeCalendarDay', href: '#' }).insert(i)));
+            }
+
+            if (count == 7) {
+                tbody.insert(row);
+                count = 0;
+            }
+            ++count;
+        }
+
+        if (count > 1) {
+            tbody.insert(row);
+        }
+
+        div.show();
+
+        // Position the popup every time in case of a different input,
+        // window sizing changes, etc.
+        if (init) {
+            p = this.trigger.cumulativeOffset();
+            vp = document.viewport.getDimensions();
+
+            if (p.left + div.offsetWidth > vp.width) {
+                div.setStyle({ left: (vp.width - 10 - div.offsetWidth) + 'px' });
+            } else {
+                div.setStyle({ left: p.left + 'px' });
+            }
+
+            if (p.top + div.offsetHeight > vp.height) {
+                div.setStyle({ top: (vp.height - 10 - div.offsetHeight) + 'px' });
+            } else {
+                div.setStyle({ top: p.top + 'px' });
+            }
+        }
+
+        // IE 6 only.
+        if (Prototype.Browser.IE && !window.XMLHttpRequest) {
+            iframe = $('hordeCalendarIframe');
+            if (!iframe) {
+                iframe = new Element('IFRAME', { name: 'hordeCalendarIframe', id: 'hordeCalendarIframe', src: 'javascript:false;', scrolling: 'no', frameborder: 0 }).hide();
+                $(document.body).insert(iframe);
+            }
+            iframe.clonePosition(div).setStyle({
+                position: 'absolute',
+                display: 'block',
+                zIndex: 1
+            });
+        }
+
+        div.setStyle({ zIndex: 999 });
+    },
+
+    hideCal: function()
+    {
+        var iefix = $('hordeCalendarIframe');
+
+        $('hordeCalendar').hide();
+
+        if (iefix) {
+            iefix.hide();
+        }
+    },
+
+    changeYear: function(by)
+    {
+        this.draw((new Date(this.date.getFullYear() + by, this.date.getMonth(), 1)).getTime());
+    },
+
+    changeMonth: function(by)
+    {
+        var newMonth = this.date.getMonth() + by,
+            newYear = this.date.getFullYear();
+
+        if (newMonth == -1) {
+            newMonth = 11;
+            newYear -= 1;
+        }
+
+        this.draw((new Date(newYear, newMonth, 1)).getTime());
+    },
+
+    init: function()
+    {
+        var i, link, row,
+            offset = this.click_week ? 1 : 0,
+            thead = new Element('THEAD'),
+            table = new Element('TABLE', { className: 'hordeCalendarPopup', cellSpacing: 0 }).insert(thead).insert(new Element('TBODY'));
+
+        // Title bar.
+        link = new Element('A', { href: '#', className: 'hordeCalendarClose rightAlign' }).insert('x');
+        thead.insert(new Element('TR').insert(new Element('TD', { colspan: 6 + offset })).insert(new Element('TD').insert(link)));
+
+        // Year.
+        row = new Element('TR');
+        link = new Element('A', { className: 'hordeCalendarPrevYear', href: '#' }).insert('&laquo;');
+        row.insert(new Element('TD').insert(link));
+
+        tmp = new Element('TD', { align: 'center', colspan: 5 + offset });
+        if (this.click_year) {
+            tmp.insert(new Element('A', { className: 'hordeCalendarYear' }));
+        } else {
+            tmp.addClassName('hordeCalendarYear');
+        }
+        row.insert(tmp);
+
+        link = new Element('A', { className: 'hordeCalendarNextYear', href: '#' }).insert('&raquo;');
+        row.insert(new Element('TD', { className: 'rightAlign' }).insert(link));
+
+        thead.insert(row);
+
+        // Month name.
+        row = new Element('TR');
+        link = new Element('A', { className: 'hordeCalendarPrevMonth', href: '#' }).insert('&laquo;');
+        row.insert(new Element('TD').insert(link));
+
+        tmp = new Element('TD', { align: 'center', colspan: 5 + offset });
+        if (this.click_year) {
+            tmp.insert(new Element('A', { className: 'hordeCalendarMonth' }));
+        } else {
+            tmp.addClassName('hordeCalendarMonth');
+        }
+        row.insert(tmp);
+
+        link = new Element('A', { className: 'hordeCalendarNextMonth', href: '#' }).insert('&raquo;');
+        row.insert(new Element('TD', { className: 'rightAlign' }).insert(link));
+
+        thead.insert(row);
+
+        // Weekdays.
+        row = new Element('TR');
+        if (this.click_week) {
+            row.insert(new Element('TH'));
+        }
+        for (i = 0; i < 7; ++i) {
+            row.insert(new Element('TH').insert(this.weekdays[(i + this.firstDayOfWeek) % 7]));
+        }
+        thead.insert(row);
+
+        $(document.body).insert({ bottom: new Element('DIV', { id: 'hordeCalendar' }).setStyle({ position: 'absolute', 'z-index': 999 }).hide().insert(table) });
+
+        $('hordeCalendar').observe('click', this.clickHandler.bindAsEventListener(this));
+    },
+
+    clickHandler: function(e)
+    {
+        var elt = e.element();
+
+        if (elt.hasClassName('hordeCalendarDay')) {
+            this.hideCal();
+            this.trigger.fire('Horde_Calendar:select', new Date(this.year, this.month, parseInt(e.element().textContent, 10)));
+        } else if (elt.hasClassName('hordeCalendarClose')) {
+            this.hideCal();
+        } else if (elt.hasClassName('hordeCalendarPrevYear')) {
+            this.changeYear(-1);
+        } else if (elt.hasClassName('hordeCalendarNextYear')) {
+            this.changeYear(1);
+        } else if (elt.hasClassName('hordeCalendarPrevMonth')) {
+            this.changeMonth(-1);
+        } else if (elt.hasClassName('hordeCalendarNextMonth')) {
+            this.changeMonth(1);
+        } else if (this.click_year && elt.hasClassName('hordeCalendarYear')) {
+            this.trigger.fire('Horde_Calendar:selectYear', new Date(this.year, this.month, 1));
+            this.hideCal();
+        } else if (this.click_month && elt.hasClassName('hordeCalendarMonth')) {
+            this.trigger.fire('Horde_Calendar:selectMonth', new Date(this.year, this.month, 1));
+            this.hideCal();
+        } else if (this.click_week && elt.hasClassName('hordeCalendarWeek')) {
+            this.trigger.fire('Horde_Calendar:selectWeek', new Date(this.year, this.month, elt.up('TR').down('A.hordeCalendarDay').textContent));
+            this.hideCal();
+        }
+
+        e.stop();
+    }
+
+};
+
+document.observe('dom:loaded', Horde_Calendar.init.bind(Horde_Calendar));
diff --git a/framework/Core/js/hordetree.js b/framework/Core/js/hordetree.js
new file mode 100644 (file)
index 0000000..aba38e1
--- /dev/null
@@ -0,0 +1,512 @@
+/**
+ * Provides the javascript class to create dynamic trees.
+ *
+ * Optionally uses the Horde_Tooltip class (tooltips.js).
+ *
+ * Custom Events
+ * -------------
+ * The 'memo' property of the Event object contains the original event object.
+ *
+ * 'Horde_Tree:expand'
+ *   Fired when a tree element is expanded.
+ *
+ * 'Horde_Tree:collapse'
+ *   Fired when a tree element is collapsed.
+ *
+ * Copyright 2003-2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author   Marko Djukic <marko@oblo.com>
+ * @author   Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @package  Core
+ */
+
+var Horde_Tree = Class.create({
+
+    initialize: function(opts)
+    {
+        this.opts = opts;
+
+        if (this.opts.initTree) {
+            this.renderTree(this.opts.initTree.nodes, this.opts.initTree.root_nodes, this.opts.initTree.is_static);
+            this.opts.initTree = null;
+        }
+
+        $(this.opts.target).observe('click', this._onClick.bindAsEventListener(this));
+
+        this.opts.ie6 = (navigator.userAgent.toLowerCase().substr(25,6)=="msie 6");
+    },
+
+    renderTree: function(nodes, rootNodes, renderStatic)
+    {
+        this.nodes = nodes;
+        this.rootNodes = rootNodes;
+        this.renderStatic = renderStatic;
+        this.dropline = [];
+        this.output = document.createDocumentFragment();
+
+        this._buildHeader();
+
+        this.rootNodes.each(function(r) {
+            this.buildTree(r, this.output);
+        }, this);
+
+        $(this.opts.target).update('');
+        $(this.opts.target).appendChild(this.output);
+
+        if (this.opts.ie6) {
+            /* Correct for frame scrollbar in IE6 by determining if a scrollbar
+             * is present, and if not readjusting the marginRight property to
+             * 0. See http://www.xs4all.nl/~ppk/js/doctypes.html for why this
+             * works */
+            if (document.documentElement.clientHeight == document.documentElement.offsetHeight) {
+                // no scrollbar present, take away extra margin
+                $(document.body).setStyle({ marginRight: 0 });
+            } else {
+                $(document.body).setStyle({ marginRight: '15px' });
+            }
+        }
+
+        // If using alternating row shading, work out correct shade.
+        if (this.opts.options.alternate) {
+            this.stripe();
+        }
+
+        if (window.Horde_Tooltips) {
+            window.Horde_ToolTips.attachBehavior();
+        }
+    },
+
+    _buildHeader: function()
+    {
+        if (this.opts.options.hideHeaders ||
+            !this.opts.header.size()) {
+            return;
+        }
+
+        var div = new Element('DIV');
+
+        this.opts.header.each(function(h) {
+            var tmp = new Element('SPAN').insert(h.html ? h.html : '&nbsp;');
+
+            if (h['class']) {
+                tmp.addClassName(h['class']);
+            }
+
+            div.appendChild(tmp);
+        }, this);
+
+        this.output.appendChild(div);
+    },
+
+    // Recursive function to walk through the tree array and build
+    // the output.
+    buildTree: function(nodeId, p)
+    {
+        var last_subnode, tmp,
+            node = this.nodes[nodeId];
+
+        this.buildLine(nodeId, p);
+
+        if (!Object.isUndefined(node.children)) {
+            last_subnode = node.children.last();
+            tmp = new Element('DIV', { id: 'nodeChildren_' + nodeId });
+            [ tmp ].invoke(node.expanded ? 'show' : 'hide');
+
+            node.children.each(function(c) {
+                if (c == last_subnode) {
+                    this.nodes[c].node_last = true;
+                }
+                this.buildTree(c, tmp);
+            }, this);
+
+            p.appendChild(tmp);
+        }
+    },
+
+    buildLine: function(nodeId, p)
+    {
+        var div, label, tmp,
+            column = 0,
+            node = this.nodes[nodeId];
+
+        div = new Element('DIV', { className: 'treeRow' });
+        if (node['class']) {
+            div.addClassName(node['class']);
+        }
+
+        // If we have headers, track which logical "column" we're in for
+        // any given cell of content.
+        if (node.extra && node.extra[0]) {
+            node.extra[0].each(function(n) {
+                div.insert(this._divClass(new Element('SPAN').update(n), column++));
+            }, this);
+        }
+
+        for (; column < this.opts.extraColsLeft; ++column) {
+            div.insert(this._divClass(new Element('SPAN').update('&nbsp;'), column));
+        }
+
+        div.insert(this._divClass(new Element('SPAN'), column));
+
+        tmp = document.createDocumentFragment();
+        for (i = Number(this.renderStatic); i < node.indent; ++i) {
+            tmp.appendChild(new Element('SPAN').addClassName('treeImg').addClassName(
+                'treeImg' + ((this.dropline[i] && this.opts.options.lines)
+                    ? this.opts.imgLine
+                    : this.opts.imgBlank)
+            ));
+        }
+
+        tmp.appendChild(this._setNodeToggle(nodeId));
+
+        if (node.url) {
+            label = new Element('A', { href: node.url }).insert(
+                this._setNodeIcon(nodeId)
+            ).insert(
+                node.label
+            );
+
+            if (node.urlclass) {
+                label.addClassName(node.urlclass);
+            } else if (this.opts.options.urlclass) {
+                label.addClassName(this.opts.options.urlclass);
+            }
+
+            if (node.title) {
+                label.writeAttribute('title', node.title);
+            }
+
+            if (node.target) {
+                label.writeAttribute('target', node.target);
+            } else if (this.opts.options.target) {
+                label.writeAttribute('target', this.opts.options.target);
+            }
+
+            //if (node.onclick) {
+            //    label.push(' onclick="' + node.onclick + '"');
+            //}
+
+            label = label.wrap('SPAN');
+        } else {
+            label = new Element('SPAN').addClassName('toggle').insert(
+                this._setNodeIcon(nodeId)
+            ).insert(
+                node.label
+            );
+        }
+
+        if (this.opts.options.multiline) {
+            div.insert(new Element('TABLE').insert(
+                new Element('TR').insert(
+                    new Element('TD').appendChild(tmp)
+                ).insert(
+                    new Element('TD').insert(label)
+                )
+            ));
+        } else {
+            div.appendChild(tmp);
+            div.insert(label)
+        }
+
+        ++column;
+
+        if (node.extra && node.extra[1]) {
+            node.extra[1].each(function(n) {
+                div.insert(this._divClass(new Element('SPAN').update(n), column++));
+            }, this);
+        }
+
+        for (; column < this.opts.extraColsRight; ++column) {
+            div.insert(this._divClass(new Element('SPAN').update('&nbsp;'), column));
+        }
+
+        p.appendChild(div);
+    },
+
+    _divClass: function(div, c)
+    {
+        if (this.opts.header[c] && this.opts.header[c]['class']) {
+            div.addClassName(this.opts.header[c]['class']);
+        }
+
+        return div;
+    },
+
+    _setNodeToggle: function(nodeId)
+    {
+        var node = this.nodes[nodeId];
+
+        if (node.indent == '0') {
+            // Top level with children.
+            this.dropline[0] = false;
+
+            if (this.renderStatic) {
+                return '';
+            }
+
+            switch (this._getLineType(nodeId)) {
+            case 1:
+            case 2:
+                this.dropline[0] = true;
+                break;
+            }
+        } else {
+            this.dropline[node.indent] = !node.node_last;
+        }
+
+        return new Element('SPAN', { id: "nodeToggle_" + nodeId }).addClassName('treeToggle').addClassName('treeImg').addClassName('treeImg' + this._getNodeToggle(nodeId));
+    },
+
+    _getNodeToggle: function(nodeId)
+    {
+        var type,
+            node = this.nodes[nodeId];
+
+        if (node.indent == '0') {
+            if (this.renderStatic) {
+                return '';
+            }
+
+            if (!this.opts.options.lines) {
+                return this.opts.imgBlank;
+            }
+
+            type = this._getLineType(nodeId);
+
+            if (node.children) {
+                // Top level with children.
+                if (node.expanded) {
+                    return type
+                        ? ((type == 2) ? this.opts.imgMinus : this.opts.imgMinusBottom)
+                        : this.opts.imgMinusOnly;
+                }
+
+                return type
+                    ? ((type == 2) ? this.opts.imgPlus : this.opts.imgPlusBottom)
+                    : this.opts.imgPlusOnly;
+            }
+
+            switch (type) {
+            case 0:
+                return this.opts.imgNullOnly;
+
+            case 1:
+                return this.opts.imgJoinTop;
+
+            case 2:
+                return this.opts.imgJoin;
+
+            case 3:
+                return this.opts.imgJoinBottom;
+            }
+        }
+
+        if (node.children) {
+            // Node with children.
+            if (!node.node_last) {
+                // Not last node.
+                if (!this.opts.options.lines) {
+                    return this.opts.imgBlank;
+                } else if (this.renderStatic) {
+                    return this.opts.imgJoin;
+                } else if (node.expanded) {
+                    return this.opts.imgMinus;
+                }
+
+                return this.opts.imgPlus;
+            }
+
+            // Last node.
+            if (!this.opts.options.lines) {
+                return this.opts.imgBlank;
+            } else if (this.renderStatic) {
+                return this.opts.imgJoinBottom;
+            } else if (node.expanded) {
+                return this.opts.imgMinusBottom;
+            }
+
+            return this.opts.imgPlusBottom;
+        }
+
+        // Node no children.
+        if (!node.node_last) {
+            // Not last node.
+            return this.opts.options.lines
+                ? this.opts.imgJoin
+                : this.opts.imgBlank;
+        }
+
+        // Last node.
+        return this.opts.options.lines
+            ? this.opts.imgJoinBottom
+            : this.opts.imgBlank;
+    },
+
+    _getLineType: function(nodeId)
+    {
+        if (this.opts.options.lines_base &&
+            this.rootNodes.size() > 1) {
+            switch (this.rootNodes.indexOf(nodeId)) {
+            case 0:
+                return 1;
+
+            case (this.rootNodes.size() - 1):
+                return 3;
+
+            default:
+                return 2;
+            }
+        }
+
+        return 0;
+    },
+
+    _setNodeIcon: function(nodeId)
+    {
+        var img,
+            node = this.nodes[nodeId];
+
+        // Image.
+        if (node.icon) {
+            // Node has a user defined icon.
+            img = new Element('IMG', { id: "nodeIcon_" + nodeId, src: (node.iconopen && node.expanded ? node.iconopen : node.icon) }).addClassName('treeIcon')
+        } else {
+            img = new Element('SPAN', { id: "nodeIcon_" + nodeId }).addClassName('treeIcon');
+            if (node.children) {
+                // Standard icon: node with children.
+                img.addClassName('treeImg' + (node.expanded ? this.opts.imgFolderOpen : this.opts.imgFolder));
+            } else {
+                // Standard icon: node, no children.
+                img.addClassName('treeImg' + this.opts.imgLeaf);
+            }
+        }
+
+        if (node.iconalt) {
+            img.writeAttribute('alt', node.iconalt);
+        }
+
+        return img;
+    },
+
+    toggle: function(nodeId)
+    {
+        var icon, nodeToggle, toggle, children,
+            node = this.nodes[nodeId];
+
+        node.expanded = !node.expanded;
+        if (children = $('nodeChildren_' + nodeId)) {
+            children.setStyle({ display: node.expanded ? 'block' : 'none' });
+        }
+
+        // Toggle the node's icon if it has separate open and closed
+        // icons.
+        if (icon = $('nodeIcon_' + nodeId)) {
+            // Image.
+            if (node.icon) {
+                icon.writeAttribute('src', (node.expanded && node.iconopen) ? node.iconopen : node.icon);
+            } else {
+                // Use standard icon set.
+                icon.writeAttribute('src', node.expanded ? this.opts.imgFolderOpen : this.opts.imgFolder);
+            }
+        }
+
+        // If using alternating row shading, work out correct shade.
+        if (this.opts.options.alternate) {
+            this.stripe();
+        }
+
+        if (toggle = $('nodeToggle_' + nodeId)) {
+            toggle.writeAttribute('class', 'treeToggle treeImg').addClassName('treeImg' + this._getNodeToggle(nodeId));
+        }
+
+        $(this.opts.target).fire(node.expanded ? 'Horde_Tree:expand' : 'Horde_Tree:collapse', nodeId);
+
+        this.saveState(nodeId, node.expanded)
+    },
+
+    stripe: function()
+    {
+        var classes = [ 'rowEven', 'rowOdd' ],
+            i = 0;
+
+        $(this.opts.target).select('DIV.treeRow').each(function(r) {
+            classes.each(r.removeClassName.bind(r));
+            if (r.clientHeight) {
+                r.addClassName(classes[++i % 2]);
+            }
+        });
+    },
+
+    saveState: function(nodeId, expanded)
+    {
+        if (this.opts.nocookie) {
+            return;
+        }
+
+        var newCookie = '',
+            newNodes = [],
+            oldCookie = this._getCookie(this.opts.target + '_expanded');
+
+        if (expanded) {
+            // Expand requested so add to cookie.
+            newCookie = (oldCookie ? oldCookie + ',' : '') + nodeId;
+        } else {
+            // Collapse requested so remove from cookie.
+            oldCookie.split(',').each(function(n) {
+                if (n != nodeId) {
+                    newNodes[newNodes.length] = n
+                }
+            });
+            newCookie = newNodes.join(',');
+        }
+
+        document.cookie = this.opts.target + '_expanded=exp' + escape(newCookie) + ';DOMAIN=' + this.opts.cookieDomain + ';PATH=' + this.opts.cookiePath + ';';
+    },
+
+    _getCookie: function(name)
+    {
+        var end,
+            dc = document.cookie,
+            prefix = name + '=exp',
+            begin = dc.indexOf('; ' + prefix);
+
+        if (begin == -1) {
+            begin = dc.indexOf(prefix);
+            if (begin != 0) {
+                return '';
+            }
+        } else {
+            begin += 2;
+        }
+
+        end = document.cookie.indexOf(';', begin);
+        if (end == -1) {
+            end = dc.length;
+        }
+
+        return unescape(dc.substring(begin + prefix.length, end));
+    },
+
+    _onClick: function(e)
+    {
+        var elt = e.element(),
+            id = elt.readAttribute('id');
+
+        if (elt.hasClassName('treeIcon')) {
+            elt = elt.up().previous();
+        } else if (elt.hasClassName('toggle')) {
+            elt = elt.previous();
+        }
+
+        id = elt.readAttribute('id');
+        if (id && id.startsWith('nodeToggle_')) {
+            this.toggle(id.substr(11));
+            e.stop();
+        }
+    }
+
+});
diff --git a/framework/Core/js/identityselect.js b/framework/Core/js/identityselect.js
new file mode 100644 (file)
index 0000000..ea337ad
--- /dev/null
@@ -0,0 +1,49 @@
+/**
+ * Horde identity selection javascript.
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Horde
+ * @package  Core
+ */
+
+var HordeIdentitySelect = {
+
+    newChoice: function()
+    {
+        var identity = $('identity'),
+            id = Number($F(identity));
+
+        if (id < 0) {
+            identity.up('FORM').reset();
+            identity.setValue(id);
+            return;
+        }
+
+        this.identities[id].each(function(a) {
+            var field = $(a[0]);
+
+            switch (a[1]) {
+            case "special":
+                identity.fire('HordeIdentitySelect:change', {
+                    i: id,
+                    pref: a[0]
+                });
+                break;
+
+            default:
+                field.setValue(a[2]);
+                break;
+            }
+        });
+    },
+
+    onDomLoad: function()
+    {
+        $('identity').observe('change', this.newChoice.bind(this));
+    }
+
+};
+
+document.observe('dom:loaded', HordeIdentitySelect.onDomLoad.bind(HordeIdentitySelect));
diff --git a/framework/Core/js/liquidmetal.js b/framework/Core/js/liquidmetal.js
new file mode 100644 (file)
index 0000000..bbb4a61
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * LiquidMetal, version: 0.1 (2009-02-05)
+ *
+ * A mimetic poly-alloy of Quicksilver's scoring algorithm, essentially
+ * LiquidMetal.
+ *
+ * For usage and examples, visit:
+ * http://github.com/rmm5t/liquidmetal
+ *
+ * Licensed under the MIT:
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ * Copyright (c) 2009, Ryan McGeary (ryanonjavascript -[at]- mcgeary [*dot*] org)
+ * Modified by the Horde Project to use compressibility.
+ *
+ * @category Horde
+ * @package  Core
+ */
+var LiquidMetal = {
+
+    SCORE_NO_MATCH: 0.0,
+    SCORE_MATCH: 1.0,
+    SCORE_TRAILING: 0.8,
+    SCORE_TRAILING_BUT_STARTED: 0.9,
+    SCORE_BUFFER: 0.85,
+
+    score: function(str, abbr)
+    {
+        // Short circuits
+        if (abbr.length == 0) {
+           return this.SCORE_TRAILING;
+        }
+        if (abbr.length > str.length) {
+            return this.SCORE_NO_MATCH;
+        }
+
+        var scores = this.buildScoreArray(str, abbr),
+            sum = 0.0;
+
+        scores.each(function(i) {
+            sum += i;
+        });
+
+        return (sum / scores.size());
+    },
+
+    buildScoreArray: function(str, abbr)
+    {
+        var lastIndex = -1,
+            lower = str.toLowerCase(),
+            scores = new Array(str.length),
+            started = false;
+
+        abbr.toLowerCase().split("").each(function(c) {
+            var index = to = lower.indexOf(c, lastIndex + 1),
+                val = this.SCORE_BUFFER;
+
+            if (index < 0) {
+                return this.fillArray(scores, this.SCORE_NO_MATCH);
+            }
+
+            if (index == 0) {
+                started = true;
+            }
+
+            if (this.isNewWord(str, index)) {
+                scores[index - 1] = 1;
+                to = index - 1;
+            } else if (!this.isUpperCase(str, index)) {
+                val = this.SCORE_NO_MATCH;
+            }
+
+            this.fillArray(scores, val, lastIndex + 1, val);
+
+            scores[index] = this.SCORE_MATCH;
+            lastIndex = index;
+        }.bind(this));
+
+        this.fillArray(scores, started ? this.SCORE_TRAILING_BUT_STARTED : this.SCORE_TRAILING, lastIndex + 1);
+
+        return scores;
+    },
+
+    isUpperCase: function(str, index)
+    {
+        var c = str.charAt(index);
+        return ("A" <= c && c <= "Z");
+    },
+
+    isNewWord: function(str, index)
+    {
+        var c = str.charAt(index - 1);
+        return (c == " " || c == "\t");
+    },
+
+    fillArray: function(arr, value, from, to)
+    {
+        from = Math.max(from || 0, 0);
+        to = Math.min(to || arr.length, arr.length);
+        for (var i = from; i < to; ++i) {
+            arr[i] = value;
+        }
+        return arr;
+    }
+};
diff --git a/framework/Core/js/open_html_helper.js b/framework/Core/js/open_html_helper.js
new file mode 100644 (file)
index 0000000..b57ab51
--- /dev/null
@@ -0,0 +1,67 @@
+/**
+ * Horde Html Helper Javascript Class
+ *
+ * Provides the javascript class insert html tags by clicking on icons.
+ *
+ * The helpers available:
+ *      emoticons - for inserting emoticons strings
+ *
+ * Copyright 2003-2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author   Marko Djukic <marko@oblo.com>
+ * @category Horde
+ * @package  Core
+ * @todo     Add handling for font tags, tables, etc.
+ */
+
+var Horde_Html_Helper = {
+
+    iconlist: [],
+    targetElement: null,
+
+    open: function(type, target)
+    {
+        var cell, row, table, tbody,
+            lay = $('htmlhelper_' + target);
+        this.targetElement = $(target);
+
+        if (lay.getStyle('display') == 'block') {
+            lay.hide();
+            return false;
+        }
+
+        if (lay.firstChild) {
+            lay.removeChild(lay.firstChild);
+        }
+
+        tbody = new Element('TBODY');
+        table = new Element('TABLE', { border: 0, cellSpacing: 0 }).insert(tbody);
+
+        if (type == 'emoticons') {
+            row = new Element('TR');
+            cell = new Element('TD');
+
+            iconlist.each(function(i) {
+                var link =
+                    new Element('A', { href: '#' }).insert(
+                        new Element('IMG', { align: 'middle', border: 0, src: i[0] })
+                    );
+                cell.appendChild(link);
+
+                link.observe('click', function(e) {
+                    this.targetElement.setValue($F(this.targetElement) + i[1] + ' ');
+                    e.stop();
+                }.bindAsEventListener(this));
+            });
+
+            row.insert(cell);
+            tbody.insert(row);
+            table.insert(tbody);
+        }
+
+        lay.insert(table).setStyle({ display: 'block' });
+    }
+};
diff --git a/framework/Core/js/prefs.js b/framework/Core/js/prefs.js
new file mode 100644 (file)
index 0000000..b717343
--- /dev/null
@@ -0,0 +1,17 @@
+/**
+ * Provides the javascript for the prefs page.
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Horde
+ * @package  Core
+ */
+
+document.observe('dom:loaded', function() {
+    $('appsubmit').hide();
+
+    $('app').observe('change', function() {
+        $('appswitch').submit();
+    });
+});
diff --git a/framework/Core/js/prettyautocomplete.js b/framework/Core/js/prettyautocomplete.js
new file mode 100644 (file)
index 0000000..3dbfdac
--- /dev/null
@@ -0,0 +1,284 @@
+/**
+ * @category Horde
+ * @package  Core
+ */
+var PrettyAutocompleter = Class.create({
+
+    initialize: function(element, params)
+    {
+        this.p = Object.extend({
+            // Outer div/fake input box and CSS class
+            box: 'HordeACBox',
+            boxClass: 'hordeACBox',
+            // <ul> CSS class
+            listClass: 'hordeACList',
+            // CSS class for real input field
+            growingInputClass: 'hordeACTrigger',
+            // Dom id for <li> that holds the input field.
+            triggerContainer: 'hordeACTriggerContainer',
+            // Min pixel width of input field
+            minTriggerWidth: 100,
+            // Allow for a function that filters the display value
+            // This function should *always* return escaped HTML
+            displayFilter: function(t) { return t.escapeHTML() },
+            filterCallback: this._filterChoices.bind(this),
+            onAdd: Prototype.K,
+            onRemove: Prototype.K
+        }, params || {});
+
+        // Array to hold the currently selected items to ease with removing
+        // them, assuring no duplicates etc..
+        this.selectedItems = [];
+
+        // The original input element is transformed into the hidden input
+        // field that hold the text values (p.items), while p.trigger is
+        // the borderless input field located in p.box
+        this.p.items = element;
+        this.p.trigger = element + 'real';
+        this.initialized = false;
+        this._enabled = true;
+    },
+
+    /**
+     * Initializes the autocompleter, builds the dom structure, registers
+     * events, etc...
+     */
+    init: function()
+    {
+        if (this.initialized) {
+            return;
+        }
+
+        // Build the DOM structure
+        this.buildStructure();
+
+        // Remember the bound method to unregister later.
+        this._boundProcessValue = this._processValue.bind(this);
+        var trigger = $(this.p.trigger);
+        trigger.observe('keydown', this._onKeyDown.bindAsEventListener(this));
+        trigger.observe('blur', this._boundProcessValue);
+
+        // Make sure the p.items element is hidden
+        if (!this.p.debug) {
+            $(this.p.items).hide();
+        }
+
+        // Set the updateElement callback
+        this.p.onSelect = this._updateElement.bind(this);
+
+        // Look for clicks on the box to simulate clicking in an input box
+        $(this.p.box).observe('click', function() { trigger.focus() });
+        trigger.observe('blur', this._resize.bind(this));
+        trigger.observe('keydown', this._resize.bind(this));
+        trigger.observe('keypress', this._resize.bind(this));
+        trigger.observe('keyup', this._resize.bind(this));
+
+        // Create the underlaying Autocompleter
+        this.p.uri += '/input=' + this.p.trigger;
+
+        this.p.onShow = this._knvShow.bind(this);
+        this.p.onHide = this._knvHide.bind(this);
+
+        // Make sure the knl is contained in the overlay
+        this.p.domParent = this.p.box;
+        new Ajax.Autocompleter(this.p.trigger, this.p.uri, this.p);
+
+        // Prepopulate the items and the container elements?
+        if (typeof this.p.existing != 'undefined') {
+            this.init(this.p.existing);
+        }
+
+        this.initialized = true;
+    },
+
+    /**
+     * Resets the autocompleter's state.
+     */
+    reset: function(existing)
+    {
+        if (!this.initialized) {
+            this.init();
+        }
+
+        // TODO: Resize the trigger field to fill the current line?
+        // Clear any existing values
+        if (this.selectedItems.length) {
+            $(this.p.box).select('li.' + this.p.listClass + 'Item').each(function(item) {
+                this.removeItemNode(item);
+            }.bind(this));
+        }
+
+        // Clear the hidden items field
+        $(this.p.items).value = '';
+
+        // Add any initial values
+        if (typeof existing != 'undefined' && existing.length) {
+            for (var i = 0, l = existing.length; i < l; i++) {
+                this.addNewItemNode(existing[i]);
+            }
+        }
+        this._enabled = true;
+    },
+
+    buildStructure: function()
+    {
+        // Build the outter box
+        var box = new Element('div', { id: this.p.box, className: this.p.boxClass }).setStyle({ position: 'relative' });
+
+        // The list - where the choosen items are placed as <li> nodes
+        var list = new Element('ul', { className: this.p.listClass });
+
+        // The input element and the <li> wraper
+        var inputListItem = new Element('li', {
+                className: this.p.listClass + 'Member',
+                id: this.p.triggerContainer }),
+            growingInput = new Element('input', {
+                className: this.p.growingInputClass,
+                id: this.p.trigger,
+                name: this.p.trigger,
+                autocomplete: 'off' });
+
+        // Create a hidden span node to help calculate the needed size
+        // of the input field.
+        this.sizer = new Element('span').setStyle({ float: 'left', display: 'inline-block', position: 'absolute', left: '-1000px' });
+
+        inputListItem.update(growingInput);
+        list.update(inputListItem);
+        box.update(list);
+        box.insert(this.sizer);
+
+        // 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)
+        box.insert(Element.replace($(this.p.items), box));
+    },
+
+    shutdown: function()
+    {
+        this._processValue();
+    },
+
+    _onKeyDown: function(e)
+    {
+        // Check for a comma
+        if (e.keyCode == 188) {
+            this._processValue();
+            e.stop();
+        }
+    },
+
+    _processValue: function()
+    {
+        var value = $F(this.p.trigger).replace(/^,/, '').strip();
+        if (value.length) {
+            this.addNewItemNode(value);
+            this.p.onAdd(value);
+        }
+    },
+
+    _resize: function()
+    {
+        this.sizer.update($(this.p.trigger).value);
+        newSize = Math.min(this.sizer.getWidth(), $(this.p.box).getWidth());
+        newSize = Math.max(newSize, this.p.minTriggerWidth);
+        $(this.p.trigger).setStyle({ width: newSize + 'px' });
+    },
+
+    // Used as the updateElement callback.
+    _updateElement: function(item)
+    {
+        this.addNewItemNode(item);
+        this.p.onAdd(item);
+    },
+
+    addNewItemNode: function(value)
+    {
+        // Don't add if it's already present.
+        for (var x = 0, len = this.selectedItems.length; x < len; x++) {
+            if (this.selectedItems[x].rawValue == value) {
+                $(this.p.trigger).value = '';
+                return;
+            }
+        }
+
+        var displayValue = this.p.displayFilter(value),
+            newItem = new Element('li', { className: this.p.listClass + 'Member ' + this.p.listClass + 'Item' }).update(displayValue),
+            x = new Element('img', { className: 'hordeACItemRemove', src: this.p.deleteIcon });
+        x.observe('click', this._removeItemHandler.bindAsEventListener(this));
+        newItem.insert(x);
+        $(this.p.triggerContainer).insert({ before: newItem });
+        $(this.p.trigger).value = '';
+
+        // Add to hidden input field.
+        if ($(this.p.items).value) {
+            $(this.p.items).value = $(this.p.items).value + ', ' + value;
+        } else {
+            $(this.p.items).value = value;
+        }
+
+        // ...and keep the selectedItems array up to date.
+        this.selectedItems.push({ rawValue: value, displayValue: displayValue });
+    },
+
+    removeItemNode: function(item)
+    {
+        var value = item.collectTextNodesIgnoreClass('informal');
+        for (var x = 0, len = this.selectedItems.length; x < len; x++) {
+            if (this.selectedItems[x].displayValue.unescapeHTML() == value) {
+               this.selectedItems.splice(x, 1);
+               break;
+            }
+        }
+        item.remove();
+        this.p.onRemove(value);
+    },
+
+    disable: function()
+    {
+      if (!this._enabled || !this.initialized) {
+          return;
+      }
+
+      this._enabled = false;
+      $(this.p.box).select('.hordeACItemRemove').invoke('toggle');
+      $(this.p.trigger).disable();
+    },
+
+    enable: function()
+    {
+        if (this._enabled) {
+            return;
+        }
+        this._enabled = true;
+        $(this.p.box).select('.hordeACItemRemove').invoke('toggle');
+        $(this.p.trigger).enable();
+    },
+
+    _removeItemHandler: function(e)
+    {
+        var realValues = [], x, len;
+        this.removeItemNode(e.element().up());
+        for (x = 0, len = this.selectedItems.length; x < len; x++) {
+            realValues.push(this.selectedItems[x].rawValue);
+        }
+        $(this.p.items).value = realValues.join(',');
+    },
+
+    _filterChoices: function(c)
+    {
+        this.selectedItems.each(function(item) {
+            c = c.without(item.rawValue);
+        });
+        return c;
+    },
+
+    _knvShow: function(l)
+    {
+        $(this.p.trigger).stopObserving('blur', this._boundProcessValue);
+    },
+
+    _knvHide: function(l)
+    {
+        $(this.p.trigger).observe('blur', this._boundProcessValue);
+    }
+});
diff --git a/framework/Core/js/sourceselect.js b/framework/Core/js/sourceselect.js
new file mode 100644 (file)
index 0000000..b7cdf62
--- /dev/null
@@ -0,0 +1,138 @@
+/**
+ * Provides the javascript for managing the source selection widget.
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Horde
+ * @package  Core
+ */
+
+var HordeSourceSelectPrefs = {
+
+    // Vars defaulting to null: source_list
+
+    setSourcesHidden: function()
+    {
+        var out = [], ss;
+
+        if (this.source_list) {
+            ss = $F('source_select');
+            if (ss) {
+                this.source_list.each(function(s) {
+                    if (s.source == ss) {
+                        s.selected = $F('selected_sources');
+                    }
+                    out.push([ s.source, s.selected ]);
+                });
+            }
+        } else {
+            $A($('selected_sources').options).slice(1).each(function(s) {
+                out.push(s.value);
+            });
+        }
+
+        $('sources').setValue(Object.toJSON(out));
+    },
+
+    moveAction: function(from, to)
+    {
+        var moved = false;
+
+        $(from).childElements().each(function(c) {
+            if (c.selected) {
+                c.remove();
+                c.selected = false;
+                $(to).insert(c);
+                moved = true;
+            }
+        });
+
+
+        if (moved) {
+            $(to).fire('HordeSourceSelectPrefs:add');
+            $(from).fire('HordeSourceSelectPrefs:remove');
+            this.setSourcesHidden();
+        }
+    },
+
+    moveSource: function(e, mode)
+    {
+        var sa = $('selected_sources'), sel, tmp;
+
+        if (sa.selectedIndex < 1 || sa.length < 3) {
+            return;
+        }
+
+        // Deselect everything but the first selected item
+        sa.childElements().each(function(s) {
+            if (sel) {
+                s.selected = false;
+            } else if (s.selected) {
+                sel = s;
+            }
+        });
+
+        switch (mode) {
+        case 'down':
+            tmp = sel.next();
+            if (tmp) {
+                sel.remove();
+                tmp.insert({ after: sel });
+            }
+            break;
+
+        case 'up':
+            tmp = sel.previous();
+            if (tmp && tmp.value) {
+                sel.remove();
+                tmp.insert({ before: sel });
+            }
+            break;
+        }
+
+        this.setSourcesHidden();
+        e.stop();
+    },
+
+    changeSource: function()
+    {
+        var source,
+            sel = $('selected_sources'),
+            ss = $('source_select'),
+            unsel = $('unselected_sources'),
+            val = $F(ss);
+
+        sel.down().siblings().invoke('remove');
+        unsel.down().siblings().invoke('remove');
+
+        if (val) {
+            source = this.source_list.find(function(s) {
+                return val == s.source;
+            });
+            source.selected.each(function(s) {
+                sel.insert(new Option(s.v, s.l));
+            });
+            source.unselected.each(function(u) {
+                unsel.insert(new Option(s.v, s.l));
+            });
+        }
+    },
+
+    onDomLoad: function()
+    {
+        if (this.source_list) {
+            $('source_select').observe('change', this.changeSource.bind(this));
+        }
+
+        this.setSourcesHidden();
+
+        $('addsource').observe('click', this.moveAction.bind(this, 'unselected_sources', 'selected_sources'));
+        $('removesource').observe('click', this.moveAction.bind(this, 'selected_sources', 'unselected_sources'));
+        $('moveup').observe('click', this.moveSource.bindAsEventListener(this, 'up'));
+        $('movedown').observe('click', this.moveSource.bindAsEventListener(this, 'down'));
+    }
+
+};
+
+document.observe('dom:loaded', HordeSourceSelectPrefs.onDomLoad.bind(HordeSourceSelectPrefs));
diff --git a/framework/Core/js/spellchecker.js b/framework/Core/js/spellchecker.js
new file mode 100644 (file)
index 0000000..e3ae778
--- /dev/null
@@ -0,0 +1,287 @@
+/**
+ * This spell checker was inspired by work done by Garrison Locke, but
+ * was rewritten almost completely by Chuck Hagenbuch to use
+ * Prototype/Scriptaculous.
+ *
+ * Requires: prototype.js (v1.6.1+), KeyNavList.js
+ *
+ * Copyright 2005-2010 The Horde Project (http://www.horde.org/)
+ *
+ * Custom Events:
+ * --------------
+ * Custom events are triggered on the target element.
+ *
+ * 'SpellChecker:after'
+ *    Fired when the spellcheck processing ends.
+ *
+ * 'SpellChecker:before'
+ *    Fired before the spellcheck is performed.
+ *
+ * 'SpellChecker:noerror'
+ *    Fired when no spellcheck errors are found.
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Horde
+ * @package  Core
+ */
+
+var SpellChecker = Class.create({
+
+    // Vars used and defaulting to null:
+    //   bad, choices, disabled, htmlAreaParent, lc, locale, reviewDiv,
+    //   statusButton, statusClass, suggestions, target, url
+    options: {},
+    resumeOnDblClick: true,
+    state: 'CheckSpelling',
+
+    // Options:
+    //   bs = (array) Button states
+    //   locales = (array) List of locales. See KeyNavList for structure.
+    //   sc = (string) Status class
+    //   statusButton = (string/element) DOM ID or element of the status
+    //                  button
+    //   target = (string|Element) DOM element containing data
+    //   url = (string) URL of specllchecker handler
+    initialize: function(opts)
+    {
+        var d, lc, tmp, ul;
+
+        this.url = opts.url;
+        this.target = $(opts.target);
+        this.statusButton = $(opts.statusButton);
+        this.buttonStates = opts.bs;
+        this.statusClass = opts.sc || '';
+        this.disabled = false;
+
+        this.options.onComplete = this.onComplete.bind(this);
+
+        document.observe('click', this.onClick.bindAsEventListener(this));
+
+        if (opts.locales) {
+            this.lc = new KeyNavList(this.statusButton, {
+                list: opts.locales,
+                onChoose: this.setLocale.bindAsEventListener(this)
+            });
+
+            this.statusButton.insert({ after: new Element('SPAN', { className: 'spellcheckPopdownImg' }) });
+        }
+
+        this.setStatus('CheckSpelling');
+    },
+
+    setLocale: function(locale)
+    {
+        this.locale = locale;
+    },
+
+    targetValue: function()
+    {
+        return Object.isUndefined(this.target.value)
+            ? this.target.innerHTML
+            : this.target.value;
+    },
+
+    spellCheck: function()
+    {
+        this.target.fire('SpellChecker:before');
+
+        var opts = Object.clone(this.options),
+            p = $H(),
+            url = this.url;
+
+        this.setStatus('Checking');
+
+        p.set(this.target.identify(), this.targetValue());
+        opts.parameters = p.toQueryString();
+
+        if (this.locale) {
+            url += '/locale=' + this.locale;
+        }
+        if (this.htmlAreaParent) {
+            url += '/html=1';
+        }
+
+        new Ajax.Request(url, opts);
+    },
+
+    onComplete: function(request)
+    {
+        var bad, content, washidden,
+            i = 0,
+            result = request.responseJSON;
+
+        if (Object.isUndefined(result)) {
+            this.setStatus('Error');
+            return;
+        }
+
+        this.suggestions = result.suggestions || [];
+
+        if (!this.suggestions.size()) {
+            this.setStatus('CheckSpelling');
+            this.target.fire('SpellChecker:noerror');
+            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 = '<span index="' + (i++) + '" class="spellcheckIncorrect">' + node + '</span>';
+            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: this.target.readAttribute('class') }).addClassName('spellcheck').setStyle({ overflow: 'auto' });
+            if (this.resumeOnDblClick) {
+                this.reviewDiv.observe('dblclick', this.resume.bind(this));
+            }
+        }
+
+        if (!this.target.visible()) {
+            this.target.show();
+            washidden = true;
+        }
+        this.reviewDiv.setStyle({ width: this.target.clientWidth + 'px', height: this.target.clientHeight + 'px'});
+        if (washidden) {
+            this.target.hide();
+        }
+
+        if (!this.htmlAreaParent) {
+            content = content.replace(/~~~/g, '<br />');
+        }
+        this.reviewDiv.update(content);
+
+        if (this.htmlAreaParent) {
+            $(this.htmlAreaParent).insert({ bottom: this.reviewDiv });
+        } else {
+            this.target.hide().insert({ before: this.reviewDiv });
+        }
+
+        this.setStatus('ResumeEdit');
+    },
+
+    onClick: function(e)
+    {
+        var data = [], index, elt = e.element();
+
+        if (this.disabled) {
+            return;
+        }
+
+        if (elt == this.statusButton) {
+            switch (this.state) {
+            case 'CheckSpelling':
+                this.spellCheck();
+                break;
+
+            case 'ResumeEdit':
+                this.resume();
+                break;
+            }
+
+            e.stop();
+        } else if (elt.hasClassName('spellcheckPopdownImg')) {
+            this.lc.show();
+            this.lc.ignoreClick(e);
+            e.stop();
+        } else if (elt.hasClassName('spellcheckIncorrect')) {
+            index = e.element().readAttribute('index');
+
+            $A(this.suggestions[index]).each(function(node) {
+                data.push({ l: node, v: node });
+            });
+
+            if (this.choices) {
+                this.choices.updateBase(elt);
+                this.choices.opts.onChoose = function(val) {elt.update(val).writeAttribute({ className: 'spellcheckCorrected' });};
+            } else {
+                this.choices = new KeyNavList(elt, {
+                    esc: true,
+                    onChoose: function(val) {
+                        elt.update(val).writeAttribute({ className: 'spellcheckCorrected' });
+                    }
+                });
+            }
+
+            this.choices.show(data);
+            this.choices.ignoreClick(e);
+            e.stop();
+        }
+    },
+
+    resume: function()
+    {
+        if (!this.reviewDiv) {
+            return;
+        }
+
+        var t;
+
+        this.reviewDiv.select('span.spellcheckIncorrect').each(function(n) {
+            n.replace(n.innerHTML);
+        });
+
+        t = this.reviewDiv.innerHTML;
+        if (!this.htmlAreaParent) {
+            t = t.replace(/<br *\/?>/gi, '~~~').unescapeHTML().replace(/~~~/g, "\n");
+        }
+        this.target.setValue(t);
+        this.target.enable();
+
+        if (this.resumeOnDblClick) {
+            this.reviewDiv.stopObserving('dblclick');
+        }
+        this.reviewDiv.remove();
+        this.reviewDiv = null;
+
+        this.setStatus('CheckSpelling');
+
+        if (!this.htmlAreaParent) {
+            this.target.show();
+        }
+
+        this.target.fire('SpellChecker:after');
+    },
+
+    setStatus: function(state)
+    {
+        if (!this.statusButton) {
+            return;
+        }
+
+        this.state = state;
+        switch (this.statusButton.tagName) {
+        case 'INPUT':
+            this.statusButton.setValue(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;
+    },
+
+    disable: function(disable)
+    {
+        this.disabled = disable;
+    }
+
+});
index 69926c5..a179722 100644 (file)
@@ -55,6 +55,20 @@ Application Framework.</description>
  </notes>
  <contents>
   <dir baseinstalldir="/" name="/">
+   <dir name="js">
+    <file name="addressbooksprefs.js" role="horde" />
+    <file name="alarmprefs.js" role="horde" />
+    <file name="autocomplete.js" role="horde" />
+    <file name="calendar.js" role="horde" />
+    <file name="hordetree.js" role="horde" />
+    <file name="identityselect.js" role="horde" />
+    <file name="liquidmetal.js" role="horde" />
+    <file name="open_html_helper.js" role="horde" />
+    <file name="prefs.js" role="horde" />
+    <file name="prettyautocomplete.js" role="horde" />
+    <file name="spellchecker.js" role="horde" />
+    <file name="sourceselect.js" role="horde" />
+   </dir> <!-- /js -->
    <dir name="lib">
     <dir name="Horde">
      <dir name="Config">
@@ -373,8 +387,25 @@ Application Framework.</description>
    </package>
   </optional>
  </dependencies>
+ <usesrole>
+  <role>horde</role>
+  <package>Role</package>
+  <channel>pear.horde.org</channel>
+ </usesrole>
  <phprelease>
   <filelist>
+   <install as="js/addressbooksprefs.js" name="js/addressbooksprefs.js" />
+   <install as="js/alarmprefs.js" name="js/alarmprefs.js" />
+   <install as="js/autocomplete.js" name="js/autocomplete.js" />
+   <install as="js/calendar.js" name="js/calendar.js" />
+   <install as="js/hordetree.js" name="js/hordetree.js" />
+   <install as="js/identityselect.js" name="js/identityselect.js" />
+   <install as="js/liquidmetal.js" name="js/liquidmetal.js" />
+   <install as="js/open_html_helper.js" name="js/open_html_helper.js" />
+   <install as="js/prefs.js" name="js/prefs.js" />
+   <install as="js/prettyautocomplete.js" name="js/prettyautocomplete.js" />
+   <install as="js/spellchecker.js" name="js/spellchecker.js" />
+   <install as="js/sourceselect.js" name="js/sourceselect.js" />
    <install as="Horde.php" name="lib/Horde.php" />
    <install as="Horde/Config.php" name="lib/Horde/Config.php" />
    <install as="Horde/ErrorHandler.php" name="lib/Horde/ErrorHandler.php" />
diff --git a/horde/js/addressbooksprefs.js b/horde/js/addressbooksprefs.js
deleted file mode 100644 (file)
index 2014254..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/**
- * Provides the javascript for managing addressbooks.
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- */
-
-var HordeAddressbooksPrefs = {
-
-    // Variables set by other code: fields, nonetext
-
-    updateSearchFields: function()
-    {
-        var tmp,
-            sv = $F('selected_sources'),
-            sf = $('search_fields_select');
-
-        sf.childElements().invoke('remove');
-
-        if (sv.size() == 1) {
-            tmp = this.fields.get(sv.first());
-            tmp.entries.each(function(o) {
-                var opt = new Option(o.label, o.name);
-                if (tmp.selected.include(o.name)) {
-                    opt.selected = true;
-                }
-                sf.insert(opt);
-            });
-        } else {
-            tmp = new Option(this.nonetext, '');
-            tmp.disabled = true;
-            sf.insert(tmp);
-        }
-    },
-
-    changeSearchFields: function()
-    {
-        var tmp,
-            out = $H(),
-            sv = $F('selected_sources');
-
-        if (sv.size() == 1) {
-            tmp = this.fields.get(sv.first());
-            tmp.selected = $F('search_fields_select');
-            this.fields.set(sv.first(), tmp);
-
-            this.fields.each(function(f) {
-                out.set(f.key, f.value.selected);
-            });
-
-            $('search_fields').setValue(Object.toJSON(out));
-        }
-    },
-
-    onDomLoad: function()
-    {
-        this.fields = $H(this.fields);
-
-        this.updateSearchFields();
-
-        $('search_fields_select').observe('change', this.changeSearchFields.bind(this));
-        $('selected_sources').observe('change', this.updateSearchFields.bind(this));
-        $('selected_sources').observe('HordeSourceSelectPrefs:remove', this.updateSearchFields.bind(this));
-    }
-
-};
-
-document.observe('dom:loaded', HordeAddressbooksPrefs.onDomLoad.bind(HordeAddressbooksPrefs));
diff --git a/horde/js/alarmprefs.js b/horde/js/alarmprefs.js
deleted file mode 100644 (file)
index c226b90..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * Provides the javascript for managing alarms.
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- */
-
-var HordeAlarmPrefs = {
-
-    // Variables defaulting to null: pref
-
-    updateParams: function()
-    {
-        [ 'notify', 'mail', 'sms' ].each(function(method) {
-            var p = $(method + 'Params');
-            if (p) {
-                if ($(this.pref).getValue().include(method)) {
-                    p.show();
-                } else {
-                    p.hide();
-                }
-            }
-        }, this);
-    },
-
-    onDomLoad: function()
-    {
-        $(this.pref).observe('change', this.updateParams.bind(this));
-        this.updateParams();
-    }
-
-};
-
-document.observe('dom:load', HordeAlarmPrefs.onDomLoad.bind(HordeAlarmPrefs));
diff --git a/horde/js/autocomplete.js b/horde/js/autocomplete.js
deleted file mode 100644 (file)
index 0cb4df0..0000000
+++ /dev/null
@@ -1,330 +0,0 @@
-/**
- * autocomplete.js - A javascript library which implements autocomplete.
- * Requires prototype.js v1.6.0.2+, scriptaculous v1.8.0+ (effects.js),
- * and keynavlist.js.
- *
- * Adapted from script.aculo.us controls.js v1.8.0
- *   (c) 2005-2007 Thomas Fuchs, Ivan Krstic, and Jon Tirsen
- *   Contributors: Richard Livsey, Rahul Bhargava, Rob Wills
- *   http://script.aculo.us/
- *
- * The original script was freely distributable under the terms of an
- * MIT-style license.
- *
- * Usage:
- * ------
- * TODO: options = autoSelect, frequency, minChars, onSelect, onShow, onType,
- *                 paramName, tokens
- *
- * Copyright 2007-2010 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 Autocompleter = {};
-
-Autocompleter.Base = Class.create({
-    baseInitialize: function(elt, opts)
-    {
-        this.elt = elt = $(elt);
-        this.changed = false;
-        this.observer = null;
-        this.oldval = $F(elt);
-        this.opts = Object.extend({
-            frequency: 0.4,
-            indicator: null,
-            minChars: 1,
-            onSelect: Prototype.K,
-            onShow: Prototype.K,
-            onType: Prototype.K,
-            filterCallback: Prototype.K,
-            paramName: elt.readAttribute('name'),
-            tokens: [],
-            keydownObserver: this.elt
-        }, (this._setOptions ? this._setOptions(opts) : (opts || {})));
-
-        // Force carriage returns as token delimiters anyway
-        if (!this.opts.tokens.include('\n')) {
-            this.opts.tokens.push('\n');
-        }
-
-        elt.writeAttribute('autocomplete', 'off');
-        elt.observe("keydown", this._onKeyDown.bindAsEventListener(this));
-    },
-
-    _onKeyDown: function(e)
-    {
-        var a = document.activeElement;
-
-        if (Object.isUndefined(a) || a == this.elt) {
-            switch (e.keyCode) {
-            case 0:
-                if (!Prototype.Browser.WebKit) {
-                    break;
-                }
-                // Fall-through
-
-            // Ignore events caught by KevNavList
-            case Event.KEY_DOWN:
-            case Event.KEY_ESC:
-            case Event.KEY_RETURN:
-            case Event.KEY_TAB:
-            case Event.KEY_UP:
-                return;
-            }
-
-            this.changed = true;
-
-            if (this.observer) {
-                clearTimeout(this.observer);
-            }
-
-            this.observer = this.onObserverEvent.bind(this).delay(this.opts.frequency);
-        }
-    },
-
-    updateChoices: function(choices)
-    {
-        var a = document.activeElement, c = [], re;
-
-        if (this.changed ||
-            (Object.isUndefined(a) || a != this.elt)) {
-            return;
-        }
-
-        if (this.opts.indicator) {
-            $(this.opts.indicator).hide();
-        }
-
-        choices = this.opts.filterCallback(choices);
-        if (!choices.size()) {
-            if (this.knl) {
-                this.knl.hide();
-            }
-            this.getNewVal(this.lastentry);
-        } else if (choices.size() == 1 && this.opts.autoSelect) {
-            this.onSelect(choices.first());
-            if (this.knl) {
-                this.knl.hide();
-            }
-        } else {
-            re = new RegExp("(" + this.getToken() + ")", "i");
-
-            choices.each(function(n) {
-                c.push({
-                    l: n.escapeHTML().gsub(re, '<strong>#{1}</strong>'),
-                    v: n
-                });
-            });
-
-            if (!this.knl) {
-                this.knl = new KeyNavList(this.elt, { onChoose: this.onSelect.bind(this),
-                                                        onShow: this.opts.onShow.bind(this),
-                                                     domParent: this.opts.domParent,
-                                               keydownObserver: this.opts.keydownObserver});
-            }
-
-            this.knl.show(c);
-        }
-    },
-
-    onObserverEvent: function()
-    {
-        this.changed = false;
-
-        var entry = this.getToken();
-
-        if (entry.length >= this.opts.minChars) {
-            entry = this.opts.onType(entry);
-        }
-
-        if (entry.length) {
-            if (this.opts.indicator) {
-                $(this.opts.indicator).show();
-            }
-            this.lastentry = entry;
-            this.getUpdatedChoices(entry);
-        } else if (this.knl) {
-            this.knl.hide();
-        }
-    },
-
-    getToken: function()
-    {
-        var bounds = this.getTokenBounds();
-        return $F(this.elt).substring(bounds[0], bounds[1]).strip();
-    },
-
-    getTokenBounds: function()
-    {
-        var diff, i, index, l, offset, tp,
-            t = this.opts.tokens,
-            value = $F(this.elt),
-            nextTokenPos = value.length,
-            prevTokenPos = -1,
-            boundary = Math.min(nextTokenPos, this.oldval.length);
-
-        if (value.strip().empty()) {
-            return [ -1, 0 ];
-        }
-
-        diff = boundary;
-        for (i = 0; i < boundary; ++i) {
-            if (value[i] != this.oldval[i]) {
-                diff = i;
-                break;
-            }
-        }
-
-        offset = (diff == this.oldval.length ? 1 : 0);
-
-        for (index = 0, l = t.length; index < l; ++index) {
-            tp = value.lastIndexOf(t[index], diff + offset - 1);
-            if (tp > prevTokenPos) {
-                prevTokenPos = tp;
-            }
-            tp = value.indexOf(t[index], diff + offset);
-            if (tp != -1 && tp < nextTokenPos) {
-                nextTokenPos = tp;
-            }
-        }
-        return [ prevTokenPos + 1, nextTokenPos ];
-    },
-
-    onSelect: function(entry)
-    {
-        if (entry) {
-            this.elt.setValue(this.opts.onSelect(this.getNewVal(entry))).focus();
-            if (this.knl) {
-                this.knl.markSelected();
-            }
-        }
-    },
-
-    getNewVal: function(entry)
-    {
-        var bounds = this.getTokenBounds(), newval, v, ws;
-
-        if (bounds[0] == -1) {
-            newval = entry;
-        } else {
-            v = $F(this.elt);
-            newval = v.substr(0, bounds[0]);
-            ws = v.substr(bounds[0]).match(/^\s+/);
-            if (ws) {
-                newval += ws[0];
-            }
-            newval += entry + v.substr(bounds[1]);
-        }
-
-        this.oldval = newval;
-
-        return newval;
-    }
-
-});
-
-Ajax.Autocompleter = Class.create(Autocompleter.Base, {
-
-    initialize: function(element, url, opts)
-    {
-        this.baseInitialize(element, opts);
-        this.opts = Object.extend(this.opts, {
-            asynchronous: true,
-            onComplete: this._onComplete.bind(this),
-            defaultParams: $H(this.opts.parameters)
-        });
-        this.url = url;
-        this.cache = $H();
-    },
-
-    getUpdatedChoices: function(t)
-    {
-        var p,
-            o = this.opts,
-            c = this.cache.get(t);
-
-        if (c) {
-            this.updateChoices(c);
-        } else {
-            p = Object.clone(o.defaultParams);
-            p.set(o.paramName, t);
-            o.parameters = p.toQueryString();
-            new Ajax.Request(this.url, o);
-        }
-    },
-
-    _onComplete: function(request)
-    {
-        this.updateChoices(this.cache.set(this.getToken(), request.responseJSON));
-    }
-});
-
-Autocompleter.Local = Class.create(Autocompleter.Base, {
-
-    initialize: function(element, arr, opts)
-    {
-        this.baseInitialize(element, opts);
-        this.opts.arr = arr;
-    },
-
-    getUpdatedChoices: function(entry)
-    {
-        var choices,
-            csort = [],
-            entry_len = entry.length,
-            i = 0,
-            o = this.opts;
-
-        if (o.ignoreCase) {
-            entry = entry.toLowerCase();
-        }
-
-        choices = o.arr.findAll(function(t) {
-            if (i == o.choices) {
-                throw $break;
-            }
-
-            if (o.ignoreCase) {
-                t = t.toLowerCase();
-            }
-            t = t.unescapeHTML();
-
-            var pos = t.indexOf(entry);
-            if (pos != -1 &&
-                ((pos == 0 && t.length != entry_len) ||
-                 (entry_len >= o.partialChars &&
-                  o.partialSearch &&
-                  (o.fullSearch || /\s/.test(t.substr(pos - 1, 1)))))) {
-                ++i;
-                return true;
-            }
-            return false;
-        }, this);
-
-        if (o.score) {
-            choices.each(function(term) {
-                csort.push({ s: LiquidMetal.score(term, entry), t: term });
-            }.bind(this));
-            // Sort the terms
-            csort.sort(function(a, b) { return b.s - a.s; });
-            choices = csort.pluck('t');
-        }
-
-        this.updateChoices(choices);
-    },
-
-    _setOptions: function(opts)
-    {
-        return Object.extend({
-            choices: 10,
-            fullSearch: false,
-            ignoreCase: true,
-            partialChars: 2,
-            partialSearch: true,
-            score: false
-        }, opts || {});
-    }
-
-});
diff --git a/horde/js/calendar.js b/horde/js/calendar.js
deleted file mode 100644 (file)
index d104a7d..0000000
+++ /dev/null
@@ -1,332 +0,0 @@
-/**
- * Horde javascript calendar widget.
- *
- * Custom Events:
- * --------------
- * Horde_Calendar:select
- *   params: Date object
- *   Fired when a date is selected.
- *
- * Horde_Calendar:selectMonth
- *   params: Date object
- *   Fired when a month is selected.
- *
- * Horde_Calendar:selectWeek
- *   params: Date object
- *   Fired when a week is selected.
- *
- * Horde_Calendar:selectYear
- *   params: Date object
- *   Fired when a year is selected.
- */
-
-var Horde_Calendar =
-{
-    // Variables set externally: click_month, click_week, click_year,
-    //                           firstDayOfWeek, fullweekdays, months,
-    //                           weekdays
-    // Variables defaulting to null: date, month, openDate, trigger, year
-
-    open: function(trigger, data)
-    {
-        var date = data ? data : new Date();
-
-        this.openDate = date.getTime();
-        this.trigger = $(trigger);
-        this.draw(this.openDate, true);
-    },
-
-    /**
-     * Days in the month (month is a zero-indexed javascript month).
-     */
-    daysInMonth: function(month, year)
-    {
-        switch (month) {
-        case 3:
-        case 5:
-        case 8:
-        case 10:
-            return 30;
-
-        case 1:
-            return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
-                ? 29
-                : 28;
-
-        default:
-            return 31;
-        }
-    },
-
-    weeksInMonth: function(month, year)
-    {
-        var firstWeekDays, weeks,
-            firstOfMonth = (new Date(year, month, 1)).getDay();
-
-        if ((this.firstDayOfWeek == 1 && firstOfMonth == 0) ||
-            (this.firstDayOfWeek == 0 && firstOfMonth == 6)) {
-            firstWeekDays = 7 - firstOfMonth + this.firstDayOfWeek;
-            weeks = 1;
-        } else {
-            firstWeekDays = this.firstDayOfWeek - firstOfMonth;
-            weeks = 0;
-        }
-
-        firstWeekDays %= 7;
-
-        return Math.ceil((this.daysInMonth(month, year) - firstWeekDays) / 7) + weeks;
-    },
-
-    // http://javascript.about.com/library/blstdweek.htm
-    weekOfYear: function(d)
-    {
-        var newYear = new Date(d.getFullYear(), 0, 1),
-            day = newYear.getDay();
-        if (this.firstDayOfWeek != 0) {
-            day = ((day + (7 - this.firstDayOfWeek)) % 7);
-        }
-        return Math.ceil((((d - newYear) / 86400000) + day + 1) / 7);
-    },
-
-    draw: function(timestamp, init)
-    {
-        this.date = new Date(timestamp);
-        this.month = this.date.getMonth();
-        this.year = this.date.getFullYear();
-
-        var cell, i, i_max, p, row, startOfView, vp,
-            count = 1,
-            div = $('hordeCalendar'),
-            tbody = div.down('TBODY'),
-
-            // Requires init above
-            daysInMonth = this.daysInMonth(this.month, this.year),
-            daysInView = this.weeksInMonth(this.month, this.year) * 7,
-            firstOfMonth = (new Date(this.year, this.month, 1)).getDay(),
-
-            // Cache today and open date.
-            today = new Date(),
-            today_year = today.getFullYear(),
-            today_month = today.getMonth(),
-            today_day = today.getDate(),
-            open = new Date(this.openDate),
-            open_year = open.getFullYear(),
-            open_month = open.getMonth(),
-            open_day = open.getDate();
-
-        if (this.firstDayOfWeek == 0) {
-            startOfView = 1 - firstOfMonth;
-        } else {
-            // @TODO Adjust this for days other than Monday.
-            startOfView = (firstOfMonth == 0)
-                ? -5
-                : 2 - firstOfMonth;
-        }
-
-        div.down('.hordeCalendarYear').update(this.year);
-        div.down('.hordeCalendarMonth').update(this.months[this.month]);
-        tbody.update('');
-
-        for (i = startOfView, i_max = startOfView + daysInView; i < i_max; ++i) {
-            if (count == 1) {
-                row = new Element('TR');
-                if (this.click_week) {
-                    row.insert(new Element('TD').insert(new Element('A', { className: 'hordeCalendarWeek' }).insert(this.weekOfYear(new Date(this.year, this.month, (i < 1) ? 1 : i)))));
-                }
-            }
-
-            cell = new Element('TD');
-
-            if (i < 1 || i > daysInMonth) {
-                cell.addClassName('hordeCalendarEmpty');
-                row.insert(cell);
-            } else {
-                if (today_year == this.year &&
-                    today_month == this.month &&
-                    today_day == i) {
-                    cell.writeAttribute({ className: 'hordeCalendarToday' });
-                }
-
-                if (open_year == this.year &&
-                    open_month == this.month &&
-                    open_day == i) {
-                    cell.addClassName('hordeCalendarCurrent');
-                }
-
-                row.insert(cell.insert(new Element('A', { className: 'hordeCalendarDay', href: '#' }).insert(i)));
-            }
-
-            if (count == 7) {
-                tbody.insert(row);
-                count = 0;
-            }
-            ++count;
-        }
-
-        if (count > 1) {
-            tbody.insert(row);
-        }
-
-        div.show();
-
-        // Position the popup every time in case of a different input,
-        // window sizing changes, etc.
-        if (init) {
-            p = this.trigger.cumulativeOffset();
-            vp = document.viewport.getDimensions();
-
-            if (p.left + div.offsetWidth > vp.width) {
-                div.setStyle({ left: (vp.width - 10 - div.offsetWidth) + 'px' });
-            } else {
-                div.setStyle({ left: p.left + 'px' });
-            }
-
-            if (p.top + div.offsetHeight > vp.height) {
-                div.setStyle({ top: (vp.height - 10 - div.offsetHeight) + 'px' });
-            } else {
-                div.setStyle({ top: p.top + 'px' });
-            }
-        }
-
-        // IE 6 only.
-        if (Prototype.Browser.IE && !window.XMLHttpRequest) {
-            iframe = $('hordeCalendarIframe');
-            if (!iframe) {
-                iframe = new Element('IFRAME', { name: 'hordeCalendarIframe', id: 'hordeCalendarIframe', src: 'javascript:false;', scrolling: 'no', frameborder: 0 }).hide();
-                $(document.body).insert(iframe);
-            }
-            iframe.clonePosition(div).setStyle({
-                position: 'absolute',
-                display: 'block',
-                zIndex: 1
-            });
-        }
-
-        div.setStyle({ zIndex: 999 });
-    },
-
-    hideCal: function()
-    {
-        var iefix = $('hordeCalendarIframe');
-
-        $('hordeCalendar').hide();
-
-        if (iefix) {
-            iefix.hide();
-        }
-    },
-
-    changeYear: function(by)
-    {
-        this.draw((new Date(this.date.getFullYear() + by, this.date.getMonth(), 1)).getTime());
-    },
-
-    changeMonth: function(by)
-    {
-        var newMonth = this.date.getMonth() + by,
-            newYear = this.date.getFullYear();
-
-        if (newMonth == -1) {
-            newMonth = 11;
-            newYear -= 1;
-        }
-
-        this.draw((new Date(newYear, newMonth, 1)).getTime());
-    },
-
-    init: function()
-    {
-        var i, link, row,
-            offset = this.click_week ? 1 : 0,
-            thead = new Element('THEAD'),
-            table = new Element('TABLE', { className: 'hordeCalendarPopup', cellSpacing: 0 }).insert(thead).insert(new Element('TBODY'));
-
-        // Title bar.
-        link = new Element('A', { href: '#', className: 'hordeCalendarClose rightAlign' }).insert('x');
-        thead.insert(new Element('TR').insert(new Element('TD', { colspan: 6 + offset })).insert(new Element('TD').insert(link)));
-
-        // Year.
-        row = new Element('TR');
-        link = new Element('A', { className: 'hordeCalendarPrevYear', href: '#' }).insert('&laquo;');
-        row.insert(new Element('TD').insert(link));
-
-        tmp = new Element('TD', { align: 'center', colspan: 5 + offset });
-        if (this.click_year) {
-            tmp.insert(new Element('A', { className: 'hordeCalendarYear' }));
-        } else {
-            tmp.addClassName('hordeCalendarYear');
-        }
-        row.insert(tmp);
-
-        link = new Element('A', { className: 'hordeCalendarNextYear', href: '#' }).insert('&raquo;');
-        row.insert(new Element('TD', { className: 'rightAlign' }).insert(link));
-
-        thead.insert(row);
-
-        // Month name.
-        row = new Element('TR');
-        link = new Element('A', { className: 'hordeCalendarPrevMonth', href: '#' }).insert('&laquo;');
-        row.insert(new Element('TD').insert(link));
-
-        tmp = new Element('TD', { align: 'center', colspan: 5 + offset });
-        if (this.click_year) {
-            tmp.insert(new Element('A', { className: 'hordeCalendarMonth' }));
-        } else {
-            tmp.addClassName('hordeCalendarMonth');
-        }
-        row.insert(tmp);
-
-        link = new Element('A', { className: 'hordeCalendarNextMonth', href: '#' }).insert('&raquo;');
-        row.insert(new Element('TD', { className: 'rightAlign' }).insert(link));
-
-        thead.insert(row);
-
-        // Weekdays.
-        row = new Element('TR');
-        if (this.click_week) {
-            row.insert(new Element('TH'));
-        }
-        for (i = 0; i < 7; ++i) {
-            row.insert(new Element('TH').insert(this.weekdays[(i + this.firstDayOfWeek) % 7]));
-        }
-        thead.insert(row);
-
-        $(document.body).insert({ bottom: new Element('DIV', { id: 'hordeCalendar' }).setStyle({ position: 'absolute', 'z-index': 999 }).hide().insert(table) });
-
-        $('hordeCalendar').observe('click', this.clickHandler.bindAsEventListener(this));
-    },
-
-    clickHandler: function(e)
-    {
-        var elt = e.element();
-
-        if (elt.hasClassName('hordeCalendarDay')) {
-            this.hideCal();
-            this.trigger.fire('Horde_Calendar:select', new Date(this.year, this.month, parseInt(e.element().textContent, 10)));
-        } else if (elt.hasClassName('hordeCalendarClose')) {
-            this.hideCal();
-        } else if (elt.hasClassName('hordeCalendarPrevYear')) {
-            this.changeYear(-1);
-        } else if (elt.hasClassName('hordeCalendarNextYear')) {
-            this.changeYear(1);
-        } else if (elt.hasClassName('hordeCalendarPrevMonth')) {
-            this.changeMonth(-1);
-        } else if (elt.hasClassName('hordeCalendarNextMonth')) {
-            this.changeMonth(1);
-        } else if (this.click_year && elt.hasClassName('hordeCalendarYear')) {
-            this.trigger.fire('Horde_Calendar:selectYear', new Date(this.year, this.month, 1));
-            this.hideCal();
-        } else if (this.click_month && elt.hasClassName('hordeCalendarMonth')) {
-            this.trigger.fire('Horde_Calendar:selectMonth', new Date(this.year, this.month, 1));
-            this.hideCal();
-        } else if (this.click_week && elt.hasClassName('hordeCalendarWeek')) {
-            this.trigger.fire('Horde_Calendar:selectWeek', new Date(this.year, this.month, elt.up('TR').down('A.hordeCalendarDay').textContent));
-            this.hideCal();
-        }
-
-        e.stop();
-    }
-
-};
-
-document.observe('dom:loaded', Horde_Calendar.init.bind(Horde_Calendar));
diff --git a/horde/js/hordetree.js b/horde/js/hordetree.js
deleted file mode 100644 (file)
index 3659e62..0000000
+++ /dev/null
@@ -1,511 +0,0 @@
-/**
- * Provides the javascript class to create dynamic trees.
- *
- * Optionally uses the Horde_Tooltip class (tooltips.js).
- *
- * Custom Events
- * -------------
- * The 'memo' property of the Event object contains the original event object.
- *
- * 'Horde_Tree:expand'
- *   Fired when a tree element is expanded.
- *
- * 'Horde_Tree:collapse'
- *   Fired when a tree element is collapsed.
- *
- * Copyright 2003-2010 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.
- *
- * @author   Marko Djukic <marko@oblo.com>
- * @author   Michael Slusarz <slusarz@horde.org>
- * @category Horde
- */
-
-var Horde_Tree = Class.create({
-
-    initialize: function(opts)
-    {
-        this.opts = opts;
-
-        if (this.opts.initTree) {
-            this.renderTree(this.opts.initTree.nodes, this.opts.initTree.root_nodes, this.opts.initTree.is_static);
-            this.opts.initTree = null;
-        }
-
-        $(this.opts.target).observe('click', this._onClick.bindAsEventListener(this));
-
-        this.opts.ie6 = (navigator.userAgent.toLowerCase().substr(25,6)=="msie 6");
-    },
-
-    renderTree: function(nodes, rootNodes, renderStatic)
-    {
-        this.nodes = nodes;
-        this.rootNodes = rootNodes;
-        this.renderStatic = renderStatic;
-        this.dropline = [];
-        this.output = document.createDocumentFragment();
-
-        this._buildHeader();
-
-        this.rootNodes.each(function(r) {
-            this.buildTree(r, this.output);
-        }, this);
-
-        $(this.opts.target).update('');
-        $(this.opts.target).appendChild(this.output);
-
-        if (this.opts.ie6) {
-            /* Correct for frame scrollbar in IE6 by determining if a scrollbar
-             * is present, and if not readjusting the marginRight property to
-             * 0. See http://www.xs4all.nl/~ppk/js/doctypes.html for why this
-             * works */
-            if (document.documentElement.clientHeight == document.documentElement.offsetHeight) {
-                // no scrollbar present, take away extra margin
-                $(document.body).setStyle({ marginRight: 0 });
-            } else {
-                $(document.body).setStyle({ marginRight: '15px' });
-            }
-        }
-
-        // If using alternating row shading, work out correct shade.
-        if (this.opts.options.alternate) {
-            this.stripe();
-        }
-
-        if (window.Horde_Tooltips) {
-            window.Horde_ToolTips.attachBehavior();
-        }
-    },
-
-    _buildHeader: function()
-    {
-        if (this.opts.options.hideHeaders ||
-            !this.opts.header.size()) {
-            return;
-        }
-
-        var div = new Element('DIV');
-
-        this.opts.header.each(function(h) {
-            var tmp = new Element('SPAN').insert(h.html ? h.html : '&nbsp;');
-
-            if (h['class']) {
-                tmp.addClassName(h['class']);
-            }
-
-            div.appendChild(tmp);
-        }, this);
-
-        this.output.appendChild(div);
-    },
-
-    // Recursive function to walk through the tree array and build
-    // the output.
-    buildTree: function(nodeId, p)
-    {
-        var last_subnode, tmp,
-            node = this.nodes[nodeId];
-
-        this.buildLine(nodeId, p);
-
-        if (!Object.isUndefined(node.children)) {
-            last_subnode = node.children.last();
-            tmp = new Element('DIV', { id: 'nodeChildren_' + nodeId });
-            [ tmp ].invoke(node.expanded ? 'show' : 'hide');
-
-            node.children.each(function(c) {
-                if (c == last_subnode) {
-                    this.nodes[c].node_last = true;
-                }
-                this.buildTree(c, tmp);
-            }, this);
-
-            p.appendChild(tmp);
-        }
-    },
-
-    buildLine: function(nodeId, p)
-    {
-        var div, label, tmp,
-            column = 0,
-            node = this.nodes[nodeId];
-
-        div = new Element('DIV', { className: 'treeRow' });
-        if (node['class']) {
-            div.addClassName(node['class']);
-        }
-
-        // If we have headers, track which logical "column" we're in for
-        // any given cell of content.
-        if (node.extra && node.extra[0]) {
-            node.extra[0].each(function(n) {
-                div.insert(this._divClass(new Element('SPAN').update(n), column++));
-            }, this);
-        }
-
-        for (; column < this.opts.extraColsLeft; ++column) {
-            div.insert(this._divClass(new Element('SPAN').update('&nbsp;'), column));
-        }
-
-        div.insert(this._divClass(new Element('SPAN'), column));
-
-        tmp = document.createDocumentFragment();
-        for (i = Number(this.renderStatic); i < node.indent; ++i) {
-            tmp.appendChild(new Element('SPAN').addClassName('treeImg').addClassName(
-                'treeImg' + ((this.dropline[i] && this.opts.options.lines)
-                    ? this.opts.imgLine
-                    : this.opts.imgBlank)
-            ));
-        }
-
-        tmp.appendChild(this._setNodeToggle(nodeId));
-
-        if (node.url) {
-            label = new Element('A', { href: node.url }).insert(
-                this._setNodeIcon(nodeId)
-            ).insert(
-                node.label
-            );
-
-            if (node.urlclass) {
-                label.addClassName(node.urlclass);
-            } else if (this.opts.options.urlclass) {
-                label.addClassName(this.opts.options.urlclass);
-            }
-
-            if (node.title) {
-                label.writeAttribute('title', node.title);
-            }
-
-            if (node.target) {
-                label.writeAttribute('target', node.target);
-            } else if (this.opts.options.target) {
-                label.writeAttribute('target', this.opts.options.target);
-            }
-
-            //if (node.onclick) {
-            //    label.push(' onclick="' + node.onclick + '"');
-            //}
-
-            label = label.wrap('SPAN');
-        } else {
-            label = new Element('SPAN').addClassName('toggle').insert(
-                this._setNodeIcon(nodeId)
-            ).insert(
-                node.label
-            );
-        }
-
-        if (this.opts.options.multiline) {
-            div.insert(new Element('TABLE').insert(
-                new Element('TR').insert(
-                    new Element('TD').appendChild(tmp)
-                ).insert(
-                    new Element('TD').insert(label)
-                )
-            ));
-        } else {
-            div.appendChild(tmp);
-            div.insert(label)
-        }
-
-        ++column;
-
-        if (node.extra && node.extra[1]) {
-            node.extra[1].each(function(n) {
-                div.insert(this._divClass(new Element('SPAN').update(n), column++));
-            }, this);
-        }
-
-        for (; column < this.opts.extraColsRight; ++column) {
-            div.insert(this._divClass(new Element('SPAN').update('&nbsp;'), column));
-        }
-
-        p.appendChild(div);
-    },
-
-    _divClass: function(div, c)
-    {
-        if (this.opts.header[c] && this.opts.header[c]['class']) {
-            div.addClassName(this.opts.header[c]['class']);
-        }
-
-        return div;
-    },
-
-    _setNodeToggle: function(nodeId)
-    {
-        var node = this.nodes[nodeId];
-
-        if (node.indent == '0') {
-            // Top level with children.
-            this.dropline[0] = false;
-
-            if (this.renderStatic) {
-                return '';
-            }
-
-            switch (this._getLineType(nodeId)) {
-            case 1:
-            case 2:
-                this.dropline[0] = true;
-                break;
-            }
-        } else {
-            this.dropline[node.indent] = !node.node_last;
-        }
-
-        return new Element('SPAN', { id: "nodeToggle_" + nodeId }).addClassName('treeToggle').addClassName('treeImg').addClassName('treeImg' + this._getNodeToggle(nodeId));
-    },
-
-    _getNodeToggle: function(nodeId)
-    {
-        var type,
-            node = this.nodes[nodeId];
-
-        if (node.indent == '0') {
-            if (this.renderStatic) {
-                return '';
-            }
-
-            if (!this.opts.options.lines) {
-                return this.opts.imgBlank;
-            }
-
-            type = this._getLineType(nodeId);
-
-            if (node.children) {
-                // Top level with children.
-                if (node.expanded) {
-                    return type
-                        ? ((type == 2) ? this.opts.imgMinus : this.opts.imgMinusBottom)
-                        : this.opts.imgMinusOnly;
-                }
-
-                return type
-                    ? ((type == 2) ? this.opts.imgPlus : this.opts.imgPlusBottom)
-                    : this.opts.imgPlusOnly;
-            }
-
-            switch (type) {
-            case 0:
-                return this.opts.imgNullOnly;
-
-            case 1:
-                return this.opts.imgJoinTop;
-
-            case 2:
-                return this.opts.imgJoin;
-
-            case 3:
-                return this.opts.imgJoinBottom;
-            }
-        }
-
-        if (node.children) {
-            // Node with children.
-            if (!node.node_last) {
-                // Not last node.
-                if (!this.opts.options.lines) {
-                    return this.opts.imgBlank;
-                } else if (this.renderStatic) {
-                    return this.opts.imgJoin;
-                } else if (node.expanded) {
-                    return this.opts.imgMinus;
-                }
-
-                return this.opts.imgPlus;
-            }
-
-            // Last node.
-            if (!this.opts.options.lines) {
-                return this.opts.imgBlank;
-            } else if (this.renderStatic) {
-                return this.opts.imgJoinBottom;
-            } else if (node.expanded) {
-                return this.opts.imgMinusBottom;
-            }
-
-            return this.opts.imgPlusBottom;
-        }
-
-        // Node no children.
-        if (!node.node_last) {
-            // Not last node.
-            return this.opts.options.lines
-                ? this.opts.imgJoin
-                : this.opts.imgBlank;
-        }
-
-        // Last node.
-        return this.opts.options.lines
-            ? this.opts.imgJoinBottom
-            : this.opts.imgBlank;
-    },
-
-    _getLineType: function(nodeId)
-    {
-        if (this.opts.options.lines_base &&
-            this.rootNodes.size() > 1) {
-            switch (this.rootNodes.indexOf(nodeId)) {
-            case 0:
-                return 1;
-
-            case (this.rootNodes.size() - 1):
-                return 3;
-
-            default:
-                return 2;
-            }
-        }
-
-        return 0;
-    },
-
-    _setNodeIcon: function(nodeId)
-    {
-        var img,
-            node = this.nodes[nodeId];
-
-        // Image.
-        if (node.icon) {
-            // Node has a user defined icon.
-            img = new Element('IMG', { id: "nodeIcon_" + nodeId, src: (node.iconopen && node.expanded ? node.iconopen : node.icon) }).addClassName('treeIcon')
-        } else {
-            img = new Element('SPAN', { id: "nodeIcon_" + nodeId }).addClassName('treeIcon');
-            if (node.children) {
-                // Standard icon: node with children.
-                img.addClassName('treeImg' + (node.expanded ? this.opts.imgFolderOpen : this.opts.imgFolder));
-            } else {
-                // Standard icon: node, no children.
-                img.addClassName('treeImg' + this.opts.imgLeaf);
-            }
-        }
-
-        if (node.iconalt) {
-            img.writeAttribute('alt', node.iconalt);
-        }
-
-        return img;
-    },
-
-    toggle: function(nodeId)
-    {
-        var icon, nodeToggle, toggle, children,
-            node = this.nodes[nodeId];
-
-        node.expanded = !node.expanded;
-        if (children = $('nodeChildren_' + nodeId)) {
-            children.setStyle({ display: node.expanded ? 'block' : 'none' });
-        }
-
-        // Toggle the node's icon if it has separate open and closed
-        // icons.
-        if (icon = $('nodeIcon_' + nodeId)) {
-            // Image.
-            if (node.icon) {
-                icon.writeAttribute('src', (node.expanded && node.iconopen) ? node.iconopen : node.icon);
-            } else {
-                // Use standard icon set.
-                icon.writeAttribute('src', node.expanded ? this.opts.imgFolderOpen : this.opts.imgFolder);
-            }
-        }
-
-        // If using alternating row shading, work out correct shade.
-        if (this.opts.options.alternate) {
-            this.stripe();
-        }
-
-        if (toggle = $('nodeToggle_' + nodeId)) {
-            toggle.writeAttribute('class', 'treeToggle treeImg').addClassName('treeImg' + this._getNodeToggle(nodeId));
-        }
-
-        $(this.opts.target).fire(node.expanded ? 'Horde_Tree:expand' : 'Horde_Tree:collapse', nodeId);
-
-        this.saveState(nodeId, node.expanded)
-    },
-
-    stripe: function()
-    {
-        var classes = [ 'rowEven', 'rowOdd' ],
-            i = 0;
-
-        $(this.opts.target).select('DIV.treeRow').each(function(r) {
-            classes.each(r.removeClassName.bind(r));
-            if (r.clientHeight) {
-                r.addClassName(classes[++i % 2]);
-            }
-        });
-    },
-
-    saveState: function(nodeId, expanded)
-    {
-        if (this.opts.nocookie) {
-            return;
-        }
-
-        var newCookie = '',
-            newNodes = [],
-            oldCookie = this._getCookie(this.opts.target + '_expanded');
-
-        if (expanded) {
-            // Expand requested so add to cookie.
-            newCookie = (oldCookie ? oldCookie + ',' : '') + nodeId;
-        } else {
-            // Collapse requested so remove from cookie.
-            oldCookie.split(',').each(function(n) {
-                if (n != nodeId) {
-                    newNodes[newNodes.length] = n
-                }
-            });
-            newCookie = newNodes.join(',');
-        }
-
-        document.cookie = this.opts.target + '_expanded=exp' + escape(newCookie) + ';DOMAIN=' + this.opts.cookieDomain + ';PATH=' + this.opts.cookiePath + ';';
-    },
-
-    _getCookie: function(name)
-    {
-        var end,
-            dc = document.cookie,
-            prefix = name + '=exp',
-            begin = dc.indexOf('; ' + prefix);
-
-        if (begin == -1) {
-            begin = dc.indexOf(prefix);
-            if (begin != 0) {
-                return '';
-            }
-        } else {
-            begin += 2;
-        }
-
-        end = document.cookie.indexOf(';', begin);
-        if (end == -1) {
-            end = dc.length;
-        }
-
-        return unescape(dc.substring(begin + prefix.length, end));
-    },
-
-    _onClick: function(e)
-    {
-        var elt = e.element(),
-            id = elt.readAttribute('id');
-
-        if (elt.hasClassName('treeIcon')) {
-            elt = elt.up().previous();
-        } else if (elt.hasClassName('toggle')) {
-            elt = elt.previous();
-        }
-
-        id = elt.readAttribute('id');
-        if (id && id.startsWith('nodeToggle_')) {
-            this.toggle(id.substr(11));
-            e.stop();
-        }
-    }
-
-});
diff --git a/horde/js/identityselect.js b/horde/js/identityselect.js
deleted file mode 100644 (file)
index 19685cc..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * Horde identity selection javascript.
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- */
-
-var HordeIdentitySelect = {
-
-    newChoice: function()
-    {
-        var identity = $('identity'),
-            id = Number($F(identity));
-
-        if (id < 0) {
-            identity.up('FORM').reset();
-            identity.setValue(id);
-            return;
-        }
-
-        this.identities[id].each(function(a) {
-            var field = $(a[0]);
-
-            switch (a[1]) {
-            case "special":
-                identity.fire('HordeIdentitySelect:change', {
-                    i: id,
-                    pref: a[0]
-                });
-                break;
-
-            default:
-                field.setValue(a[2]);
-                break;
-            }
-        });
-    },
-
-    onDomLoad: function()
-    {
-        $('identity').observe('change', this.newChoice.bind(this));
-    }
-
-};
-
-document.observe('dom:loaded', HordeIdentitySelect.onDomLoad.bind(HordeIdentitySelect));
diff --git a/horde/js/liquidmetal.js b/horde/js/liquidmetal.js
deleted file mode 100644 (file)
index 74cde8d..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * LiquidMetal, version: 0.1 (2009-02-05)
- *
- * A mimetic poly-alloy of Quicksilver's scoring algorithm, essentially
- * LiquidMetal.
- *
- * For usage and examples, visit:
- * http://github.com/rmm5t/liquidmetal
- *
- * Licensed under the MIT:
- * http://www.opensource.org/licenses/mit-license.php
- *
- * Copyright (c) 2009, Ryan McGeary (ryanonjavascript -[at]- mcgeary [*dot*] org)
- * Modified by the Horde Project to use compressibility.
- */
-var LiquidMetal = {
-
-    SCORE_NO_MATCH: 0.0,
-    SCORE_MATCH: 1.0,
-    SCORE_TRAILING: 0.8,
-    SCORE_TRAILING_BUT_STARTED: 0.9,
-    SCORE_BUFFER: 0.85,
-
-    score: function(str, abbr)
-    {
-        // Short circuits
-        if (abbr.length == 0) {
-           return this.SCORE_TRAILING;
-        }
-        if (abbr.length > str.length) {
-            return this.SCORE_NO_MATCH;
-        }
-
-        var scores = this.buildScoreArray(str, abbr),
-            sum = 0.0;
-
-        scores.each(function(i) {
-            sum += i;
-        });
-
-        return (sum / scores.size());
-    },
-
-    buildScoreArray: function(str, abbr)
-    {
-        var lastIndex = -1,
-            lower = str.toLowerCase(),
-            scores = new Array(str.length),
-            started = false;
-
-        abbr.toLowerCase().split("").each(function(c) {
-            var index = to = lower.indexOf(c, lastIndex + 1),
-                val = this.SCORE_BUFFER;
-
-            if (index < 0) {
-                return this.fillArray(scores, this.SCORE_NO_MATCH);
-            }
-
-            if (index == 0) {
-                started = true;
-            }
-
-            if (this.isNewWord(str, index)) {
-                scores[index - 1] = 1;
-                to = index - 1;
-            } else if (!this.isUpperCase(str, index)) {
-                val = this.SCORE_NO_MATCH;
-            }
-
-            this.fillArray(scores, val, lastIndex + 1, val);
-
-            scores[index] = this.SCORE_MATCH;
-            lastIndex = index;
-        }.bind(this));
-
-        this.fillArray(scores, started ? this.SCORE_TRAILING_BUT_STARTED : this.SCORE_TRAILING, lastIndex + 1);
-
-        return scores;
-    },
-
-    isUpperCase: function(str, index)
-    {
-        var c = str.charAt(index);
-        return ("A" <= c && c <= "Z");
-    },
-
-    isNewWord: function(str, index)
-    {
-        var c = str.charAt(index - 1);
-        return (c == " " || c == "\t");
-    },
-
-    fillArray: function(arr, value, from, to)
-    {
-        from = Math.max(from || 0, 0);
-        to = Math.min(to || arr.length, arr.length);
-        for (var i = from; i < to; ++i) {
-            arr[i] = value;
-        }
-        return arr;
-    }
-};
diff --git a/horde/js/open_html_helper.js b/horde/js/open_html_helper.js
deleted file mode 100644 (file)
index c483581..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
- * Horde Html Helper Javascript Class
- *
- * Provides the javascript class insert html tags by clicking on icons.
- *
- * The helpers available:
- *      emoticons - for inserting emoticons strings
- *
- * Copyright 2003-2010 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.
- *
- * @author Marko Djukic <marko@oblo.com>
- * @package Horde
- * @todo Add handling for font tags, tables, etc.
- */
-
-var Horde_Html_Helper = {
-
-    iconlist: [],
-    targetElement: null,
-
-    open: function(type, target)
-    {
-        var cell, row, table, tbody,
-            lay = $('htmlhelper_' + target);
-        this.targetElement = $(target);
-
-        if (lay.getStyle('display') == 'block') {
-            lay.hide();
-            return false;
-        }
-
-        if (lay.firstChild) {
-            lay.removeChild(lay.firstChild);
-        }
-
-        tbody = new Element('TBODY');
-        table = new Element('TABLE', { border: 0, cellSpacing: 0 }).insert(tbody);
-
-        if (type == 'emoticons') {
-            row = new Element('TR');
-            cell = new Element('TD');
-
-            iconlist.each(function(i) {
-                var link =
-                    new Element('A', { href: '#' }).insert(
-                        new Element('IMG', { align: 'middle', border: 0, src: i[0] })
-                    );
-                cell.appendChild(link);
-
-                link.observe('click', function(e) {
-                    this.targetElement.setValue($F(this.targetElement) + i[1] + ' ');
-                    e.stop();
-                }.bindAsEventListener(this));
-            });
-
-            row.insert(cell);
-            tbody.insert(row);
-            table.insert(tbody);
-        }
-
-        lay.insert(table).setStyle({ display: 'block' });
-    }
-};
diff --git a/horde/js/prefs.js b/horde/js/prefs.js
deleted file mode 100644 (file)
index fc60897..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-/**
- * Provides the javascript for the prefs page.
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- */
-
-document.observe('dom:loaded', function() {
-    $('appsubmit').hide();
-
-    $('app').observe('change', function() {
-        $('appswitch').submit();
-    });
-});
diff --git a/horde/js/prettyautocomplete.js b/horde/js/prettyautocomplete.js
deleted file mode 100644 (file)
index cc1be4f..0000000
+++ /dev/null
@@ -1,280 +0,0 @@
-var PrettyAutocompleter = Class.create({
-
-    initialize: function(element, params)
-    {
-        this.p = Object.extend({
-            // Outer div/fake input box and CSS class
-            box: 'HordeACBox',
-            boxClass: 'hordeACBox',
-            // <ul> CSS class
-            listClass: 'hordeACList',
-            // CSS class for real input field
-            growingInputClass: 'hordeACTrigger',
-            // Dom id for <li> that holds the input field.
-            triggerContainer: 'hordeACTriggerContainer',
-            // Min pixel width of input field
-            minTriggerWidth: 100,
-            // Allow for a function that filters the display value
-            // This function should *always* return escaped HTML
-            displayFilter: function(t) { return t.escapeHTML() },
-            filterCallback: this._filterChoices.bind(this),
-            onAdd: Prototype.K,
-            onRemove: Prototype.K
-        }, params || {});
-
-        // Array to hold the currently selected items to ease with removing
-        // them, assuring no duplicates etc..
-        this.selectedItems = [];
-
-        // The original input element is transformed into the hidden input
-        // field that hold the text values (p.items), while p.trigger is
-        // the borderless input field located in p.box
-        this.p.items = element;
-        this.p.trigger = element + 'real';
-        this.initialized = false;
-        this._enabled = true;
-    },
-
-    /**
-     * Initializes the autocompleter, builds the dom structure, registers
-     * events, etc...
-     */
-    init: function()
-    {
-        if (this.initialized) {
-            return;
-        }
-
-        // Build the DOM structure
-        this.buildStructure();
-
-        // Remember the bound method to unregister later.
-        this._boundProcessValue = this._processValue.bind(this);
-        var trigger = $(this.p.trigger);
-        trigger.observe('keydown', this._onKeyDown.bindAsEventListener(this));
-        trigger.observe('blur', this._boundProcessValue);
-
-        // Make sure the p.items element is hidden
-        if (!this.p.debug) {
-            $(this.p.items).hide();
-        }
-
-        // Set the updateElement callback
-        this.p.onSelect = this._updateElement.bind(this);
-
-        // Look for clicks on the box to simulate clicking in an input box
-        $(this.p.box).observe('click', function() { trigger.focus() });
-        trigger.observe('blur', this._resize.bind(this));
-        trigger.observe('keydown', this._resize.bind(this));
-        trigger.observe('keypress', this._resize.bind(this));
-        trigger.observe('keyup', this._resize.bind(this));
-
-        // Create the underlaying Autocompleter
-        this.p.uri += '/input=' + this.p.trigger;
-
-        this.p.onShow = this._knvShow.bind(this);
-        this.p.onHide = this._knvHide.bind(this);
-
-        // Make sure the knl is contained in the overlay
-        this.p.domParent = this.p.box;
-        new Ajax.Autocompleter(this.p.trigger, this.p.uri, this.p);
-
-        // Prepopulate the items and the container elements?
-        if (typeof this.p.existing != 'undefined') {
-            this.init(this.p.existing);
-        }
-
-        this.initialized = true;
-    },
-
-    /**
-     * Resets the autocompleter's state.
-     */
-    reset: function(existing)
-    {
-        if (!this.initialized) {
-            this.init();
-        }
-
-        // TODO: Resize the trigger field to fill the current line?
-        // Clear any existing values
-        if (this.selectedItems.length) {
-            $(this.p.box).select('li.' + this.p.listClass + 'Item').each(function(item) {
-                this.removeItemNode(item);
-            }.bind(this));
-        }
-
-        // Clear the hidden items field
-        $(this.p.items).value = '';
-
-        // Add any initial values
-        if (typeof existing != 'undefined' && existing.length) {
-            for (var i = 0, l = existing.length; i < l; i++) {
-                this.addNewItemNode(existing[i]);
-            }
-        }
-        this._enabled = true;
-    },
-
-    buildStructure: function()
-    {
-        // Build the outter box
-        var box = new Element('div', { id: this.p.box, className: this.p.boxClass }).setStyle({ position: 'relative' });
-
-        // The list - where the choosen items are placed as <li> nodes
-        var list = new Element('ul', { className: this.p.listClass });
-
-        // The input element and the <li> wraper
-        var inputListItem = new Element('li', {
-                className: this.p.listClass + 'Member',
-                id: this.p.triggerContainer }),
-            growingInput = new Element('input', {
-                className: this.p.growingInputClass,
-                id: this.p.trigger,
-                name: this.p.trigger,
-                autocomplete: 'off' });
-
-        // Create a hidden span node to help calculate the needed size
-        // of the input field.
-        this.sizer = new Element('span').setStyle({ float: 'left', display: 'inline-block', position: 'absolute', left: '-1000px' });
-
-        inputListItem.update(growingInput);
-        list.update(inputListItem);
-        box.update(list);
-        box.insert(this.sizer);
-
-        // 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)
-        box.insert(Element.replace($(this.p.items), box));
-    },
-
-    shutdown: function()
-    {
-        this._processValue();
-    },
-
-    _onKeyDown: function(e)
-    {
-        // Check for a comma
-        if (e.keyCode == 188) {
-            this._processValue();
-            e.stop();
-        }
-    },
-
-    _processValue: function()
-    {
-        var value = $F(this.p.trigger).replace(/^,/, '').strip();
-        if (value.length) {
-            this.addNewItemNode(value);
-            this.p.onAdd(value);
-        }
-    },
-
-    _resize: function()
-    {
-        this.sizer.update($(this.p.trigger).value);
-        newSize = Math.min(this.sizer.getWidth(), $(this.p.box).getWidth());
-        newSize = Math.max(newSize, this.p.minTriggerWidth);
-        $(this.p.trigger).setStyle({ width: newSize + 'px' });
-    },
-
-    // Used as the updateElement callback.
-    _updateElement: function(item)
-    {
-        this.addNewItemNode(item);
-        this.p.onAdd(item);
-    },
-
-    addNewItemNode: function(value)
-    {
-        // Don't add if it's already present.
-        for (var x = 0, len = this.selectedItems.length; x < len; x++) {
-            if (this.selectedItems[x].rawValue == value) {
-                $(this.p.trigger).value = '';
-                return;
-            }
-        }
-
-        var displayValue = this.p.displayFilter(value),
-            newItem = new Element('li', { className: this.p.listClass + 'Member ' + this.p.listClass + 'Item' }).update(displayValue),
-            x = new Element('img', { className: 'hordeACItemRemove', src: this.p.deleteIcon });
-        x.observe('click', this._removeItemHandler.bindAsEventListener(this));
-        newItem.insert(x);
-        $(this.p.triggerContainer).insert({ before: newItem });
-        $(this.p.trigger).value = '';
-
-        // Add to hidden input field.
-        if ($(this.p.items).value) {
-            $(this.p.items).value = $(this.p.items).value + ', ' + value;
-        } else {
-            $(this.p.items).value = value;
-        }
-
-        // ...and keep the selectedItems array up to date.
-        this.selectedItems.push({ rawValue: value, displayValue: displayValue });
-    },
-
-    removeItemNode: function(item)
-    {
-        var value = item.collectTextNodesIgnoreClass('informal');
-        for (var x = 0, len = this.selectedItems.length; x < len; x++) {
-            if (this.selectedItems[x].displayValue.unescapeHTML() == value) {
-               this.selectedItems.splice(x, 1);
-               break;
-            }
-        }
-        item.remove();
-        this.p.onRemove(value);
-    },
-
-    disable: function()
-    {
-      if (!this._enabled || !this.initialized) {
-          return;
-      }
-
-      this._enabled = false;
-      $(this.p.box).select('.hordeACItemRemove').invoke('toggle');
-      $(this.p.trigger).disable();
-    },
-
-    enable: function()
-    {
-        if (this._enabled) {
-            return;
-        }
-        this._enabled = true;
-        $(this.p.box).select('.hordeACItemRemove').invoke('toggle');
-        $(this.p.trigger).enable();
-    },
-
-    _removeItemHandler: function(e)
-    {
-        var realValues = [], x, len;
-        this.removeItemNode(e.element().up());
-        for (x = 0, len = this.selectedItems.length; x < len; x++) {
-            realValues.push(this.selectedItems[x].rawValue);
-        }
-        $(this.p.items).value = realValues.join(',');
-    },
-
-    _filterChoices: function(c)
-    {
-        this.selectedItems.each(function(item) {
-            c = c.without(item.rawValue);
-        });
-        return c;
-    },
-
-    _knvShow: function(l)
-    {
-        $(this.p.trigger).stopObserving('blur', this._boundProcessValue);
-    },
-
-    _knvHide: function(l)
-    {
-        $(this.p.trigger).observe('blur', this._boundProcessValue);
-    }
-});
diff --git a/horde/js/sourceselect.js b/horde/js/sourceselect.js
deleted file mode 100644 (file)
index a2b7570..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-/**
- * Provides the javascript for managing the source selection widget.
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- */
-
-var HordeSourceSelectPrefs = {
-
-    // Vars defaulting to null: source_list
-
-    setSourcesHidden: function()
-    {
-        var out = [], ss;
-
-        if (this.source_list) {
-            ss = $F('source_select');
-            if (ss) {
-                this.source_list.each(function(s) {
-                    if (s.source == ss) {
-                        s.selected = $F('selected_sources');
-                    }
-                    out.push([ s.source, s.selected ]);
-                });
-            }
-        } else {
-            $A($('selected_sources').options).slice(1).each(function(s) {
-                out.push(s.value);
-            });
-        }
-
-        $('sources').setValue(Object.toJSON(out));
-    },
-
-    moveAction: function(from, to)
-    {
-        var moved = false;
-
-        $(from).childElements().each(function(c) {
-            if (c.selected) {
-                c.remove();
-                c.selected = false;
-                $(to).insert(c);
-                moved = true;
-            }
-        });
-
-
-        if (moved) {
-            $(to).fire('HordeSourceSelectPrefs:add');
-            $(from).fire('HordeSourceSelectPrefs:remove');
-            this.setSourcesHidden();
-        }
-    },
-
-    moveSource: function(e, mode)
-    {
-        var sa = $('selected_sources'), sel, tmp;
-
-        if (sa.selectedIndex < 1 || sa.length < 3) {
-            return;
-        }
-
-        // Deselect everything but the first selected item
-        sa.childElements().each(function(s) {
-            if (sel) {
-                s.selected = false;
-            } else if (s.selected) {
-                sel = s;
-            }
-        });
-
-        switch (mode) {
-        case 'down':
-            tmp = sel.next();
-            if (tmp) {
-                sel.remove();
-                tmp.insert({ after: sel });
-            }
-            break;
-
-        case 'up':
-            tmp = sel.previous();
-            if (tmp && tmp.value) {
-                sel.remove();
-                tmp.insert({ before: sel });
-            }
-            break;
-        }
-
-        this.setSourcesHidden();
-        e.stop();
-    },
-
-    changeSource: function()
-    {
-        var source,
-            sel = $('selected_sources'),
-            ss = $('source_select'),
-            unsel = $('unselected_sources'),
-            val = $F(ss);
-
-        sel.down().siblings().invoke('remove');
-        unsel.down().siblings().invoke('remove');
-
-        if (val) {
-            source = this.source_list.find(function(s) {
-                return val == s.source;
-            });
-            source.selected.each(function(s) {
-                sel.insert(new Option(s.v, s.l));
-            });
-            source.unselected.each(function(u) {
-                unsel.insert(new Option(s.v, s.l));
-            });
-        }
-    },
-
-    onDomLoad: function()
-    {
-        if (this.source_list) {
-            $('source_select').observe('change', this.changeSource.bind(this));
-        }
-
-        this.setSourcesHidden();
-
-        $('addsource').observe('click', this.moveAction.bind(this, 'unselected_sources', 'selected_sources'));
-        $('removesource').observe('click', this.moveAction.bind(this, 'selected_sources', 'unselected_sources'));
-        $('moveup').observe('click', this.moveSource.bindAsEventListener(this, 'up'));
-        $('movedown').observe('click', this.moveSource.bindAsEventListener(this, 'down'));
-    }
-
-};
-
-document.observe('dom:loaded', HordeSourceSelectPrefs.onDomLoad.bind(HordeSourceSelectPrefs));
diff --git a/horde/js/spellchecker.js b/horde/js/spellchecker.js
deleted file mode 100644 (file)
index 2b8a8c6..0000000
+++ /dev/null
@@ -1,284 +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.
- *
- * Requires: prototype.js (v1.6.1+), KeyNavList.js
- *
- * Copyright 2005-2010 The Horde Project (http://www.horde.org/)
- *
- * Custom Events:
- * --------------
- * Custom events are triggered on the target element.
- *
- * 'SpellChecker:after'
- *    Fired when the spellcheck processing ends.
- *
- * 'SpellChecker:before'
- *    Fired before the spellcheck is performed.
- *
- * 'SpellChecker:noerror'
- *    Fired when no spellcheck errors are found.
- *
- * 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, disabled, htmlAreaParent, lc, locale, reviewDiv,
-    //   statusButton, statusClass, suggestions, target, url
-    options: {},
-    resumeOnDblClick: true,
-    state: 'CheckSpelling',
-
-    // Options:
-    //   bs = (array) Button states
-    //   locales = (array) List of locales. See KeyNavList for structure.
-    //   sc = (string) Status class
-    //   statusButton = (string/element) DOM ID or element of the status
-    //                  button
-    //   target = (string|Element) DOM element containing data
-    //   url = (string) URL of specllchecker handler
-    initialize: function(opts)
-    {
-        var d, lc, tmp, ul;
-
-        this.url = opts.url;
-        this.target = $(opts.target);
-        this.statusButton = $(opts.statusButton);
-        this.buttonStates = opts.bs;
-        this.statusClass = opts.sc || '';
-        this.disabled = false;
-
-        this.options.onComplete = this.onComplete.bind(this);
-
-        document.observe('click', this.onClick.bindAsEventListener(this));
-
-        if (opts.locales) {
-            this.lc = new KeyNavList(this.statusButton, {
-                list: opts.locales,
-                onChoose: this.setLocale.bindAsEventListener(this)
-            });
-
-            this.statusButton.insert({ after: new Element('SPAN', { className: 'spellcheckPopdownImg' }) });
-        }
-
-        this.setStatus('CheckSpelling');
-    },
-
-    setLocale: function(locale)
-    {
-        this.locale = locale;
-    },
-
-    targetValue: function()
-    {
-        return Object.isUndefined(this.target.value)
-            ? this.target.innerHTML
-            : this.target.value;
-    },
-
-    spellCheck: function()
-    {
-        this.target.fire('SpellChecker:before');
-
-        var opts = Object.clone(this.options),
-            p = $H(),
-            url = this.url;
-
-        this.setStatus('Checking');
-
-        p.set(this.target.identify(), this.targetValue());
-        opts.parameters = p.toQueryString();
-
-        if (this.locale) {
-            url += '/locale=' + this.locale;
-        }
-        if (this.htmlAreaParent) {
-            url += '/html=1';
-        }
-
-        new Ajax.Request(url, opts);
-    },
-
-    onComplete: function(request)
-    {
-        var bad, content, washidden,
-            i = 0,
-            result = request.responseJSON;
-
-        if (Object.isUndefined(result)) {
-            this.setStatus('Error');
-            return;
-        }
-
-        this.suggestions = result.suggestions || [];
-
-        if (!this.suggestions.size()) {
-            this.setStatus('CheckSpelling');
-            this.target.fire('SpellChecker:noerror');
-            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 = '<span index="' + (i++) + '" class="spellcheckIncorrect">' + node + '</span>';
-            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: this.target.readAttribute('class') }).addClassName('spellcheck').setStyle({ overflow: 'auto' });
-            if (this.resumeOnDblClick) {
-                this.reviewDiv.observe('dblclick', this.resume.bind(this));
-            }
-        }
-
-        if (!this.target.visible()) {
-            this.target.show();
-            washidden = true;
-        }
-        this.reviewDiv.setStyle({ width: this.target.clientWidth + 'px', height: this.target.clientHeight + 'px'});
-        if (washidden) {
-            this.target.hide();
-        }
-
-        if (!this.htmlAreaParent) {
-            content = content.replace(/~~~/g, '<br />');
-        }
-        this.reviewDiv.update(content);
-
-        if (this.htmlAreaParent) {
-            $(this.htmlAreaParent).insert({ bottom: this.reviewDiv });
-        } else {
-            this.target.hide().insert({ before: this.reviewDiv });
-        }
-
-        this.setStatus('ResumeEdit');
-    },
-
-    onClick: function(e)
-    {
-        var data = [], index, elt = e.element();
-
-        if (this.disabled) {
-            return;
-        }
-
-        if (elt == this.statusButton) {
-            switch (this.state) {
-            case 'CheckSpelling':
-                this.spellCheck();
-                break;
-
-            case 'ResumeEdit':
-                this.resume();
-                break;
-            }
-
-            e.stop();
-        } else if (elt.hasClassName('spellcheckPopdownImg')) {
-            this.lc.show();
-            this.lc.ignoreClick(e);
-            e.stop();
-        } else if (elt.hasClassName('spellcheckIncorrect')) {
-            index = e.element().readAttribute('index');
-
-            $A(this.suggestions[index]).each(function(node) {
-                data.push({ l: node, v: node });
-            });
-
-            if (this.choices) {
-                this.choices.updateBase(elt);
-                this.choices.opts.onChoose = function(val) {elt.update(val).writeAttribute({ className: 'spellcheckCorrected' });};
-            } else {
-                this.choices = new KeyNavList(elt, {
-                    esc: true,
-                    onChoose: function(val) {
-                        elt.update(val).writeAttribute({ className: 'spellcheckCorrected' });
-                    }
-                });
-            }
-
-            this.choices.show(data);
-            this.choices.ignoreClick(e);
-            e.stop();
-        }
-    },
-
-    resume: function()
-    {
-        if (!this.reviewDiv) {
-            return;
-        }
-
-        var t;
-
-        this.reviewDiv.select('span.spellcheckIncorrect').each(function(n) {
-            n.replace(n.innerHTML);
-        });
-
-        t = this.reviewDiv.innerHTML;
-        if (!this.htmlAreaParent) {
-            t = t.replace(/<br *\/?>/gi, '~~~').unescapeHTML().replace(/~~~/g, "\n");
-        }
-        this.target.setValue(t);
-        this.target.enable();
-
-        if (this.resumeOnDblClick) {
-            this.reviewDiv.stopObserving('dblclick');
-        }
-        this.reviewDiv.remove();
-        this.reviewDiv = null;
-
-        this.setStatus('CheckSpelling');
-
-        if (!this.htmlAreaParent) {
-            this.target.show();
-        }
-
-        this.target.fire('SpellChecker:after');
-    },
-
-    setStatus: function(state)
-    {
-        if (!this.statusButton) {
-            return;
-        }
-
-        this.state = state;
-        switch (this.statusButton.tagName) {
-        case 'INPUT':
-            this.statusButton.setValue(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;
-    },
-
-    disable: function(disable)
-    {
-        this.disabled = disable;
-    }
-
-});