+++ /dev/null
-# Makefile for PO directory in any package using GNU gettext.
-# Copyright (C) 1995-1997, 2000-2004 by Ulrich Drepper <drepper@gnu.ai.mit.edu>
-#
-# This file can be copied and used freely without restrictions. It can
-# be used in projects which are not available under the GNU General Public
-# License but which still want to provide support for the GNU gettext
-# functionality.
-# Please note that the actual code of GNU gettext is covered by the GNU
-# General Public License and is *not* in the public domain.
-#
-# Origin: gettext-0.14
-
-PACKAGE = webcit
-VERSION = 6.21
-
-SHELL = /bin/sh
-
-
-srcdir = .
-top_srcdir = ..
-
-
-prefix = /appl/citadel
-exec_prefix = ${prefix}
-datadir = ${prefix}/share
-localedir = $(datadir)/locale
-gettextsrcdir = $(datadir)/gettext/po
-
-INSTALL = /usr/bin/install -c
-INSTALL_DATA = ${INSTALL} -m 644
-MKINSTALLDIRS = $(top_builddir)/./mkinstalldirs
-mkinstalldirs = $(SHELL) $(MKINSTALLDIRS)
-
-GMSGFMT = /usr/bin/msgfmt
-MSGFMT = /usr/bin/msgfmt
-XGETTEXT = /usr/bin/xgettext
-MSGMERGE = msgmerge
-MSGMERGE_UPDATE = /usr/bin/msgmerge --update
-MSGINIT = msginit
-MSGCONV = msgconv
-MSGFILTER = msgfilter
-
-POFILES = @POFILES@
-GMOFILES = @GMOFILES@
-UPDATEPOFILES = @UPDATEPOFILES@
-DUMMYPOFILES = @DUMMYPOFILES@
-DISTFILES.common = Makefile.in.in remove-potcdate.sin \
-$(DISTFILES.common.extra1) $(DISTFILES.common.extra2) $(DISTFILES.common.extra3)
-DISTFILES = $(DISTFILES.common) Makevars POTFILES.in $(DOMAIN).pot stamp-po \
-$(POFILES) $(GMOFILES) \
-$(DISTFILES.extra1) $(DISTFILES.extra2) $(DISTFILES.extra3)
-
-POTFILES = \
-
-CATALOGS = @CATALOGS@
-
-# Makevars gets inserted here. (Don't remove this line!)
-
-.SUFFIXES:
-.SUFFIXES: .po .gmo .mo .sed .sin .nop .po-create .po-update
-
-.po.mo:
- @echo "$(MSGFMT) -c -o $@ $<"; \
- $(MSGFMT) -c -o t-$@ $< && mv t-$@ $@
-
-.po.gmo:
- @lang=`echo $* | sed -e 's,.*/,,'`; \
- test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \
- echo "$${cdcmd}rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics -o $${lang}.gmo $${lang}.po"; \
- cd $(srcdir) && rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics -o t-$${lang}.gmo $${lang}.po && mv t-$${lang}.gmo $${lang}.gmo
-
-.sin.sed:
- sed -e '/^#/d' $< > t-$@
- mv t-$@ $@
-
-
-all: all-yes
-
-all-yes: stamp-po
-all-no:
-
-# stamp-po is a timestamp denoting the last time at which the CATALOGS have
-# been loosely updated. Its purpose is that when a developer or translator
-# checks out the package via CVS, and the $(DOMAIN).pot file is not in CVS,
-# "make" will update the $(DOMAIN).pot and the $(CATALOGS), but subsequent
-# invocations of "make" will do nothing. This timestamp would not be necessary
-# if updating the $(CATALOGS) would always touch them; however, the rule for
-# $(POFILES) has been designed to not touch files that don't need to be
-# changed.
-stamp-po: $(srcdir)/$(DOMAIN).pot
- test -z "$(GMOFILES)" || $(MAKE) $(GMOFILES)
- @echo "touch stamp-po"
- @echo timestamp > stamp-poT
- @mv stamp-poT stamp-po
-
-# Note: Target 'all' must not depend on target '$(DOMAIN).pot-update',
-# otherwise packages like GCC can not be built if only parts of the source
-# have been downloaded.
-
-# This target rebuilds $(DOMAIN).pot; it is an expensive operation.
-# Note that $(DOMAIN).pot is not touched if it doesn't need to be changed.
-$(DOMAIN).pot-update: $(POTFILES) $(srcdir)/POTFILES.in remove-potcdate.sed
- $(XGETTEXT) --default-domain=$(DOMAIN) --directory=$(top_srcdir) \
- --add-comments=TRANSLATORS: $(XGETTEXT_OPTIONS) \
- --files-from=$(srcdir)/POTFILES.in \
- --copyright-holder='$(COPYRIGHT_HOLDER)' \
- --msgid-bugs-address='$(MSGID_BUGS_ADDRESS)'
- test ! -f $(DOMAIN).po || { \
- if test -f $(srcdir)/$(DOMAIN).pot; then \
- sed -f remove-potcdate.sed < $(srcdir)/$(DOMAIN).pot > $(DOMAIN).1po && \
- sed -f remove-potcdate.sed < $(DOMAIN).po > $(DOMAIN).2po && \
- if cmp $(DOMAIN).1po $(DOMAIN).2po >/dev/null 2>&1; then \
- rm -f $(DOMAIN).1po $(DOMAIN).2po $(DOMAIN).po; \
- else \
- rm -f $(DOMAIN).1po $(DOMAIN).2po $(srcdir)/$(DOMAIN).pot && \
- mv $(DOMAIN).po $(srcdir)/$(DOMAIN).pot; \
- fi; \
- else \
- mv $(DOMAIN).po $(srcdir)/$(DOMAIN).pot; \
- fi; \
- }
-
-# This rule has no dependencies: we don't need to update $(DOMAIN).pot at
-# every "make" invocation, only create it when it is missing.
-# Only "make $(DOMAIN).pot-update" or "make dist" will force an update.
-$(srcdir)/$(DOMAIN).pot:
- $(MAKE) $(DOMAIN).pot-update
-
-# This target rebuilds a PO file if $(DOMAIN).pot has changed.
-# Note that a PO file is not touched if it doesn't need to be changed.
-$(POFILES): $(srcdir)/$(DOMAIN).pot
- @lang=`echo $@ | sed -e 's,.*/,,' -e 's/\.po$$//'`; \
- if test -f "$(srcdir)/$${lang}.po"; then \
- test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \
- echo "$${cdcmd}$(MSGMERGE_UPDATE) $${lang}.po $(DOMAIN).pot"; \
- cd $(srcdir) && $(MSGMERGE_UPDATE) $${lang}.po $(DOMAIN).pot; \
- else \
- $(MAKE) $${lang}.po-create; \
- fi
-
-
-install: install-exec install-data
-install-exec:
-install-data: install-data-yes
- if test "$(PACKAGE)" = "gettext-tools"; then \
- $(mkinstalldirs) $(DESTDIR)$(gettextsrcdir); \
- for file in $(DISTFILES.common) Makevars.template; do \
- $(INSTALL_DATA) $(srcdir)/$$file \
- $(DESTDIR)$(gettextsrcdir)/$$file; \
- done; \
- for file in Makevars; do \
- rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \
- done; \
- else \
- : ; \
- fi
-install-data-no: all
-install-data-yes: all
- $(mkinstalldirs) $(DESTDIR)$(datadir)
- @catalogs='$(CATALOGS)'; \
- for cat in $$catalogs; do \
- cat=`basename $$cat`; \
- lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \
- dir=$(localedir)/$$lang/LC_MESSAGES; \
- $(mkinstalldirs) $(DESTDIR)$$dir; \
- if test -r $$cat; then realcat=$$cat; else realcat=$(srcdir)/$$cat; fi; \
- $(INSTALL_DATA) $$realcat $(DESTDIR)$$dir/$(DOMAIN).mo; \
- echo "installing $$realcat as $(DESTDIR)$$dir/$(DOMAIN).mo"; \
- for lc in '' $(EXTRA_LOCALE_CATEGORIES); do \
- if test -n "$$lc"; then \
- if (cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc 2>/dev/null) | grep ' -> ' >/dev/null; then \
- link=`cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc | sed -e 's/^.* -> //'`; \
- mv $(DESTDIR)$(localedir)/$$lang/$$lc $(DESTDIR)$(localedir)/$$lang/$$lc.old; \
- mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \
- (cd $(DESTDIR)$(localedir)/$$lang/$$lc.old && \
- for file in *; do \
- if test -f $$file; then \
- ln -s ../$$link/$$file $(DESTDIR)$(localedir)/$$lang/$$lc/$$file; \
- fi; \
- done); \
- rm -f $(DESTDIR)$(localedir)/$$lang/$$lc.old; \
- else \
- if test -d $(DESTDIR)$(localedir)/$$lang/$$lc; then \
- :; \
- else \
- rm -f $(DESTDIR)$(localedir)/$$lang/$$lc; \
- mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \
- fi; \
- fi; \
- rm -f $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \
- ln -s ../LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo 2>/dev/null || \
- ln $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo 2>/dev/null || \
- cp -p $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \
- echo "installing $$realcat link as $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo"; \
- fi; \
- done; \
- done
-
-install-strip: install
-
-installdirs: installdirs-exec installdirs-data
-installdirs-exec:
-installdirs-data: installdirs-data-yes
- if test "$(PACKAGE)" = "gettext-tools"; then \
- $(mkinstalldirs) $(DESTDIR)$(gettextsrcdir); \
- else \
- : ; \
- fi
-installdirs-data-no:
-installdirs-data-yes:
- $(mkinstalldirs) $(DESTDIR)$(datadir)
- @catalogs='$(CATALOGS)'; \
- for cat in $$catalogs; do \
- cat=`basename $$cat`; \
- lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \
- dir=$(localedir)/$$lang/LC_MESSAGES; \
- $(mkinstalldirs) $(DESTDIR)$$dir; \
- for lc in '' $(EXTRA_LOCALE_CATEGORIES); do \
- if test -n "$$lc"; then \
- if (cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc 2>/dev/null) | grep ' -> ' >/dev/null; then \
- link=`cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc | sed -e 's/^.* -> //'`; \
- mv $(DESTDIR)$(localedir)/$$lang/$$lc $(DESTDIR)$(localedir)/$$lang/$$lc.old; \
- mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \
- (cd $(DESTDIR)$(localedir)/$$lang/$$lc.old && \
- for file in *; do \
- if test -f $$file; then \
- ln -s ../$$link/$$file $(DESTDIR)$(localedir)/$$lang/$$lc/$$file; \
- fi; \
- done); \
- rm -f $(DESTDIR)$(localedir)/$$lang/$$lc.old; \
- else \
- if test -d $(DESTDIR)$(localedir)/$$lang/$$lc; then \
- :; \
- else \
- rm -f $(DESTDIR)$(localedir)/$$lang/$$lc; \
- mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \
- fi; \
- fi; \
- fi; \
- done; \
- done
-
-# Define this as empty until I found a useful application.
-installcheck:
-
-uninstall: uninstall-exec uninstall-data
-uninstall-exec:
-uninstall-data: uninstall-data-yes
- if test "$(PACKAGE)" = "gettext-tools"; then \
- for file in $(DISTFILES.common) Makevars.template; do \
- rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \
- done; \
- else \
- : ; \
- fi
-uninstall-data-no:
-uninstall-data-yes:
- catalogs='$(CATALOGS)'; \
- for cat in $$catalogs; do \
- cat=`basename $$cat`; \
- lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \
- for lc in LC_MESSAGES $(EXTRA_LOCALE_CATEGORIES); do \
- rm -f $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \
- done; \
- done
-
-check: all
-
-info dvi ps pdf html tags TAGS ctags CTAGS ID:
-
-mostlyclean:
- rm -f remove-potcdate.sed
- rm -f stamp-poT
- rm -f core core.* $(DOMAIN).po $(DOMAIN).1po $(DOMAIN).2po *.new.po
- rm -fr *.o
-
-clean: mostlyclean
-
-distclean: clean
- rm -f Makefile Makefile.in POTFILES *.mo
-
-maintainer-clean: distclean
- @echo "This command is intended for maintainers to use;"
- @echo "it deletes files that may require special tools to rebuild."
- rm -f stamp-po $(GMOFILES)
-
-distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
-dist distdir:
- $(MAKE) update-po
- @$(MAKE) dist2
-# This is a separate target because 'update-po' must be executed before.
-dist2: $(DISTFILES)
- dists="$(DISTFILES)"; \
- if test "$(PACKAGE)" = "gettext-tools"; then \
- dists="$$dists Makevars.template"; \
- fi; \
- if test -f $(srcdir)/ChangeLog; then \
- dists="$$dists ChangeLog"; \
- fi; \
- for i in 0 1 2 3 4 5 6 7 8 9; do \
- if test -f $(srcdir)/ChangeLog.$$i; then \
- dists="$$dists ChangeLog.$$i"; \
- fi; \
- done; \
- if test -f $(srcdir)/LINGUAS; then dists="$$dists LINGUAS"; fi; \
- for file in $$dists; do \
- if test -f $$file; then \
- cp -p $$file $(distdir); \
- else \
- cp -p $(srcdir)/$$file $(distdir); \
- fi; \
- done
-
-update-po: Makefile
- $(MAKE) $(DOMAIN).pot-update
- test -z "$(UPDATEPOFILES)" || $(MAKE) $(UPDATEPOFILES)
- $(MAKE) update-gmo
-
-# General rule for creating PO files.
-
-.nop.po-create:
- @lang=`echo $@ | sed -e 's/\.po-create$$//'`; \
- echo "File $$lang.po does not exist. If you are a translator, you can create it through 'msginit'." 1>&2; \
- exit 1
-
-# General rule for updating PO files.
-
-.nop.po-update:
- @lang=`echo $@ | sed -e 's/\.po-update$$//'`; \
- if test "$(PACKAGE)" = "gettext-tools"; then PATH=`pwd`/../src:$$PATH; fi; \
- tmpdir=`pwd`; \
- echo "$$lang:"; \
- test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \
- echo "$${cdcmd}$(MSGMERGE) $$lang.po $(DOMAIN).pot -o $$lang.new.po"; \
- cd $(srcdir); \
- if $(MSGMERGE) $$lang.po $(DOMAIN).pot -o $$tmpdir/$$lang.new.po; then \
- if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \
- rm -f $$tmpdir/$$lang.new.po; \
- else \
- if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \
- :; \
- else \
- echo "msgmerge for $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \
- exit 1; \
- fi; \
- fi; \
- else \
- echo "msgmerge for $$lang.po failed!" 1>&2; \
- rm -f $$tmpdir/$$lang.new.po; \
- fi
-
-$(DUMMYPOFILES):
-
-update-gmo: Makefile $(GMOFILES)
- @:
-
-Makefile: Makefile.in.in $(top_builddir)/config.status @POMAKEFILEDEPS@
- cd $(top_builddir) \
- && CONFIG_FILES=$(subdir)/$@.in CONFIG_HEADERS= \
- $(SHELL) ./config.status
-
-force:
-
-# Tell versions [3.59,3.63) of GNU make not to export all variables.
-# Otherwise a system limit (for SysV at least) may be exceeded.
-.NOEXPORT:
--- /dev/null
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
+// (c) 2005 Jon Tirsen (http://www.tirsen.com)
+// Contributors:
+// Richard Livsey
+// Rahul Bhargava
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// Autocompleter.Base handles all the autocompletion functionality
+// that's independent of the data source for autocompletion. This
+// includes drawing the autocompletion menu, observing keyboard
+// and mouse events, and similar.
+//
+// Specific autocompleters need to provide, at the very least,
+// a getUpdatedChoices function that will be invoked every time
+// the text inside the monitored textbox changes. This method
+// should get the text for which to provide autocompletion by
+// invoking this.getToken(), NOT by directly accessing
+// this.element.value. This is to allow incremental tokenized
+// autocompletion. Specific auto-completion logic (AJAX, etc)
+// belongs in getUpdatedChoices.
+//
+// Tokenized incremental autocompletion is enabled automatically
+// when an autocompleter is instantiated with the 'tokens' option
+// in the options parameter, e.g.:
+// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
+// will incrementally autocomplete with a comma as the token.
+// Additionally, ',' in the above example can be replaced with
+// a token array, e.g. { tokens: [',', '\n'] } which
+// enables autocompletion on multiple tokens. This is most
+// useful when one of the tokens is \n (a newline), as it
+// allows smart autocompletion after linebreaks.
+
+var Autocompleter = {}
+Autocompleter.Base = function() {};
+Autocompleter.Base.prototype = {
+ baseInitialize: function(element, update, options) {
+ this.element = $(element);
+ this.update = $(update);
+ this.hasFocus = false;
+ this.changed = false;
+ this.active = false;
+ this.index = 0;
+ this.entryCount = 0;
+
+ if (this.setOptions)
+ this.setOptions(options);
+ else
+ this.options = options || {};
+
+ this.options.paramName = this.options.paramName || this.element.name;
+ this.options.tokens = this.options.tokens || [];
+ this.options.frequency = this.options.frequency || 0.4;
+ this.options.minChars = this.options.minChars || 1;
+ this.options.onShow = this.options.onShow ||
+ function(element, update){
+ if(!update.style.position || update.style.position=='absolute') {
+ update.style.position = 'absolute';
+ Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight});
+ }
+ new ScriptaculousEffect.Appear(update,{duration:0.15});
+ };
+ this.options.onHide = this.options.onHide ||
+ function(element, update){ new ScriptaculousEffect.Fade(update,{duration:0.15}) };
+
+ if (typeof(this.options.tokens) == 'string')
+ this.options.tokens = new Array(this.options.tokens);
+
+ this.observer = null;
+
+ this.element.setAttribute('autocomplete','off');
+
+ Element.hide(this.update);
+
+ Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
+ Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
+ },
+
+ show: function() {
+ if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
+ if(!this.iefix && (navigator.appVersion.indexOf('MSIE')>0) && (Element.getStyle(this.update, 'position')=='absolute')) {
+ new Insertion.After(this.update,
+ '<iframe id="' + this.update.id + '_iefix" '+
+ 'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
+ 'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
+ this.iefix = $(this.update.id+'_iefix');
+ }
+ if(this.iefix) {
+ Position.clone(this.update, this.iefix);
+ this.iefix.style.zIndex = 1;
+ this.update.style.zIndex = 2;
+ Element.show(this.iefix);
+ }
+ },
+
+ hide: function() {
+ if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
+ if(this.iefix) Element.hide(this.iefix);
+ },
+
+ startIndicator: function() {
+ if(this.options.indicator) Element.show(this.options.indicator);
+ },
+
+ stopIndicator: function() {
+ if(this.options.indicator) Element.hide(this.options.indicator);
+ },
+
+ onKeyPress: function(event) {
+ if(this.active)
+ switch(event.keyCode) {
+ case Event.KEY_TAB:
+ case Event.KEY_RETURN:
+ this.selectEntry();
+ Event.stop(event);
+ case Event.KEY_ESC:
+ this.hide();
+ this.active = false;
+ Event.stop(event);
+ return;
+ case Event.KEY_LEFT:
+ case Event.KEY_RIGHT:
+ return;
+ case Event.KEY_UP:
+ this.markPrevious();
+ this.render();
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
+ return;
+ case Event.KEY_DOWN:
+ this.markNext();
+ this.render();
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
+ return;
+ }
+ else
+ if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN)
+ return;
+
+ this.changed = true;
+ this.hasFocus = true;
+
+ if(this.observer) clearTimeout(this.observer);
+ this.observer =
+ setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
+ },
+
+ onHover: function(event) {
+ var element = Event.findElement(event, 'LI');
+ if(this.index != element.autocompleteIndex)
+ {
+ this.index = element.autocompleteIndex;
+ this.render();
+ }
+ Event.stop(event);
+ },
+
+ onClick: function(event) {
+ var element = Event.findElement(event, 'LI');
+ this.index = element.autocompleteIndex;
+ this.selectEntry();
+ this.hide();
+ },
+
+ onBlur: function(event) {
+ // needed to make click events working
+ setTimeout(this.hide.bind(this), 250);
+ this.hasFocus = false;
+ this.active = false;
+ },
+
+ render: function() {
+ if(this.entryCount > 0) {
+ for (var i = 0; i < this.entryCount; i++)
+ this.index==i ?
+ Element.addClassName(this.getEntry(i),"selected") :
+ Element.removeClassName(this.getEntry(i),"selected");
+
+ if(this.hasFocus) {
+ this.show();
+ this.active = true;
+ }
+ } else this.hide();
+ },
+
+ markPrevious: function() {
+ if(this.index > 0) this.index--
+ else this.index = this.entryCcount-1;
+ },
+
+ markNext: function() {
+ if(this.index < this.entryCount-1) this.index++
+ else this.index = 0;
+ },
+
+ getEntry: function(index) {
+ return this.update.firstChild.childNodes[index];
+ },
+
+ getCurrentEntry: function() {
+ return this.getEntry(this.index);
+ },
+
+ selectEntry: function() {
+ this.active = false;
+ this.updateElement(this.getCurrentEntry());
+ },
+
+ updateElement: function(selectedElement) {
+ if (this.options.updateElement) {
+ this.options.updateElement(selectedElement);
+ return;
+ }
+
+ var value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
+ var lastTokenPos = this.findLastToken();
+ if (lastTokenPos != -1) {
+ var newValue = this.element.value.substr(0, lastTokenPos + 1);
+ var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
+ if (whitespace)
+ newValue += whitespace[0];
+ this.element.value = newValue + value;
+ } else {
+ this.element.value = value;
+ }
+ this.element.focus();
+ },
+
+ updateChoices: function(choices) {
+ if(!this.changed && this.hasFocus) {
+ this.update.innerHTML = choices;
+ Element.cleanWhitespace(this.update);
+ Element.cleanWhitespace(this.update.firstChild);
+
+ if(this.update.firstChild && this.update.firstChild.childNodes) {
+ this.entryCount =
+ this.update.firstChild.childNodes.length;
+ for (var i = 0; i < this.entryCount; i++) {
+ var entry = this.getEntry(i);
+ entry.autocompleteIndex = i;
+ this.addObservers(entry);
+ }
+ } else {
+ this.entryCount = 0;
+ }
+
+ this.stopIndicator();
+
+ this.index = 0;
+ this.render();
+ }
+ },
+
+ addObservers: function(element) {
+ Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
+ Event.observe(element, "click", this.onClick.bindAsEventListener(this));
+ },
+
+ onObserverEvent: function() {
+ this.changed = false;
+ if(this.getToken().length>=this.options.minChars) {
+ this.startIndicator();
+ this.getUpdatedChoices();
+ } else {
+ this.active = false;
+ this.hide();
+ }
+ },
+
+ getToken: function() {
+ var tokenPos = this.findLastToken();
+ if (tokenPos != -1)
+ var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
+ else
+ var ret = this.element.value;
+
+ return /\n/.test(ret) ? '' : ret;
+ },
+
+ findLastToken: function() {
+ var lastTokenPos = -1;
+
+ for (var i=0; i<this.options.tokens.length; i++) {
+ var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
+ if (thisTokenPos > lastTokenPos)
+ lastTokenPos = thisTokenPos;
+ }
+ return lastTokenPos;
+ }
+}
+
+Ajax.Autocompleter = Class.create();
+Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
+ initialize: function(element, update, url, options) {
+ this.baseInitialize(element, update, options);
+ this.options.asynchronous = true;
+ this.options.onComplete = this.onComplete.bind(this);
+ this.options.defaultParams = this.options.parameters || null;
+ this.url = url;
+ },
+
+ getUpdatedChoices: function() {
+ entry = encodeURIComponent(this.options.paramName) + '=' +
+ encodeURIComponent(this.getToken());
+
+ this.options.parameters = this.options.callback ?
+ this.options.callback(this.element, entry) : entry;
+
+ if(this.options.defaultParams)
+ this.options.parameters += '&' + this.options.defaultParams;
+
+ new Ajax.Request(this.url, this.options);
+ },
+
+ onComplete: function(request) {
+ this.updateChoices(request.responseText);
+ }
+
+});
+
+// The local array autocompleter. Used when you'd prefer to
+// inject an array of autocompletion options into the page, rather
+// than sending out Ajax queries, which can be quite slow sometimes.
+//
+// The constructor takes four parameters. The first two are, as usual,
+// the id of the monitored textbox, and id of the autocompletion menu.
+// The third is the array you want to autocomplete from, and the fourth
+// is the options block.
+//
+// Extra local autocompletion options:
+// - choices - How many autocompletion choices to offer
+//
+// - partialSearch - If false, the autocompleter will match entered
+// text only at the beginning of strings in the
+// autocomplete array. Defaults to true, which will
+// match text at the beginning of any *word* in the
+// strings in the autocomplete array. If you want to
+// search anywhere in the string, additionally set
+// the option fullSearch to true (default: off).
+//
+// - fullSsearch - Search anywhere in autocomplete array strings.
+//
+// - partialChars - How many characters to enter before triggering
+// a partial match (unlike minChars, which defines
+// how many characters are required to do any match
+// at all). Defaults to 2.
+//
+// - ignoreCase - Whether to ignore case when autocompleting.
+// Defaults to true.
+//
+// It's possible to pass in a custom function as the 'selector'
+// option, if you prefer to write your own autocompletion logic.
+// In that case, the other options above will not apply unless
+// you support them.
+
+Autocompleter.Local = Class.create();
+Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
+ initialize: function(element, update, array, options) {
+ this.baseInitialize(element, update, options);
+ this.options.array = array;
+ },
+
+ getUpdatedChoices: function() {
+ this.updateChoices(this.options.selector(this));
+ },
+
+ setOptions: function(options) {
+ this.options = Object.extend({
+ choices: 10,
+ partialSearch: true,
+ partialChars: 2,
+ ignoreCase: true,
+ fullSearch: false,
+ selector: function(instance) {
+ var ret = []; // Beginning matches
+ var partial = []; // Inside matches
+ var entry = instance.getToken();
+ var count = 0;
+
+ for (var i = 0; i < instance.options.array.length &&
+ ret.length < instance.options.choices ; i++) {
+
+ var elem = instance.options.array[i];
+ var foundPos = instance.options.ignoreCase ?
+ elem.toLowerCase().indexOf(entry.toLowerCase()) :
+ elem.indexOf(entry);
+
+ while (foundPos != -1) {
+ if (foundPos == 0 && elem.length != entry.length) {
+ ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
+ elem.substr(entry.length) + "</li>");
+ break;
+ } else if (entry.length >= instance.options.partialChars &&
+ instance.options.partialSearch && foundPos != -1) {
+ if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
+ partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
+ elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
+ foundPos + entry.length) + "</li>");
+ break;
+ }
+ }
+
+ foundPos = instance.options.ignoreCase ?
+ elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
+ elem.indexOf(entry, foundPos + 1);
+
+ }
+ }
+ if (partial.length)
+ ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
+ return "<ul>" + ret.join('') + "</ul>";
+ }
+ }, options || {});
+ }
+});
+
+// AJAX in-place editor
+//
+// The constructor takes three parameters. The first is the element
+// that should support in-place editing. The second is the url to submit
+// the changed value to. The server should respond with the updated
+// value (the server might have post-processed it or validation might
+// have prevented it from changing). The third is a hash of options.
+//
+// Supported options are (all are optional and have sensible defaults):
+// - okText - The text of the submit button that submits the changed value
+// to the server (default: "ok")
+// - cancelText - The text of the link that cancels editing (default: "cancel")
+// - savingText - The text being displayed as the AJAX engine communicates
+// with the server (default: "Saving...")
+// - formId - The id given to the <form> element
+// (default: the id of the element to edit plus '-inplaceeditor')
+
+Ajax.InPlaceEditor = Class.create();
+Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
+Ajax.InPlaceEditor.prototype = {
+ initialize: function(element, url, options) {
+ this.url = url;
+ this.element = $(element);
+
+ this.options = Object.extend({
+ okText: "ok",
+ cancelText: "cancel",
+ savingText: "Saving...",
+ clickToEditText: "Click to edit",
+ okText: "ok",
+ rows: 1,
+ onComplete: function(transport, element) {
+ new ScriptaculousEffect.Highlight(element, {startcolor: this.options.highlightcolor});
+ },
+ onFailure: function(transport) {
+ alert("Error communicating with the server: " + transport.responseText.stripTags());
+ },
+ callback: function(form) {
+ return Form.serialize(form);
+ },
+ loadingText: 'Loading...',
+ savingClassName: 'inplaceeditor-saving',
+ formClassName: 'inplaceeditor-form',
+ highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
+ highlightendcolor: "#FFFFFF",
+ externalControl: null,
+ ajaxOptions: {}
+ }, options || {});
+
+ if(!this.options.formId && this.element.id) {
+ this.options.formId = this.element.id + "-inplaceeditor";
+ if ($(this.options.formId)) {
+ // there's already a form with that name, don't specify an id
+ this.options.formId = null;
+ }
+ }
+
+ if (this.options.externalControl) {
+ this.options.externalControl = $(this.options.externalControl);
+ }
+
+ this.originalBackground = Element.getStyle(this.element, 'background-color');
+ if (!this.originalBackground) {
+ this.originalBackground = "transparent";
+ }
+
+ this.element.title = this.options.clickToEditText;
+
+ this.onclickListener = this.enterEditMode.bindAsEventListener(this);
+ this.mouseoverListener = this.enterHover.bindAsEventListener(this);
+ this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
+ Event.observe(this.element, 'click', this.onclickListener);
+ Event.observe(this.element, 'mouseover', this.mouseoverListener);
+ Event.observe(this.element, 'mouseout', this.mouseoutListener);
+ if (this.options.externalControl) {
+ Event.observe(this.options.externalControl, 'click', this.onclickListener);
+ Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
+ Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
+ }
+ },
+ enterEditMode: function() {
+ if (this.saving) return;
+ if (this.editing) return;
+ this.editing = true;
+ this.onEnterEditMode();
+ if (this.options.externalControl) {
+ Element.hide(this.options.externalControl);
+ }
+ Element.hide(this.element);
+ this.form = this.getForm();
+ this.element.parentNode.insertBefore(this.form, this.element);
+ },
+ getForm: function() {
+ form = document.createElement("form");
+ form.id = this.options.formId;
+ Element.addClassName(form, this.options.formClassName)
+ form.onsubmit = this.onSubmit.bind(this);
+
+ this.createEditField(form);
+
+ if (this.options.textarea) {
+ var br = document.createElement("br");
+ form.appendChild(br);
+ }
+
+ okButton = document.createElement("input");
+ okButton.type = "submit";
+ okButton.value = this.options.okText;
+ form.appendChild(okButton);
+
+ cancelLink = document.createElement("a");
+ cancelLink.href = "#";
+ cancelLink.appendChild(document.createTextNode(this.options.cancelText));
+ cancelLink.onclick = this.onclickCancel.bind(this);
+ form.appendChild(cancelLink);
+ return form;
+ },
+ createEditField: function(form) {
+ if (this.options.rows == 1) {
+ this.options.textarea = false;
+ var textField = document.createElement("input");
+ textField.type = "text";
+ textField.name = "value";
+ textField.value = this.getText();
+ textField.style.backgroundColor = this.options.highlightcolor;
+ var size = this.options.size || this.options.cols || 0;
+ if (size != 0)
+ textField.size = size;
+ form.appendChild(textField);
+ this.editField = textField;
+ } else {
+ this.options.textarea = true;
+ var textArea = document.createElement("textarea");
+ textArea.name = "value";
+ textArea.value = this.getText();
+ textArea.rows = this.options.rows;
+ textArea.cols = this.options.cols || 40;
+ form.appendChild(textArea);
+ this.editField = textArea;
+ }
+ },
+ getText: function() {
+ if (this.options.loadTextURL) {
+ this.loadExternalText();
+ return this.options.loadingText;
+ } else {
+ return this.element.innerHTML;
+ }
+ },
+ loadExternalText: function() {
+ new Ajax.Request(
+ this.options.loadTextURL,
+ {
+ asynchronous: true,
+ onComplete: this.onLoadedExternalText.bind(this)
+ }
+ );
+ },
+ onLoadedExternalText: function(transport) {
+ this.form.value.value = transport.responseText.stripTags();
+ },
+ onclickCancel: function() {
+ this.onComplete();
+ this.leaveEditMode();
+ return false;
+ },
+ onFailure: function(transport) {
+ this.options.onFailure(transport);
+ if (this.oldInnerHTML) {
+ this.element.innerHTML = this.oldInnerHTML;
+ this.oldInnerHTML = null;
+ }
+ return false;
+ },
+ onSubmit: function() {
+ this.saving = true;
+ new Ajax.Updater(
+ {
+ success: this.element,
+ // don't update on failure (this could be an option)
+ failure: null
+ },
+ this.url,
+ Object.extend({
+ parameters: this.options.callback(this.form, this.editField.value),
+ onComplete: this.onComplete.bind(this),
+ onFailure: this.onFailure.bind(this)
+ }, this.options.ajaxOptions)
+ );
+ this.onLoading();
+ return false;
+ },
+ onLoading: function() {
+ this.saving = true;
+ this.removeForm();
+ this.leaveHover();
+ this.showSaving();
+ },
+ showSaving: function() {
+ this.oldInnerHTML = this.element.innerHTML;
+ this.element.innerHTML = this.options.savingText;
+ Element.addClassName(this.element, this.options.savingClassName);
+ this.element.style.backgroundColor = this.originalBackground;
+ Element.show(this.element);
+ },
+ removeForm: function() {
+ if(this.form) {
+ Element.remove(this.form);
+ this.form = null;
+ }
+ },
+ enterHover: function() {
+ if (this.saving) return;
+ this.element.style.backgroundColor = this.options.highlightcolor;
+ if (this.effect) {
+ this.effect.cancel();
+ }
+ Element.addClassName(this.element, this.options.hoverClassName)
+ },
+ leaveHover: function() {
+ if (this.options.backgroundColor) {
+ this.element.style.backgroundColor = this.oldBackground;
+ }
+ Element.removeClassName(this.element, this.options.hoverClassName)
+ if (this.saving) return;
+ this.effect = new ScriptaculousEffect.Highlight(this.element, {
+ startcolor: this.options.highlightcolor,
+ endcolor: this.options.highlightendcolor,
+ restorecolor: this.originalBackground
+ });
+ },
+ leaveEditMode: function() {
+ Element.removeClassName(this.element, this.options.savingClassName);
+ this.removeForm();
+ this.leaveHover();
+ this.element.style.backgroundColor = this.originalBackground;
+ Element.show(this.element);
+ if (this.options.externalControl) {
+ Element.show(this.options.externalControl);
+ }
+ this.editing = false;
+ this.saving = false;
+ this.oldInnerHTML = null;
+ this.onLeaveEditMode();
+ },
+ onComplete: function(transport) {
+ this.leaveEditMode();
+ this.options.onComplete.bind(this)(transport, this.element);
+ },
+ onEnterEditMode: function() {},
+ onLeaveEditMode: function() {},
+ dispose: function() {
+ if (this.oldInnerHTML) {
+ this.element.innerHTML = this.oldInnerHTML;
+ }
+ this.leaveEditMode();
+ Event.stopObserving(this.element, 'click', this.onclickListener);
+ Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
+ Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
+ if (this.options.externalControl) {
+ Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
+ Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
+ Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
+ }
+ }
+};
\ No newline at end of file
--- /dev/null
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// Element.Class part Copyright (c) 2005 by Rick Olson
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+/*--------------------------------------------------------------------------*/
+
+var Droppables = {
+ drops: false,
+
+ remove: function(element) {
+ for(var i = 0; i < this.drops.length; i++)
+ if(this.drops[i].element == element)
+ this.drops.splice(i,1);
+ },
+
+ add: function(element) {
+ element = $(element);
+ var options = Object.extend({
+ greedy: true,
+ hoverclass: null
+ }, arguments[1] || {});
+
+ // cache containers
+ if(options.containment) {
+ options._containers = new Array();
+ var containment = options.containment;
+ if((typeof containment == 'object') &&
+ (containment.constructor == Array)) {
+ for(var i=0; i<containment.length; i++)
+ options._containers.push($(containment[i]));
+ } else {
+ options._containers.push($(containment));
+ }
+ options._containers_length =
+ options._containers.length-1;
+ }
+
+ Element.makePositioned(element); // fix IE
+
+ options.element = element;
+
+ // activate the droppable
+ if(!this.drops) this.drops = [];
+ this.drops.push(options);
+ },
+
+ isContained: function(element, drop) {
+ var containers = drop._containers;
+ var parentNode = element.parentNode;
+ var i = drop._containers_length;
+ do { if(parentNode==containers[i]) return true; } while (i--);
+ return false;
+ },
+
+ isAffected: function(pX, pY, element, drop) {
+ return (
+ (drop.element!=element) &&
+ ((!drop._containers) ||
+ this.isContained(element, drop)) &&
+ ((!drop.accept) ||
+ (Element.Class.has_any(element, drop.accept))) &&
+ Position.within(drop.element, pX, pY) );
+ },
+
+ deactivate: function(drop) {
+ Element.Class.remove(drop.element, drop.hoverclass);
+ this.last_active = null;
+ },
+
+ activate: function(drop) {
+ if(this.last_active) this.deactivate(this.last_active);
+ if(drop.hoverclass)
+ Element.Class.add(drop.element, drop.hoverclass);
+ this.last_active = drop;
+ },
+
+ show: function(event, element) {
+ if(!this.drops) return;
+ var pX = Event.pointerX(event);
+ var pY = Event.pointerY(event);
+ Position.prepare();
+
+ var i = this.drops.length-1; do {
+ var drop = this.drops[i];
+ if(this.isAffected(pX, pY, element, drop)) {
+ if(drop.onHover)
+ drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
+ if(drop.greedy) {
+ this.activate(drop);
+ return;
+ }
+ }
+ } while (i--);
+ },
+
+ fire: function(event, element) {
+ if(!this.last_active) return;
+ Position.prepare();
+
+ if (this.isAffected(Event.pointerX(event), Event.pointerY(event), element, this.last_active))
+ if (this.last_active.onDrop)
+ this.last_active.onDrop(element, this.last_active.element);
+
+ },
+
+ reset: function() {
+ if(this.last_active)
+ this.deactivate(this.last_active);
+ }
+}
+
+var Draggables = {
+ observers: new Array(),
+ addObserver: function(observer) {
+ this.observers.push(observer);
+ },
+ removeObserver: function(element) { // element instead of obsever fixes mem leaks
+ for(var i = 0; i < this.observers.length; i++)
+ if(this.observers[i].element && (this.observers[i].element == element))
+ this.observers.splice(i,1);
+ },
+ notify: function(eventName, draggable) { // 'onStart', 'onEnd'
+ for(var i = 0; i < this.observers.length; i++)
+ this.observers[i][eventName](draggable);
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Draggable = Class.create();
+Draggable.prototype = {
+ initialize: function(element) {
+ var options = Object.extend({
+ handle: false,
+ starteffect: function(element) {
+ new ScriptaculousEffect.Opacity(element, {duration:0.2, from:1.0, to:0.7});
+ },
+ reverteffect: function(element, top_offset, left_offset) {
+ var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
+ new ScriptaculousEffect.MoveBy(element, -top_offset, -left_offset, {duration:dur});
+ },
+ endeffect: function(element) {
+ new ScriptaculousEffect.Opacity(element, {duration:0.2, from:0.7, to:1.0});
+ },
+ zindex: 1000,
+ revert: false
+ }, arguments[1] || {});
+
+ this.element = $(element);
+ this.handle = options.handle ? $(options.handle) : this.element;
+
+ Element.makePositioned(this.element); // fix IE
+
+ this.offsetX = 0;
+ this.offsetY = 0;
+ this.originalLeft = this.currentLeft();
+ this.originalTop = this.currentTop();
+ this.originalX = this.element.offsetLeft;
+ this.originalY = this.element.offsetTop;
+ this.originalZ = parseInt(this.element.style.zIndex || "0");
+
+ this.options = options;
+
+ this.active = false;
+ this.dragging = false;
+
+ this.eventMouseDown = this.startDrag.bindAsEventListener(this);
+ this.eventMouseUp = this.endDrag.bindAsEventListener(this);
+ this.eventMouseMove = this.update.bindAsEventListener(this);
+ this.eventKeypress = this.keyPress.bindAsEventListener(this);
+
+ Event.observe(this.handle, "mousedown", this.eventMouseDown);
+ },
+ destroy: function() {
+ Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
+ this.unregisterEvents();
+ },
+ registerEvents: function() {
+ if(this.active) return;
+ Event.observe(document, "mouseup", this.eventMouseUp);
+ Event.observe(document, "mousemove", this.eventMouseMove);
+ Event.observe(document, "keypress", this.eventKeypress);
+ },
+ unregisterEvents: function() {
+ if(!this.active) return;
+ Event.stopObserving(document, "mouseup", this.eventMouseUp);
+ Event.stopObserving(document, "mousemove", this.eventMouseMove);
+ Event.stopObserving(document, "keypress", this.eventKeypress);
+ },
+ currentLeft: function() {
+ return parseInt(this.element.style.left || '0');
+ },
+ currentTop: function() {
+ return parseInt(this.element.style.top || '0')
+ },
+ startDrag: function(event) {
+ if(Event.isLeftClick(event)) {
+ this.registerEvents();
+ this.active = true;
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
+ var offsets = Position.cumulativeOffset(this.element);
+ this.offsetX = (pointer[0] - offsets[0]);
+ this.offsetY = (pointer[1] - offsets[1]);
+ Event.stop(event);
+ }
+ },
+ finishDrag: function(event, success) {
+ this.unregisterEvents();
+
+ this.active = false;
+ this.dragging = false;
+
+ if(this.options.ghosting) {
+ Position.relativize(this.element);
+ Element.remove(this._clone);
+ this._clone = null;
+ }
+
+ if(success) Droppables.fire(event, this.element);
+ Draggables.notify('onEnd', this);
+
+ var revert = this.options.revert;
+ if(revert && typeof revert == 'function') revert = revert(this.element);
+
+ if(revert && this.options.reverteffect) {
+ this.options.reverteffect(this.element,
+ this.currentTop()-this.originalTop,
+ this.currentLeft()-this.originalLeft);
+ } else {
+ this.originalLeft = this.currentLeft();
+ this.originalTop = this.currentTop();
+ }
+
+ this.element.style.zIndex = this.originalZ;
+
+ if(this.options.endeffect)
+ this.options.endeffect(this.element);
+
+
+ Droppables.reset();
+ },
+ keyPress: function(event) {
+ if(this.active) {
+ if(event.keyCode==Event.KEY_ESC) {
+ this.finishDrag(event, false);
+ Event.stop(event);
+ }
+ }
+ },
+ endDrag: function(event) {
+ if(this.active && this.dragging) {
+ this.finishDrag(event, true);
+ Event.stop(event);
+ }
+ this.active = false;
+ this.dragging = false;
+ },
+ draw: function(event) {
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
+ var offsets = Position.cumulativeOffset(this.element);
+ offsets[0] -= this.currentLeft();
+ offsets[1] -= this.currentTop();
+ var style = this.element.style;
+ if((!this.options.constraint) || (this.options.constraint=='horizontal'))
+ style.left = (pointer[0] - offsets[0] - this.offsetX) + "px";
+ if((!this.options.constraint) || (this.options.constraint=='vertical'))
+ style.top = (pointer[1] - offsets[1] - this.offsetY) + "px";
+ if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
+ },
+ update: function(event) {
+ if(this.active) {
+ if(!this.dragging) {
+ var style = this.element.style;
+ this.dragging = true;
+ if(style.position=="") style.position = "relative";
+ style.zIndex = this.options.zindex;
+
+ if(this.options.ghosting) {
+ this._clone = this.element.cloneNode(true);
+ Position.absolutize(this.element);
+ this.element.parentNode.insertBefore(this._clone, this.element);
+ }
+
+ Draggables.notify('onStart', this);
+ if(this.options.starteffect) this.options.starteffect(this.element);
+ }
+
+ Droppables.show(event, this.element);
+ this.draw(event);
+ if(this.options.change) this.options.change(this);
+
+ // fix AppleWebKit rendering
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+
+ Event.stop(event);
+ }
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var SortableObserver = Class.create();
+SortableObserver.prototype = {
+ initialize: function(element, observer) {
+ this.element = $(element);
+ this.observer = observer;
+ this.lastValue = Sortable.serialize(this.element);
+ },
+ onStart: function() {
+ this.lastValue = Sortable.serialize(this.element);
+ },
+ onEnd: function() {
+ Sortable.unmark();
+ if(this.lastValue != Sortable.serialize(this.element))
+ this.observer(this.element)
+ }
+}
+
+var Sortable = {
+ sortables: new Array(),
+ options: function(element){
+ element = $(element);
+ for(var i=0;i<this.sortables.length;i++)
+ if(this.sortables[i].element == element)
+ return this.sortables[i];
+ return null;
+ },
+ destroy: function(element){
+ element = $(element);
+ for(var i=0;i<this.sortables.length;i++) {
+ if(this.sortables[i].element == element) {
+ var s = this.sortables[i];
+ Draggables.removeObserver(s.element);
+ for(var j=0;j<s.droppables.length;j++)
+ Droppables.remove(s.droppables[j]);
+ for(j=0;j<s.draggables.length;j++)
+ s.draggables[j].destroy();
+ this.sortables.splice(i,1);
+ }
+ }
+ },
+ create: function(element) {
+ element = $(element);
+ var options = Object.extend({
+ element: element,
+ tag: 'li', // assumes li children, override with tag: 'tagname'
+ dropOnEmpty: false,
+ tree: false, // fixme: unimplemented
+ overlap: 'vertical', // one of 'vertical', 'horizontal'
+ constraint: 'vertical', // one of 'vertical', 'horizontal', false
+ containment: element, // also takes array of elements (or id's); or false
+ handle: false, // or a CSS class
+ only: false,
+ hoverclass: null,
+ ghosting: false,
+ onChange: function() {},
+ onUpdate: function() {}
+ }, arguments[1] || {});
+
+ // clear any old sortable with same element
+ this.destroy(element);
+
+ // build options for the draggables
+ var options_for_draggable = {
+ revert: true,
+ ghosting: options.ghosting,
+ constraint: options.constraint,
+ handle: handle };
+
+ if(options.starteffect)
+ options_for_draggable.starteffect = options.starteffect;
+
+ if(options.reverteffect)
+ options_for_draggable.reverteffect = options.reverteffect;
+ else
+ if(options.ghosting) options_for_draggable.reverteffect = function(element) {
+ element.style.top = 0;
+ element.style.left = 0;
+ };
+
+ if(options.endeffect)
+ options_for_draggable.endeffect = options.endeffect;
+
+ if(options.zindex)
+ options_for_draggable.zindex = options.zindex;
+
+ // build options for the droppables
+ var options_for_droppable = {
+ overlap: options.overlap,
+ containment: options.containment,
+ hoverclass: options.hoverclass,
+ onHover: Sortable.onHover,
+ greedy: !options.dropOnEmpty
+ }
+
+ // fix for gecko engine
+ Element.cleanWhitespace(element);
+
+ options.draggables = [];
+ options.droppables = [];
+
+ // make it so
+
+ // drop on empty handling
+ if(options.dropOnEmpty) {
+ Droppables.add(element,
+ {containment: options.containment, onHover: Sortable.onEmptyHover, greedy: false});
+ options.droppables.push(element);
+ }
+
+ var elements = this.findElements(element, options);
+ if(elements) {
+ for (var i = 0; i < elements.length; i++) {
+ // handles are per-draggable
+ var handle = options.handle ?
+ Element.Class.childrenWith(elements[i], options.handle)[0] : elements[i];
+ options.draggables.push(new Draggable(elements[i], Object.extend(options_for_draggable, { handle: handle })));
+ Droppables.add(elements[i], options_for_droppable);
+
+ options.droppables.push(elements[i]);
+ }
+ }
+
+ // keep reference
+ this.sortables.push(options);
+
+ // for onupdate
+ Draggables.addObserver(new SortableObserver(element, options.onUpdate));
+
+ },
+
+ // return all suitable-for-sortable elements in a guaranteed order
+ findElements: function(element, options) {
+ if(!element.hasChildNodes()) return null;
+ var elements = [];
+ var children = element.childNodes;
+ for(var i = 0; i<children.length; i++) {
+ if(children[i].tagName && children[i].tagName==options.tag.toUpperCase() &&
+ (!options.only || (Element.Class.has(children[i], options.only))))
+ elements.push(children[i]);
+ if(options.tree) {
+ var grandchildren = this.findElements(children[i], options);
+ if(grandchildren) elements.push(grandchildren);
+ }
+ }
+
+ return (elements.length>0 ? elements.flatten() : null);
+ },
+
+ onHover: function(element, dropon, overlap) {
+ if(overlap>0.5) {
+ Sortable.mark(dropon, 'before');
+ if(dropon.previousSibling != element) {
+ var oldParentNode = element.parentNode;
+ element.style.visibility = "hidden"; // fix gecko rendering
+ dropon.parentNode.insertBefore(element, dropon);
+ if(dropon.parentNode!=oldParentNode)
+ Sortable.options(oldParentNode).onChange(element);
+ Sortable.options(dropon.parentNode).onChange(element);
+ }
+ } else {
+ Sortable.mark(dropon, 'after');
+ var nextElement = dropon.nextSibling || null;
+ if(nextElement != element) {
+ var oldParentNode = element.parentNode;
+ element.style.visibility = "hidden"; // fix gecko rendering
+ dropon.parentNode.insertBefore(element, nextElement);
+ if(dropon.parentNode!=oldParentNode)
+ Sortable.options(oldParentNode).onChange(element);
+ Sortable.options(dropon.parentNode).onChange(element);
+ }
+ }
+ },
+
+ onEmptyHover: function(element, dropon) {
+ if(element.parentNode!=dropon) {
+ dropon.appendChild(element);
+ }
+ },
+
+ unmark: function() {
+ if(Sortable._marker) Element.hide(Sortable._marker);
+ },
+
+ mark: function(dropon, position) {
+ // mark on ghosting only
+ var sortable = Sortable.options(dropon.parentNode);
+ if(sortable && !sortable.ghosting) return;
+
+ if(!Sortable._marker) {
+ Sortable._marker = $('dropmarker') || document.createElement('DIV');
+ Element.hide(Sortable._marker);
+ Element.Class.add(Sortable._marker, 'dropmarker');
+ Sortable._marker.style.position = 'absolute';
+ document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
+ }
+ var offsets = Position.cumulativeOffset(dropon);
+ Sortable._marker.style.top = offsets[1] + 'px';
+ if(position=='after') Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
+ Sortable._marker.style.left = offsets[0] + 'px';
+ Element.show(Sortable._marker);
+ },
+
+ serialize: function(element) {
+ element = $(element);
+ var sortableOptions = this.options(element);
+ var options = Object.extend({
+ tag: sortableOptions.tag,
+ only: sortableOptions.only,
+ name: element.id
+ }, arguments[1] || {});
+
+ var items = $(element).childNodes;
+ var queryComponents = new Array();
+
+ for(var i=0; i<items.length; i++)
+ if(items[i].tagName && items[i].tagName==options.tag.toUpperCase() &&
+ (!options.only || (Element.Class.has(items[i], options.only))))
+ queryComponents.push(
+ encodeURIComponent(options.name) + "[]=" +
+ encodeURIComponent(items[i].id.split("_")[1]));
+
+ return queryComponents.join("&");
+ }
+}
\ No newline at end of file
--- /dev/null
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// Parts (c) 2005 Justin Palmer (http://encytemedia.com/)
+// Parts (c) 2005 Mark Pilgrim (http://diveintomark.org/)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+var ScriptaculousEffect = {
+ tagifyText: function(element) {
+ var tagifyStyle = "position:relative";
+ if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ";zoom:1";
+ element = $(element);
+ var children = element.childNodes;
+ for (var i = 0; i < children.length; i++)
+ if(children[i].nodeType==3) {
+ var child = children[i];
+ for (var j = 0; j < child.nodeValue.length; j++)
+ element.insertBefore(
+ Builder.node('span',{style: tagifyStyle},
+ child.nodeValue.substr(j,1) == " " ? String.fromCharCode(160) :
+ child.nodeValue.substr(j,1)), child);
+ Element.remove(child);
+ }
+ },
+ multiple: function(element, effect) {
+ if(((typeof element == 'object') ||
+ (typeof element == 'function')) &&
+ (element.length))
+ var elements = element;
+ else
+ var elements = $(element).childNodes;
+
+ var options = Object.extend({
+ speed: 0.1,
+ delay: 0.0
+ }, arguments[2] || {});
+ var speed = options.speed;
+ var delay = options.delay;
+
+ for(var i = 0; i < elements.length; i++)
+ new effect(elements[i],
+ Object.extend(options, { delay: delay + i*speed }));
+ }
+};
+
+var ScriptaculousEffect2 = ScriptaculousEffect; // deprecated
+
+/* ------------- transitions ------------- */
+
+ScriptaculousEffect.Transitions = {}
+
+ScriptaculousEffect.Transitions.linear = function(pos) {
+ return pos;
+}
+ScriptaculousEffect.Transitions.sinoidal = function(pos) {
+ return (-Math.cos(pos*Math.PI)/2) + 0.5;
+}
+ScriptaculousEffect.Transitions.reverse = function(pos) {
+ return 1-pos;
+}
+ScriptaculousEffect.Transitions.flicker = function(pos) {
+ return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random(0.25);
+}
+ScriptaculousEffect.Transitions.wobble = function(pos) {
+ return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
+}
+ScriptaculousEffect.Transitions.pulse = function(pos) {
+ return (Math.floor(pos*10) % 2 == 0 ?
+ (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10)));
+}
+ScriptaculousEffect.Transitions.none = function(pos) {
+ return 0;
+}
+ScriptaculousEffect.Transitions.full = function(pos) {
+ return 1;
+}
+
+/* ------------- core effects ------------- */
+
+ScriptaculousEffect.Queue = {
+ effects: [],
+ interval: null,
+ findLast: function() {
+ var timestamp = false;
+ for(var i = 0; i < this.effects.length; i++)
+ if(!timestamp || (this.effects[i].finishOn>timestamp))
+ timestamp = this.effects[i].finishOn;
+ return timestamp;
+ },
+ add: function(effect) {
+ var timestamp = new Date().getTime();
+
+ switch(effect.options.queue) {
+ case 'front':
+ // move unstarted effects after this effect
+ for(var i = 0; i < this.effects.length; i++)
+ if(this.effects[i].state == 'idle') {
+ this.effects[i].startOn += effect.finishOn;
+ this.effects[i].finishOn += effect.finishOn;
+ }
+ break;
+ case 'end':
+ // start effect after last queued effect has finished
+ timestamp = this.findLast() || timestamp;
+ break;
+ }
+
+ effect.startOn += timestamp;
+ effect.finishOn += timestamp;
+
+ this.effects.push(effect);
+
+ if(!this.interval)
+ this.interval = setInterval(this.loop.bind(this), 40);
+ },
+ remove: function(effect) {
+ for(var i = 0; i < this.effects.length; i++)
+ if(this.effects[i]==effect) this.effects.splice(i,1);
+ if(this.effects.length == 0) {
+ clearInterval(this.interval);
+ this.interval = null;
+ }
+ },
+ loop: function() {
+ var timePos = new Date().getTime();
+ for(var i = 0; i < this.effects.length; i++) {
+ this.effects[i].loop(timePos);
+ }
+ }
+}
+
+ScriptaculousEffect.Base = function() {};
+ScriptaculousEffect.Base.prototype = {
+ setOptions: function(options) {
+ this.options = Object.extend({
+ transition: ScriptaculousEffect.Transitions.sinoidal,
+ duration: 1.0, // seconds
+ fps: 25.0, // max. 25fps due to ScriptaculousEffect.Queue implementation
+ sync: false, // true for combining
+ from: 0.0,
+ to: 1.0,
+ delay: 0.0,
+ queue: 'parallel'
+ }, options || {});
+ },
+ start: function(options) {
+ this.setOptions(options || {});
+ this.currentFrame = 0;
+ this.state = 'idle';
+ this.startOn = this.options.delay*1000;
+ this.finishOn = this.startOn + (this.options.duration*1000);
+ if(this.options.beforeStart) this.options.beforeStart(this);
+ if(!this.options.sync) ScriptaculousEffect.Queue.add(this);
+ },
+ loop: function(timePos) {
+ if(timePos >= this.startOn) {
+ if(timePos >= this.finishOn) {
+ this.render(1.0);
+ this.cancel();
+ if(this.finish) this.finish();
+ if(this.options.afterFinish) this.options.afterFinish(this);
+ return;
+ }
+ var pos = (timePos - this.startOn) / (this.finishOn - this.startOn);
+ var frame = Math.round(pos * this.options.fps * this.options.duration);
+ if(frame > this.currentFrame) {
+ this.render(pos);
+ this.currentFrame = frame;
+ }
+ }
+ },
+ render: function(pos) {
+ if(this.state == 'idle') {
+ this.state = 'running';
+ if(this.setup) this.setup();
+ }
+ if(this.options.transition) pos = this.options.transition(pos);
+ pos *= (this.options.to-this.options.from);
+ pos += this.options.from;
+ if(this.options.beforeUpdate) this.options.beforeUpdate(this);
+ if(this.update) this.update(pos);
+ if(this.options.afterUpdate) this.options.afterUpdate(this);
+ },
+ cancel: function() {
+ if(!this.options.sync) ScriptaculousEffect.Queue.remove(this);
+ this.state = 'finished';
+ }
+}
+
+ScriptaculousEffect.Parallel = Class.create();
+Object.extend(Object.extend(ScriptaculousEffect.Parallel.prototype, ScriptaculousEffect.Base.prototype), {
+ initialize: function(effects) {
+ this.effects = effects || [];
+ this.start(arguments[1]);
+ },
+ update: function(position) {
+ for (var i = 0; i < this.effects.length; i++)
+ this.effects[i].render(position);
+ },
+ finish: function(position) {
+ for (var i = 0; i < this.effects.length; i++) {
+ this.effects[i].cancel();
+ if(this.effects[i].finish) this.effects[i].finish(position);
+ }
+ }
+});
+
+// Internet Explorer caveat: works only on elements that have
+// a 'layout', meaning having a given width or height.
+// There is no way to safely set this automatically.
+ScriptaculousEffect.Opacity = Class.create();
+Object.extend(Object.extend(ScriptaculousEffect.Opacity.prototype, ScriptaculousEffect.Base.prototype), {
+ initialize: function(element) {
+ this.element = $(element);
+ var options = Object.extend({
+ from: 0.0,
+ to: 1.0
+ }, arguments[1] || {});
+ this.start(options);
+ },
+ update: function(position) {
+ this.setOpacity(position);
+ },
+ setOpacity: function(opacity) {
+ if(opacity<0.0001) opacity = 0; // fix errors with things like 6.152242992829571e-8
+ if(opacity==1.0) {
+ this.element.style.opacity = '0.999999';
+ this.element.style.filter = null;
+ } else {
+ this.element.style.opacity = opacity;
+ this.element.style.filter = "alpha(opacity:"+opacity*100+")";
+ }
+ }
+});
+
+ScriptaculousEffect.MoveBy = Class.create();
+Object.extend(Object.extend(ScriptaculousEffect.MoveBy.prototype, ScriptaculousEffect.Base.prototype), {
+ initialize: function(element, toTop, toLeft) {
+ this.element = $(element);
+ this.toTop = toTop;
+ this.toLeft = toLeft;
+ this.start(arguments[3]);
+ },
+ setup: function() {
+ this.originalTop = parseFloat(Element.getStyle(this.element,'top') || '0');
+ this.originalLeft = parseFloat(Element.getStyle(this.element,'left') || '0');
+ Element.makePositioned(this.element);
+ },
+ update: function(position) {
+ topd = this.toTop * position + this.originalTop;
+ leftd = this.toLeft * position + this.originalLeft;
+ this.setPosition(topd, leftd);
+ },
+ setPosition: function(topd, leftd) {
+ this.element.style.top = topd + "px";
+ this.element.style.left = leftd + "px";
+ }
+});
+
+ScriptaculousEffect.Scale = Class.create();
+Object.extend(Object.extend(ScriptaculousEffect.Scale.prototype, ScriptaculousEffect.Base.prototype), {
+ initialize: function(element, percent) {
+ this.element = $(element)
+ var options = Object.extend({
+ scaleX: true,
+ scaleY: true,
+ scaleContent: true,
+ scaleFromCenter: false,
+ scaleMode: 'box', // 'box' or 'contents' or {} with provided values
+ scaleFrom: 100.0,
+ scaleTo: percent
+ }, arguments[2] || {});
+ this.start(options);
+ },
+ setup: function() {
+ this.originalTop = this.element.offsetTop;
+ this.originalLeft = this.element.offsetLeft;
+ if(Element.getStyle(this.element,'font-size')=="") this.sizeEm = 1.0;
+ if(Element.getStyle(this.element,'font-size') && Element.getStyle(this.element,'font-size').indexOf("em")>0)
+ this.sizeEm = parseFloat(Element.getStyle(this.element,'font-size'));
+ this.factor = (this.options.scaleTo/100.0) - (this.options.scaleFrom/100.0);
+ if(this.options.scaleMode=='box') {
+ this.originalHeight = this.element.clientHeight;
+ this.originalWidth = this.element.clientWidth;
+ } else
+ if(this.options.scaleMode=='contents') {
+ this.originalHeight = this.element.scrollHeight;
+ this.originalWidth = this.element.scrollWidth;
+ } else {
+ this.originalHeight = this.options.scaleMode.originalHeight;
+ this.originalWidth = this.options.scaleMode.originalWidth;
+ }
+ },
+ update: function(position) {
+ var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
+ if(this.options.scaleContent && this.sizeEm)
+ this.element.style.fontSize = this.sizeEm*currentScale + "em";
+ this.setDimensions(
+ this.originalWidth * currentScale,
+ this.originalHeight * currentScale);
+ },
+ setDimensions: function(width, height) {
+ if(this.options.scaleX) this.element.style.width = width + 'px';
+ if(this.options.scaleY) this.element.style.height = height + 'px';
+ if(this.options.scaleFromCenter) {
+ var topd = (height - this.originalHeight)/2;
+ var leftd = (width - this.originalWidth)/2;
+ if(Element.getStyle(this.element,'position')=='absolute') {
+ if(this.options.scaleY) this.element.style.top = this.originalTop-topd + "px";
+ if(this.options.scaleX) this.element.style.left = this.originalLeft-leftd + "px";
+ } else {
+ if(this.options.scaleY) this.element.style.top = -topd + "px";
+ if(this.options.scaleX) this.element.style.left = -leftd + "px";
+ }
+ }
+ }
+});
+
+ScriptaculousEffect.Highlight = Class.create();
+Object.extend(Object.extend(ScriptaculousEffect.Highlight.prototype, ScriptaculousEffect.Base.prototype), {
+ initialize: function(element) {
+ this.element = $(element);
+ var options = Object.extend({
+ startcolor: "#ffff99"
+ }, arguments[1] || {});
+ this.start(options);
+ },
+ setup: function() {
+ // try to parse current background color as default for endcolor
+ // browser stores this as: "rgb(255, 255, 255)", convert to "#ffffff" format
+ if(!this.options.endcolor) {
+ var endcolor = "#ffffff";
+ var current = Element.getStyle(this.element, 'background-color');
+ if(current && current.slice(0,4) == "rgb(") {
+ endcolor = "#";
+ var cols = current.slice(4,current.length-1).split(',');
+ var i=0; do { endcolor += parseInt(cols[i]).toColorPart() } while (++i<3);
+ }
+ this.options.endcolor = endcolor;
+ }
+ // init color calculations
+ this.colors_base = [
+ parseInt(this.options.startcolor.slice(1,3),16),
+ parseInt(this.options.startcolor.slice(3,5),16),
+ parseInt(this.options.startcolor.slice(5),16) ];
+ this.colors_delta = [
+ parseInt(this.options.endcolor.slice(1,3),16)-this.colors_base[0],
+ parseInt(this.options.endcolor.slice(3,5),16)-this.colors_base[1],
+ parseInt(this.options.endcolor.slice(5),16)-this.colors_base[2]];
+ },
+ update: function(position) {
+ var colors = [
+ Math.round(this.colors_base[0]+(this.colors_delta[0]*position)),
+ Math.round(this.colors_base[1]+(this.colors_delta[1]*position)),
+ Math.round(this.colors_base[2]+(this.colors_delta[2]*position)) ];
+ this.element.style.backgroundColor = "#" +
+ colors[0].toColorPart() + colors[1].toColorPart() + colors[2].toColorPart();
+ },
+ finish: function() {
+ this.element.style.backgroundColor = this.options.restorecolor;
+ }
+});
+
+ScriptaculousEffect.ScrollTo = Class.create();
+Object.extend(Object.extend(ScriptaculousEffect.ScrollTo.prototype, ScriptaculousEffect.Base.prototype), {
+ initialize: function(element) {
+ this.element = $(element);
+ this.start(arguments[1] || {});
+ },
+ setup: function() {
+ Position.prepare();
+ var offsets = Position.cumulativeOffset(this.element);
+ var max = window.innerHeight ?
+ window.height - window.innerHeight :
+ document.body.scrollHeight -
+ (document.documentElement.clientHeight ?
+ document.documentElement.clientHeight : document.body.clientHeight);
+ this.scrollStart = Position.deltaY;
+ this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
+ },
+ update: function(position) {
+ Position.prepare();
+ window.scrollTo(Position.deltaX,
+ this.scrollStart + (position*this.delta));
+ }
+});
+
+/* ------------- combination effects ------------- */
+
+ScriptaculousEffect.Fade = function(element) {
+ var options = Object.extend({
+ from: 1.0,
+ to: 0.0,
+ afterFinish: function(effect)
+ { Element.hide(effect.element);
+ effect.setOpacity(1); }
+ }, arguments[1] || {});
+ return new ScriptaculousEffect.Opacity(element,options);
+}
+
+ScriptaculousEffect.Appear = function(element) {
+ var options = Object.extend({
+ from: 0.0,
+ to: 1.0,
+ beforeStart: function(effect)
+ { effect.setOpacity(0);
+ Element.show(effect.element); },
+ afterUpdate: function(effect)
+ { Element.show(effect.element); }
+ }, arguments[1] || {});
+ return new ScriptaculousEffect.Opacity(element,options);
+}
+
+ScriptaculousEffect.Puff = function(element) {
+ return new ScriptaculousEffect.Parallel(
+ [ new ScriptaculousEffect.Scale(element, 200, { sync: true, scaleFromCenter: true }),
+ new ScriptaculousEffect.Opacity(element, { sync: true, to: 0.0, from: 1.0 } ) ],
+ Object.extend({ duration: 1.0,
+ beforeUpdate: function(effect)
+ { effect.effects[0].element.style.position = 'absolute'; },
+ afterFinish: function(effect)
+ { Element.hide(effect.effects[0].element); }
+ }, arguments[1] || {})
+ );
+}
+
+ScriptaculousEffect.BlindUp = function(element) {
+ element = $(element);
+ Element.makeClipping(element);
+ return new ScriptaculousEffect.Scale(element, 0,
+ Object.extend({ scaleContent: false,
+ scaleX: false,
+ afterFinish: function(effect)
+ {
+ Element.hide(effect.element);
+ Element.undoClipping(effect.element);
+ }
+ }, arguments[1] || {})
+ );
+}
+
+ScriptaculousEffect.BlindDown = function(element) {
+ element = $(element);
+ element.style.height = '0px';
+ Element.makeClipping(element);
+ Element.show(element);
+ return new ScriptaculousEffect.Scale(element, 100,
+ Object.extend({ scaleContent: false,
+ scaleX: false,
+ scaleMode: 'contents',
+ scaleFrom: 0,
+ afterFinish: function(effect) {
+ Element.undoClipping(effect.element);
+ }
+ }, arguments[1] || {})
+ );
+}
+
+ScriptaculousEffect.SwitchOff = function(element) {
+ return new ScriptaculousEffect.Appear(element,
+ { duration: 0.4,
+ transition: ScriptaculousEffect.Transitions.flicker,
+ afterFinish: function(effect)
+ { effect.element.style.overflow = 'hidden';
+ new ScriptaculousEffect.Scale(effect.element, 1,
+ { duration: 0.3, scaleFromCenter: true,
+ scaleX: false, scaleContent: false,
+ afterUpdate: function(effect) {
+ if(effect.element.style.position=="")
+ effect.element.style.position = 'relative'; },
+ afterFinish: function(effect) { Element.hide(effect.element); }
+ } )
+ }
+ } );
+}
+
+ScriptaculousEffect.DropOut = function(element) {
+ return new ScriptaculousEffect.Parallel(
+ [ new ScriptaculousEffect.MoveBy(element, 100, 0, { sync: true }),
+ new ScriptaculousEffect.Opacity(element, { sync: true, to: 0.0, from: 1.0 } ) ],
+ Object.extend(
+ { duration: 0.5,
+ afterFinish: function(effect)
+ { Element.hide(effect.effects[0].element); }
+ }, arguments[1] || {}));
+}
+
+ScriptaculousEffect.Shake = function(element) {
+ return new ScriptaculousEffect.MoveBy(element, 0, 20,
+ { duration: 0.05, afterFinish: function(effect) {
+ new ScriptaculousEffect.MoveBy(effect.element, 0, -40,
+ { duration: 0.1, afterFinish: function(effect) {
+ new ScriptaculousEffect.MoveBy(effect.element, 0, 40,
+ { duration: 0.1, afterFinish: function(effect) {
+ new ScriptaculousEffect.MoveBy(effect.element, 0, -40,
+ { duration: 0.1, afterFinish: function(effect) {
+ new ScriptaculousEffect.MoveBy(effect.element, 0, 40,
+ { duration: 0.1, afterFinish: function(effect) {
+ new ScriptaculousEffect.MoveBy(effect.element, 0, -20,
+ { duration: 0.05, afterFinish: function(effect) {
+ }}) }}) }}) }}) }}) }});
+}
+
+ScriptaculousEffect.SlideDown = function(element) {
+ element = $(element);
+ element.style.height = '0px';
+ Element.makeClipping(element);
+ Element.cleanWhitespace(element);
+ Element.makePositioned(element.firstChild);
+ Element.show(element);
+ return new ScriptaculousEffect.Scale(element, 100,
+ Object.extend({ scaleContent: false,
+ scaleX: false,
+ scaleMode: 'contents',
+ scaleFrom: 0,
+ afterUpdate: function(effect)
+ { effect.element.firstChild.style.bottom =
+ (effect.originalHeight - effect.element.clientHeight) + 'px'; },
+ afterFinish: function(effect)
+ { Element.undoClipping(effect.element); }
+ }, arguments[1] || {})
+ );
+}
+
+ScriptaculousEffect.SlideUp = function(element) {
+ element = $(element);
+ Element.makeClipping(element);
+ Element.cleanWhitespace(element);
+ Element.makePositioned(element.firstChild);
+ Element.show(element);
+ return new ScriptaculousEffect.Scale(element, 0,
+ Object.extend({ scaleContent: false,
+ scaleX: false,
+ afterUpdate: function(effect)
+ { effect.element.firstChild.style.bottom =
+ (effect.originalHeight - effect.element.clientHeight) + 'px'; },
+ afterFinish: function(effect)
+ {
+ Element.hide(effect.element);
+ Element.undoClipping(effect.element);
+ }
+ }, arguments[1] || {})
+ );
+}
+
+ScriptaculousEffect.Squish = function(element) {
+ return new ScriptaculousEffect.Scale(element, 0,
+ { afterFinish: function(effect) { Element.hide(effect.element); } });
+}
+
+ScriptaculousEffect.Grow = function(element) {
+ element = $(element);
+ var options = arguments[1] || {};
+
+ var originalWidth = element.clientWidth;
+ var originalHeight = element.clientHeight;
+ element.style.overflow = 'hidden';
+ Element.show(element);
+
+ var direction = options.direction || 'center';
+ var moveTransition = options.moveTransition || ScriptaculousEffect.Transitions.sinoidal;
+ var scaleTransition = options.scaleTransition || ScriptaculousEffect.Transitions.sinoidal;
+ var opacityTransition = options.opacityTransition || ScriptaculousEffect.Transitions.full;
+
+ var initialMoveX, initialMoveY;
+ var moveX, moveY;
+
+ switch (direction) {
+ case 'top-left':
+ initialMoveX = initialMoveY = moveX = moveY = 0;
+ break;
+ case 'top-right':
+ initialMoveX = originalWidth;
+ initialMoveY = moveY = 0;
+ moveX = -originalWidth;
+ break;
+ case 'bottom-left':
+ initialMoveX = moveX = 0;
+ initialMoveY = originalHeight;
+ moveY = -originalHeight;
+ break;
+ case 'bottom-right':
+ initialMoveX = originalWidth;
+ initialMoveY = originalHeight;
+ moveX = -originalWidth;
+ moveY = -originalHeight;
+ break;
+ case 'center':
+ initialMoveX = originalWidth / 2;
+ initialMoveY = originalHeight / 2;
+ moveX = -originalWidth / 2;
+ moveY = -originalHeight / 2;
+ break;
+ }
+
+ return new ScriptaculousEffect.MoveBy(element, initialMoveY, initialMoveX, {
+ duration: 0.01,
+ beforeUpdate: function(effect) { $(element).style.height = '0px'; },
+ afterFinish: function(effect) {
+ new ScriptaculousEffect.Parallel(
+ [ new ScriptaculousEffect.Opacity(element, { sync: true, to: 1.0, from: 0.0, transition: opacityTransition }),
+ new ScriptaculousEffect.MoveBy(element, moveY, moveX, { sync: true, transition: moveTransition }),
+ new ScriptaculousEffect.Scale(element, 100, {
+ scaleMode: { originalHeight: originalHeight, originalWidth: originalWidth },
+ sync: true, scaleFrom: 0, scaleTo: 100, transition: scaleTransition })],
+ options); }
+ });
+}
+
+ScriptaculousEffect.Shrink = function(element) {
+ element = $(element);
+ var options = arguments[1] || {};
+
+ var originalWidth = element.clientWidth;
+ var originalHeight = element.clientHeight;
+ element.style.overflow = 'hidden';
+ Element.show(element);
+
+ var direction = options.direction || 'center';
+ var moveTransition = options.moveTransition || ScriptaculousEffect.Transitions.sinoidal;
+ var scaleTransition = options.scaleTransition || ScriptaculousEffect.Transitions.sinoidal;
+ var opacityTransition = options.opacityTransition || ScriptaculousEffect.Transitions.none;
+
+ var moveX, moveY;
+
+ switch (direction) {
+ case 'top-left':
+ moveX = moveY = 0;
+ break;
+ case 'top-right':
+ moveX = originalWidth;
+ moveY = 0;
+ break;
+ case 'bottom-left':
+ moveX = 0;
+ moveY = originalHeight;
+ break;
+ case 'bottom-right':
+ moveX = originalWidth;
+ moveY = originalHeight;
+ break;
+ case 'center':
+ moveX = originalWidth / 2;
+ moveY = originalHeight / 2;
+ break;
+ }
+
+ return new ScriptaculousEffect.Parallel(
+ [ new ScriptaculousEffect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: opacityTransition }),
+ new ScriptaculousEffect.Scale(element, 0, { sync: true, transition: moveTransition }),
+ new ScriptaculousEffect.MoveBy(element, moveY, moveX, { sync: true, transition: scaleTransition }) ],
+ options);
+}
+
+ScriptaculousEffect.Pulsate = function(element) {
+ element = $(element);
+ var options = arguments[1] || {};
+ var transition = options.transition || ScriptaculousEffect.Transitions.sinoidal;
+ var reverser = function(pos){ return transition(1-ScriptaculousEffect.Transitions.pulse(pos)) };
+ reverser.bind(transition);
+ return new ScriptaculousEffect.Opacity(element,
+ Object.extend(Object.extend({ duration: 3.0,
+ afterFinish: function(effect) { Element.show(effect.element); }
+ }, options), {transition: reverser}));
+}
+
+ScriptaculousEffect.Fold = function(element) {
+ element = $(element);
+ element.style.overflow = 'hidden';
+ return new ScriptaculousEffect.Scale(element, 5, Object.extend({
+ scaleContent: false,
+ scaleTo: 100,
+ scaleX: false,
+ afterFinish: function(effect) {
+ new ScriptaculousEffect.Scale(element, 1, {
+ scaleContent: false,
+ scaleTo: 0,
+ scaleY: false,
+ afterFinish: function(effect) { Element.hide(effect.element) } });
+ }}, arguments[1] || {}));
+}
+
+// old: new ScriptaculousEffect.ContentZoom(element, percent)
+// new: Element.setContentZoom(element, percent)
+
+Element.setContentZoom = function(element, percent) {
+ element = $(element);
+ element.style.fontSize = (percent/100) + "em";
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+}
<script type="text/javascript" src="static/wclib.js"></script>
<script type="text/javascript" src="static/prototype.js"></script>
<script type="text/javascript" src="static/rico.js"></script>
+
+<script type="text/javascript" src="static/scriptaculous.js"></script>
+
</head>
<body>
-/* Prototype: an object-oriented Javascript library, version 1.2.1
+/* Prototype JavaScript framework, version 1.4.0_pre4
* (c) 2005 Sam Stephenson <sam@conio.net>
*
* THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff
/*--------------------------------------------------------------------------*/
var Prototype = {
- Version: '1.2.1'
+ Version: '1.4.0_pre4',
+
+ emptyFunction: function() {},
+ K: function(x) {return x}
}
var Class = {
var Abstract = new Object();
-Object.prototype.extend = function(object) {
- for (property in object) {
- this[property] = object[property];
+Object.extend = function(destination, source) {
+ for (property in source) {
+ destination[property] = source[property];
}
- return this;
+ return destination;
}
Function.prototype.bind = function(object) {
- var method = this;
+ var __method = this;
return function() {
- method.apply(object, arguments);
+ return __method.apply(object, arguments);
}
}
Function.prototype.bindAsEventListener = function(object) {
- var method = this;
+ var __method = this;
return function(event) {
- method.call(object, event || window.event);
+ return __method.call(object, event || window.event);
}
}
var Try = {
these: function() {
var returnValue;
-
+
for (var i = 0; i < arguments.length; i++) {
var lambda = arguments[i];
try {
break;
} catch (e) {}
}
-
+
return returnValue;
}
}
this.callback = callback;
this.frequency = frequency;
this.currentlyExecuting = false;
-
+
this.registerCallback();
},
-
+
registerCallback: function() {
- setTimeout(this.onTimerEvent.bind(this), this.frequency * 1000);
+ setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
},
-
+
onTimerEvent: function() {
if (!this.currentlyExecuting) {
try {
this.currentlyExecuting = false;
}
}
-
- this.registerCallback();
}
}
function $() {
var elements = new Array();
-
+
for (var i = 0; i < arguments.length; i++) {
var element = arguments[i];
if (typeof element == 'string')
if (arguments.length == 1)
return element;
-
+
elements.push(element);
}
-
+
return elements;
}
-/*--------------------------------------------------------------------------*/
-
if (!Array.prototype.push) {
Array.prototype.push = function() {
var startLength = this.length;
if (!parameters) parameters = new Array();
for (var i = 0; i < parameters.length; i++)
- parameterStrings[i] = 'x[' + i + ']';
+ parameterStrings[i] = 'parameters[' + i + ']';
object.__apply__ = this;
- var result = eval('obj.__apply__(' +
- parameterStrings[i].join(', ') + ')');
+ var result = eval('object.__apply__(' +
+ parameterStrings.join(', ') + ')');
object.__apply__ = null;
return result;
}
}
-/*--------------------------------------------------------------------------*/
+Object.extend(String.prototype, {
+ stripTags: function() {
+ return this.replace(/<\/?[^>]+>/gi, '');
+ },
+
+ escapeHTML: function() {
+ var div = document.createElement('div');
+ var text = document.createTextNode(this);
+ div.appendChild(text);
+ return div.innerHTML;
+ },
+
+ unescapeHTML: function() {
+ var div = document.createElement('div');
+ div.innerHTML = this.stripTags();
+ return div.childNodes[0].nodeValue;
+ },
+
+ parseQuery: function() {
+ var str = this;
+ if (str.substring(0,1) == '?') {
+ str = this.substring(1);
+ }
+ var result = {};
+ var pairs = str.split('&');
+ for (var i = 0; i < pairs.length; i++) {
+ var pair = pairs[i].split('=');
+ result[pair[0]] = pair[1];
+ }
+ return result;
+ }
+});
+
+
+var _break = new Object();
+var _continue = new Object();
+
+var Enumerable = {
+ each: function(iterator) {
+ var index = 0;
+ try {
+ this._each(function(value) {
+ try {
+ iterator(value, index++);
+ } catch (e) {
+ if (e != _continue) throw e;
+ }
+ });
+ } catch (e) {
+ if (e != _break) throw e;
+ }
+ },
+
+ all: function(iterator) {
+ var result = true;
+ this.each(function(value, index) {
+ if (!(result &= (iterator || Prototype.K)(value, index)))
+ throw _break;
+ });
+ return result;
+ },
+
+ any: function(iterator) {
+ var result = true;
+ this.each(function(value, index) {
+ if (result &= (iterator || Prototype.K)(value, index))
+ throw _break;
+ });
+ return result;
+ },
+
+ collect: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ results.push(iterator(value, index));
+ });
+ return results;
+ },
+
+ detect: function (iterator) {
+ var result;
+ this.each(function(value, index) {
+ if (iterator(value, index)) {
+ result = value;
+ throw _break;
+ }
+ });
+ return result;
+ },
+
+ findAll: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ if (iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ grep: function(pattern, iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ var stringValue = value.toString();
+ if (stringValue.match(pattern))
+ results.push((iterator || Prototype.K)(value, index));
+ })
+ return results;
+ },
+
+ include: function(object) {
+ var found = false;
+ this.each(function(value) {
+ if (value == object) {
+ found = true;
+ throw _break;
+ }
+ });
+ return found;
+ },
+
+ inject: function(memo, iterator) {
+ this.each(function(value, index) {
+ memo = iterator(memo, value, index);
+ });
+ return memo;
+ },
+
+ invoke: function(method) {
+ var args = $A(arguments).slice(1);
+ return this.collect(function(value) {
+ return value[method].apply(value, args);
+ });
+ },
+
+ max: function(iterator) {
+ var result;
+ this.each(function(value, index) {
+ value = (iterator || Prototype.K)(value, index);
+ if (value >= (result || value))
+ result = value;
+ });
+ return result;
+ },
+
+ min: function(iterator) {
+ var result;
+ this.each(function(value, index) {
+ value = (iterator || Prototype.K)(value, index);
+ if (value <= (result || value))
+ result = value;
+ });
+ return result;
+ },
+
+ partition: function(iterator) {
+ var trues = [], falses = [];
+ this.each(function(value, index) {
+ ((iterator || Prototype.K)(value, index) ?
+ trues : falses).push(value);
+ });
+ return [trues, falses];
+ },
+
+ pluck: function(property) {
+ var results = [];
+ this.each(function(value, index) {
+ results.push(value[property]);
+ });
+ return results;
+ },
+
+ reject: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ if (!iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ sortBy: function(iterator) {
+ return this.collect(function(value, index) {
+ return {value: value, criteria: iterator(value, index)};
+ }).sort(function(left, right) {
+ var a = left.criteria, b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ }).pluck('value');
+ },
+
+ toArray: function() {
+ return this.collect(Prototype.K);
+ },
+
+ zip: function() {
+ var iterator = Prototype.K, args = $A(arguments);
+ if (typeof args.last() == 'function')
+ iterator = args.pop();
+
+ var collections = [this].concat(args).map($A);
+ return this.map(function(value, index) {
+ iterator(value = collections.pluck(index));
+ return value;
+ });
+ }
+}
+
+Object.extend(Enumerable, {
+ map: Enumerable.collect,
+ find: Enumerable.detect,
+ select: Enumerable.findAll,
+ member: Enumerable.include,
+ entries: Enumerable.toArray
+});
+
+$A = Array.from = function(iterable) {
+ var results = [];
+ for (var i = 0; i < iterable.length; i++)
+ results.push(iterable[i]);
+ return results;
+}
+
+Object.extend(Array.prototype, {
+ _each: function(iterator) {
+ for (var i = 0; i < this.length; i++)
+ iterator(this[i]);
+ },
+
+ first: function() {
+ return this[0];
+ },
+
+ last: function() {
+ return this[this.length - 1];
+ }
+});
+
+Object.extend(Array.prototype, Enumerable);
+
var Ajax = {
getTransport: function() {
function() {return new ActiveXObject('Microsoft.XMLHTTP')},
function() {return new XMLHttpRequest()}
) || false;
- },
-
- emptyFunction: function() {}
+ }
}
Ajax.Base = function() {};
method: 'post',
asynchronous: true,
parameters: ''
- }.extend(options || {});
+ }
+ Object.extend(this.options, options || {});
+ },
+
+ responseIsSuccess: function() {
+ return this.transport.status == undefined
+ || this.transport.status == 0
+ || (this.transport.status >= 200 && this.transport.status < 300);
+ },
+
+ responseIsFailure: function() {
+ return !this.responseIsSuccess();
}
}
Ajax.Request.Events =
['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
-Ajax.Request.prototype = (new Ajax.Base()).extend({
+Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
initialize: function(url, options) {
this.transport = Ajax.getTransport();
this.setOptions(options);
-
+ this.request(url);
+ },
+
+ request: function(url) {
+ var parameters = this.options.parameters || '';
+ if (parameters.length > 0) parameters += '&_=';
+
try {
if (this.options.method == 'get')
- url += '?' + this.options.parameters + '&_=';
-
+ url += '?' + parameters;
+
this.transport.open(this.options.method, url,
this.options.asynchronous);
-
+
if (this.options.asynchronous) {
this.transport.onreadystatechange = this.onStateChange.bind(this);
setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
}
-
- this.transport.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
- this.transport.setRequestHeader('X-Prototype-Version',
- Prototype.Version);
-
- if (this.options.method == 'post') {
- this.transport.setRequestHeader('Connection', 'close');
- this.transport.setRequestHeader('Content-type',
- 'application/x-www-form-urlencoded');
- }
- if (this.options.requestHeaders) {
- for (var i=0; i< (this.options.requestHeaders.length-1);i+=2)
- this.transport.setRequestHeader(this.options.requestHeaders[i],
- this.options.requestHeaders[i+1]);
- }
+ this.setRequestHeaders();
- var sendData = this.options.postBody ? this.options.postBody
- : this.options.parameters ? this.options.parameters + '&_'
- : null;
+ var body = this.options.postBody ? this.options.postBody : parameters;
+ this.transport.send(this.options.method == 'post' ? body : null);
- this.transport.send(this.options.method == 'post' ? sendData : null );
-
} catch (e) {
- }
+ }
},
-
+
+ setRequestHeaders: function() {
+ var requestHeaders =
+ ['X-Requested-With', 'XMLHttpRequest',
+ 'X-Prototype-Version', Prototype.Version];
+
+ if (this.options.method == 'post') {
+ requestHeaders.push('Content-type',
+ 'application/x-www-form-urlencoded');
+
+ /* Force "Connection: close" for Mozilla browsers to work around
+ * a bug where XMLHttpReqeuest sends an incorrect Content-length
+ * header. See Mozilla Bugzilla #246651.
+ */
+ if (this.transport.overrideMimeType)
+ requestHeaders.push('Connection', 'close');
+ }
+
+ if (this.options.requestHeaders)
+ requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
+
+ for (var i = 0; i < requestHeaders.length; i += 2)
+ this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
+ },
+
onStateChange: function() {
var readyState = this.transport.readyState;
if (readyState != 1)
this.respondToReadyState(this.transport.readyState);
},
-
+
respondToReadyState: function(readyState) {
var event = Ajax.Request.Events[readyState];
- (this.options['on' + event] || Ajax.emptyFunction)(this.transport);
+
+ if (event == 'Complete')
+ (this.options['on' + this.transport.status]
+ || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
+ || Prototype.emptyFunction)(this.transport);
+
+ (this.options['on' + event] || Prototype.emptyFunction)(this.transport);
+
+ /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
+ if (event == 'Complete')
+ this.transport.onreadystatechange = Prototype.emptyFunction;
}
});
Ajax.Updater = Class.create();
-Ajax.Updater.prototype = (new Ajax.Base()).extend({
+Ajax.Updater.ScriptFragment = '(?:<script.*?>)((\n|.)*?)(?:<\/script>)';
+
+Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
initialize: function(container, url, options) {
- this.container = $(container);
- this.setOptions(options);
-
- if (this.options.asynchronous) {
- this.onComplete = this.options.onComplete;
- this.options.onComplete = this.updateContent.bind(this);
+ this.containers = {
+ success: container.success ? $(container.success) : $(container),
+ failure: container.failure ? $(container.failure) :
+ (container.success ? null : $(container))
}
- this.request = new Ajax.Request(url, this.options);
-
- if (!this.options.asynchronous)
+ this.transport = Ajax.getTransport();
+ this.setOptions(options);
+
+ var onComplete = this.options.onComplete || Prototype.emptyFunction;
+ this.options.onComplete = (function() {
this.updateContent();
+ onComplete(this.transport);
+ }).bind(this);
+
+ this.request(url);
},
-
+
updateContent: function() {
- if (this.request.transport.status == 200) {
+ var receiver = this.responseIsSuccess() ?
+ this.containers.success : this.containers.failure;
+
+ var match = new RegExp(Ajax.Updater.ScriptFragment, 'img');
+ var response = this.transport.responseText.replace(match, '');
+ var scripts = this.transport.responseText.match(match);
+
+ if (receiver) {
if (this.options.insertion) {
- new this.options.insertion(this.container,
- this.request.transport.responseText);
+ new this.options.insertion(receiver, response);
} else {
- this.container.innerHTML = this.request.transport.responseText;
+ receiver.innerHTML = response;
}
- }
+ }
+
+ if (this.responseIsSuccess()) {
+ if (this.onComplete)
+ setTimeout((function() {this.onComplete(
+ this.transport)}).bind(this), 10);
+ }
+
+ if (this.options.evalScripts && scripts) {
+ match = new RegExp(Ajax.Updater.ScriptFragment, 'im');
+ setTimeout((function() {
+ for (var i = 0; i < scripts.length; i++)
+ eval(scripts[i].match(match)[1]);
+ }).bind(this), 10);
+ }
+ }
+});
+
+Ajax.PeriodicalUpdater = Class.create();
+Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
+ initialize: function(container, url, options) {
+ this.setOptions(options);
+ this.onComplete = this.options.onComplete;
+
+ this.frequency = (this.options.frequency || 2);
+ this.decay = 1;
+
+ this.updater = {};
+ this.container = container;
+ this.url = url;
+
+ this.start();
+ },
+
+ start: function() {
+ this.options.onComplete = this.updateComplete.bind(this);
+ this.onTimerEvent();
+ },
+
+ stop: function() {
+ this.updater.onComplete = undefined;
+ clearTimeout(this.timer);
+ (this.onComplete || Ajax.emptyFunction).apply(this, arguments);
+ },
+
+ updateComplete: function(request) {
+ if (this.options.decay) {
+ this.decay = (request.responseText == this.lastText ?
+ this.decay * this.options.decay : 1);
- if (this.onComplete) {
- setTimeout((function() {this.onComplete(
- this.request.transport)}).bind(this), 10);
+ this.lastText = request.responseText;
}
+ this.timer = setTimeout(this.onTimerEvent.bind(this),
+ this.decay * this.frequency * 1000);
+ },
+
+ onTimerEvent: function() {
+ this.updater = new Ajax.Updater(this.container, this.url, this.options);
}
});
+document.getElementsByClassName = function(className) {
+ var children = document.getElementsByTagName('*') || document.all;
+ var elements = new Array();
+
+ for (var i = 0; i < children.length; i++) {
+ var child = children[i];
+ var classNames = child.className.split(' ');
+ for (var j = 0; j < classNames.length; j++) {
+ if (classNames[j] == className) {
+ elements.push(child);
+ break;
+ }
+ }
+ }
+
+ return elements;
+}
+
/*--------------------------------------------------------------------------*/
+if (!window.Element) {
+ var Element = new Object();
+}
+
+Object.extend(Element, {
+ toggle: function() {
+ for (var i = 0; i < arguments.length; i++) {
+ var element = $(arguments[i]);
+ element.style.display =
+ (element.style.display == 'none' ? '' : 'none');
+ }
+ },
+
+ hide: function() {
+ for (var i = 0; i < arguments.length; i++) {
+ var element = $(arguments[i]);
+ element.style.display = 'none';
+ }
+ },
+
+ show: function() {
+ for (var i = 0; i < arguments.length; i++) {
+ var element = $(arguments[i]);
+ element.style.display = '';
+ }
+ },
+
+ remove: function(element) {
+ element = $(element);
+ element.parentNode.removeChild(element);
+ },
+
+ getHeight: function(element) {
+ element = $(element);
+ return element.offsetHeight;
+ },
+
+ hasClassName: function(element, className) {
+ element = $(element);
+ if (!element)
+ return;
+ var a = element.className.split(' ');
+ for (var i = 0; i < a.length; i++) {
+ if (a[i] == className)
+ return true;
+ }
+ return false;
+ },
+
+ addClassName: function(element, className) {
+ element = $(element);
+ Element.removeClassName(element, className);
+ element.className += ' ' + className;
+ },
+
+ removeClassName: function(element, className) {
+ element = $(element);
+ if (!element)
+ return;
+ var newClassName = '';
+ var a = element.className.split(' ');
+ for (var i = 0; i < a.length; i++) {
+ if (a[i] != className) {
+ if (i > 0)
+ newClassName += ' ';
+ newClassName += a[i];
+ }
+ }
+ element.className = newClassName;
+ },
+
+ // removes whitespace-only text node children
+ cleanWhitespace: function(element) {
+ var element = $(element);
+ for (var i = 0; i < element.childNodes.length; i++) {
+ var node = element.childNodes[i];
+ if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+ Element.remove(node);
+ }
+ }
+});
+
+var Toggle = new Object();
+Toggle.display = Element.toggle;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.Insertion = function(adjacency) {
+ this.adjacency = adjacency;
+}
+
+Abstract.Insertion.prototype = {
+ initialize: function(element, content) {
+ this.element = $(element);
+ this.content = content;
+
+ if (this.adjacency && this.element.insertAdjacentHTML) {
+ this.element.insertAdjacentHTML(this.adjacency, this.content);
+ } else {
+ this.range = this.element.ownerDocument.createRange();
+ if (this.initializeRange) this.initializeRange();
+ this.fragment = this.range.createContextualFragment(this.content);
+ this.insertContent();
+ }
+ }
+}
+
+var Insertion = new Object();
+
+Insertion.Before = Class.create();
+Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
+ initializeRange: function() {
+ this.range.setStartBefore(this.element);
+ },
+
+ insertContent: function() {
+ this.element.parentNode.insertBefore(this.fragment, this.element);
+ }
+});
+
+Insertion.Top = Class.create();
+Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
+ initializeRange: function() {
+ this.range.selectNodeContents(this.element);
+ this.range.collapse(true);
+ },
+
+ insertContent: function() {
+ this.element.insertBefore(this.fragment, this.element.firstChild);
+ }
+});
+
+Insertion.Bottom = Class.create();
+Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
+ initializeRange: function() {
+ this.range.selectNodeContents(this.element);
+ this.range.collapse(this.element);
+ },
+
+ insertContent: function() {
+ this.element.appendChild(this.fragment);
+ }
+});
+
+Insertion.After = Class.create();
+Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
+ initializeRange: function() {
+ this.range.setStartAfter(this.element);
+ },
+
+ insertContent: function() {
+ this.element.parentNode.insertBefore(this.fragment,
+ this.element.nextSibling);
+ }
+});
+
var Field = {
clear: function() {
for (var i = 0; i < arguments.length; i++)
},
getElements: function(form) {
- form = $(form);
+ var form = $(form);
var elements = new Array();
for (tagName in Form.Element.Serializers) {
return elements;
},
+ getInputs: function(form, typeName, name) {
+ var form = $(form);
+ var inputs = form.getElementsByTagName('input');
+
+ if (!typeName && !name)
+ return inputs;
+
+ var matchingInputs = new Array();
+ for (var i = 0; i < inputs.length; i++) {
+ var input = inputs[i];
+ if ((typeName && input.type != typeName) ||
+ (name && input.name != name))
+ continue;
+ matchingInputs.push(input);
+ }
+
+ return matchingInputs;
+ },
+
disable: function(form) {
var elements = Form.getElements(form);
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
element.blur();
- element.disable = 'true';
+ element.disabled = 'true';
+ }
+ },
+
+ enable: function(form) {
+ var elements = Form.getElements(form);
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ element.disabled = '';
}
},
focusFirstElement: function(form) {
- form = $(form);
+ var form = $(form);
var elements = Form.getElements(form);
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
Form.Element = {
serialize: function(element) {
- element = $(element);
+ var element = $(element);
var method = element.tagName.toLowerCase();
var parameter = Form.Element.Serializers[method](element);
},
getValue: function(element) {
- element = $(element);
+ var element = $(element);
var method = element.tagName.toLowerCase();
var parameter = Form.Element.Serializers[method](element);
Form.Element.Serializers = {
input: function(element) {
switch (element.type.toLowerCase()) {
+ case 'submit':
case 'hidden':
case 'password':
case 'text':
},
select: function(element) {
- var index = element.selectedIndex;
- var value = element.options[index].value || element.options[index].text;
- return [element.name, (index >= 0) ? value : ''];
+ var value = '';
+ if (element.type == 'select-one') {
+ var index = element.selectedIndex;
+ if (index >= 0)
+ value = element.options[index].value || element.options[index].text;
+ } else {
+ value = new Array();
+ for (var i = 0; i < element.length; i++) {
+ var opt = element.options[i];
+ if (opt.selected)
+ value.push(opt.value || opt.text);
+ }
+ }
+ return [element.name, value];
}
}
},
registerCallback: function() {
- setTimeout(this.onTimerEvent.bind(this), this.frequency * 1000);
+ setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
},
onTimerEvent: function() {
this.callback(this.element, value);
this.lastValue = value;
}
-
- this.registerCallback();
}
}
Form.Element.Observer = Class.create();
-Form.Element.Observer.prototype = (new Abstract.TimedObserver()).extend({
+Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
getValue: function() {
return Form.Element.getValue(this.element);
}
});
Form.Observer = Class.create();
-Form.Observer.prototype = (new Abstract.TimedObserver()).extend({
+Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
getValue: function() {
return Form.serialize(this.element);
}
});
-
/*--------------------------------------------------------------------------*/
-document.getElementsByClassName = function(className) {
- var children = document.getElementsByTagName('*') || document.all;
- var elements = new Array();
-
- for (var i = 0; i < children.length; i++) {
- var child = children[i];
- var classNames = child.className.split(' ');
- for (var j = 0; j < classNames.length; j++) {
- if (classNames[j] == className) {
- elements.push(child);
- break;
- }
- }
- }
-
- return elements;
-}
-
-/*--------------------------------------------------------------------------*/
-
-var Element = {
- toggle: function() {
- for (var i = 0; i < arguments.length; i++) {
- var element = $(arguments[i]);
- element.style.display =
- (element.style.display == 'none' ? '' : 'none');
- }
- },
-
- hide: function() {
- for (var i = 0; i < arguments.length; i++) {
- var element = $(arguments[i]);
- element.style.display = 'none';
- }
+Abstract.EventObserver = function() {}
+Abstract.EventObserver.prototype = {
+ initialize: function(element, callback) {
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ if (this.element.tagName.toLowerCase() == 'form')
+ this.registerFormCallbacks();
+ else
+ this.registerCallback(this.element);
},
-
- show: function() {
- for (var i = 0; i < arguments.length; i++) {
- var element = $(arguments[i]);
- element.style.display = '';
+
+ onElementEvent: function() {
+ var value = this.getValue();
+ if (this.lastValue != value) {
+ this.callback(this.element, value);
+ this.lastValue = value;
}
},
-
- remove: function(element) {
- element = $(element);
- element.parentNode.removeChild(element);
+
+ registerFormCallbacks: function() {
+ var elements = Form.getElements(this.element);
+ for (var i = 0; i < elements.length; i++)
+ this.registerCallback(elements[i]);
},
-
- getHeight: function(element) {
- element = $(element);
- return element.offsetHeight;
+
+ registerCallback: function(element) {
+ if (element.type) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ element.target = this;
+ element.prev_onclick = element.onclick || Prototype.emptyFunction;
+ element.onclick = function() {
+ this.prev_onclick();
+ this.target.onElementEvent();
+ }
+ break;
+ case 'password':
+ case 'text':
+ case 'textarea':
+ case 'select-one':
+ case 'select-multiple':
+ element.target = this;
+ element.prev_onchange = element.onchange || Prototype.emptyFunction;
+ element.onchange = function() {
+ this.prev_onchange();
+ this.target.onElementEvent();
+ }
+ break;
+ }
+ }
}
}
-var Toggle = new Object();
-Toggle.display = Element.toggle;
+Form.Element.EventObserver = Class.create();
+Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
-/*--------------------------------------------------------------------------*/
+Form.EventObserver = Class.create();
+Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
-Abstract.Insertion = function(adjacency) {
- this.adjacency = adjacency;
-}
-Abstract.Insertion.prototype = {
- initialize: function(element, content) {
- this.element = $(element);
- this.content = content;
-
- if (this.adjacency && this.element.insertAdjacentHTML) {
- this.element.insertAdjacentHTML(this.adjacency, this.content);
- } else {
- this.range = this.element.ownerDocument.createRange();
- if (this.initializeRange) this.initializeRange();
- this.fragment = this.range.createContextualFragment(this.content);
- this.insertContent();
- }
- }
+if (!window.Event) {
+ var Event = new Object();
}
-var Insertion = new Object();
-
-Insertion.Before = Class.create();
-Insertion.Before.prototype = (new Abstract.Insertion('beforeBegin')).extend({
- initializeRange: function() {
- this.range.setStartBefore(this.element);
+Object.extend(Event, {
+ KEY_BACKSPACE: 8,
+ KEY_TAB: 9,
+ KEY_RETURN: 13,
+ KEY_ESC: 27,
+ KEY_LEFT: 37,
+ KEY_UP: 38,
+ KEY_RIGHT: 39,
+ KEY_DOWN: 40,
+ KEY_DELETE: 46,
+
+ element: function(event) {
+ return event.target || event.srcElement;
},
-
- insertContent: function() {
- this.element.parentNode.insertBefore(this.fragment, this.element);
- }
-});
-Insertion.Top = Class.create();
-Insertion.Top.prototype = (new Abstract.Insertion('afterBegin')).extend({
- initializeRange: function() {
- this.range.selectNodeContents(this.element);
- this.range.collapse(true);
+ isLeftClick: function(event) {
+ return (((event.which) && (event.which == 1)) ||
+ ((event.button) && (event.button == 1)));
},
-
- insertContent: function() {
- this.element.insertBefore(this.fragment, this.element.firstChild);
- }
-});
-Insertion.Bottom = Class.create();
-Insertion.Bottom.prototype = (new Abstract.Insertion('beforeEnd')).extend({
- initializeRange: function() {
- this.range.selectNodeContents(this.element);
- this.range.collapse(this.element);
+ pointerX: function(event) {
+ return event.pageX || (event.clientX +
+ (document.documentElement.scrollLeft || document.body.scrollLeft));
},
-
- insertContent: function() {
- this.element.appendChild(this.fragment);
- }
-});
-Insertion.After = Class.create();
-Insertion.After.prototype = (new Abstract.Insertion('afterEnd')).extend({
- initializeRange: function() {
- this.range.setStartAfter(this.element);
+ pointerY: function(event) {
+ return event.pageY || (event.clientY +
+ (document.documentElement.scrollTop || document.body.scrollTop));
},
-
- insertContent: function() {
- this.element.parentNode.insertBefore(this.fragment,
- this.element.nextSibling);
- }
-});
-
-/*--------------------------------------------------------------------------*/
-var Effect = new Object();
+ stop: function(event) {
+ if (event.preventDefault) {
+ event.preventDefault();
+ event.stopPropagation();
+ } else {
+ event.returnValue = false;
+ }
+ },
-Effect.Highlight = Class.create();
-Effect.Highlight.prototype = {
- initialize: function(element) {
- this.element = $(element);
- this.start = 153;
- this.finish = 255;
- this.current = this.start;
- this.fade();
+ // find the first node with the given tagName, starting from the
+ // node the event was triggered on; traverses the DOM upwards
+ findElement: function(event, tagName) {
+ var element = Event.element(event);
+ while (element.parentNode && (!element.tagName ||
+ (element.tagName.toUpperCase() != tagName.toUpperCase())))
+ element = element.parentNode;
+ return element;
},
+
+ observers: false,
- fade: function() {
- if (this.isFinished()) return;
- if (this.timer) clearTimeout(this.timer);
- this.highlight(this.element, this.current);
- this.current += 17;
- this.timer = setTimeout(this.fade.bind(this), 250);
+ _observeAndCache: function(element, name, observer, useCapture) {
+ if (!this.observers) this.observers = [];
+ if (element.addEventListener) {
+ this.observers.push([element, name, observer, useCapture]);
+ element.addEventListener(name, observer, useCapture);
+ } else if (element.attachEvent) {
+ this.observers.push([element, name, observer, useCapture]);
+ element.attachEvent('on' + name, observer);
+ }
},
- isFinished: function() {
- return this.current > this.finish;
+ unloadCache: function() {
+ if (!Event.observers) return;
+ for (var i = 0; i < Event.observers.length; i++) {
+ Event.stopObserving.apply(this, Event.observers[i]);
+ Event.observers[i][0] = null;
+ }
+ Event.observers = false;
},
-
- highlight: function(element, current) {
- element.style.backgroundColor = "#ffff" + current.toColorPart();
- }
-}
-
-Effect.Fade = Class.create();
-Effect.Fade.prototype = {
- initialize: function(element) {
- this.element = $(element);
- this.start = 100;
- this.finish = 0;
- this.current = this.start;
- this.fade();
- },
-
- fade: function() {
- if (this.isFinished()) { this.element.style.display = 'none'; return; }
- if (this.timer) clearTimeout(this.timer);
- this.setOpacity(this.element, this.current);
- this.current -= 10;
- this.timer = setTimeout(this.fade.bind(this), 50);
- },
-
- isFinished: function() {
- return this.current <= this.finish;
+ observe: function(element, name, observer, useCapture) {
+ var element = $(element);
+ useCapture = useCapture || false;
+
+ if (name == 'keypress' &&
+ ((/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+ || element.attachEvent))
+ name = 'keydown';
+
+ this._observeAndCache(element, name, observer, useCapture);
},
-
- setOpacity: function(element, opacity) {
- opacity = (opacity == 100) ? 99.999 : opacity;
- element.style.filter = "alpha(opacity:"+opacity+")";
- element.style.opacity = opacity/100 /*//*/;
- }
-}
-Effect.Scale = Class.create();
-Effect.Scale.prototype = {
- initialize: function(element, percent) {
- this.element = $(element);
- this.startScale = 1.0;
- this.startHeight = this.element.offsetHeight;
- this.startWidth = this.element.offsetWidth;
- this.currentHeight = this.startHeight;
- this.currentWidth = this.startWidth;
- this.finishScale = (percent/100) /*//*/;
- if (this.element.style.fontSize=="") this.sizeEm = 1.0;
- if (this.element.style.fontSize.indexOf("em")>0)
- this.sizeEm = parseFloat(this.element.style.fontSize);
- if(this.element.effect_scale) {
- clearTimeout(this.element.effect_scale.timer);
- this.startScale = this.element.effect_scale.currentScale;
- this.startHeight = this.element.effect_scale.startHeight;
- this.startWidth = this.element.effect_scale.startWidth;
- if(this.element.effect_scale.sizeEm)
- this.sizeEm = this.element.effect_scale.sizeEm;
- }
- this.element.effect_scale = this;
- this.currentScale = this.startScale;
- this.factor = this.finishScale - this.startScale;
- this.options = arguments[2] || {};
- this.scale();
- },
-
- scale: function() {
- if (this.isFinished()) {
- this.setDimensions(this.element, this.startWidth*this.finishScale, this.startHeight*this.finishScale);
- if(this.sizeEm) this.element.style.fontSize = this.sizeEm*this.finishScale + "em";
- if(this.options.complete) this.options.complete(this);
- return;
- }
- if (this.timer) clearTimeout(this.timer);
- if (this.options.step) this.options.step(this);
- this.setDimensions(this.element, this.currentWidth, this.currentHeight);
- if(this.sizeEm) this.element.style.fontSize = this.sizeEm*this.currentScale + "em";
- this.currentScale += (this.factor/10) /*//*/;
- this.currentWidth = this.startWidth * this.currentScale;
- this.currentHeight = this.startHeight * this.currentScale;
- this.timer = setTimeout(this.scale.bind(this), 50);
- },
-
- isFinished: function() {
- return (this.factor < 0) ?
- this.currentScale <= this.finishScale : this.currentScale >= this.finishScale;
- },
-
- setDimensions: function(element, width, height) {
- element.style.width = width + 'px';
- element.style.height = height + 'px';
+ stopObserving: function(element, name, observer, useCapture) {
+ var element = $(element);
+ useCapture = useCapture || false;
+
+ if (name == 'keypress' &&
+ ((/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+ || element.detachEvent))
+ name = 'keydown';
+
+ if (element.removeEventListener) {
+ element.removeEventListener(name, observer, useCapture);
+ } else if (element.detachEvent) {
+ element.detachEvent('on' + name, observer);
+ }
}
-}
+});
-Effect.Squish = Class.create();
-Effect.Squish.prototype = {
- initialize: function(element) {
- this.element = $(element);
- new Effect.Scale(this.element, 1, { complete: this.hide.bind(this) } );
+/* prevent memory leaks in IE */
+Event.observe(window, 'unload', Event.unloadCache, false);
+
+var Position = {
+
+ // set to true if needed, warning: firefox performance problems
+ // NOT neeeded for page scrolling, only if draggable contained in
+ // scrollable elements
+ includeScrollOffsets: false,
+
+ // must be called before calling withinIncludingScrolloffset, every time the
+ // page is scrolled
+ prepare: function() {
+ this.deltaX = window.pageXOffset
+ || document.documentElement.scrollLeft
+ || document.body.scrollLeft
+ || 0;
+ this.deltaY = window.pageYOffset
+ || document.documentElement.scrollTop
+ || document.body.scrollTop
+ || 0;
},
- hide: function() {
- this.element.style.display = 'none';
- }
-}
-Effect.Puff = Class.create();
-Effect.Puff.prototype = {
- initialize: function(element) {
- this.element = $(element);
- this.opacity = 100;
- this.startTop = this.element.top || this.element.offsetTop;
- this.startLeft = this.element.left || this.element.offsetLeft;
- new Effect.Scale(this.element, 200, { step: this.fade.bind(this), complete: this.hide.bind(this) } );
- },
- fade: function(effect) {
- topd = (((effect.currentScale)*effect.startHeight) - effect.startHeight)/2;
- leftd = (((effect.currentScale)*effect.startWidth) - effect.startWidth)/2;
- this.element.style.position='absolute';
- this.element.style.top = this.startTop-topd + "px";
- this.element.style.left = this.startLeft-leftd + "px";
- this.opacity -= 10;
- this.setOpacity(this.element, this.opacity);
- if(navigator.appVersion.indexOf('AppleWebKit')>0) this.element.innerHTML += ''; //force redraw on safari
+ realOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.scrollTop || 0;
+ valueL += element.scrollLeft || 0;
+ element = element.parentNode;
+ } while (element);
+ return [valueL, valueT];
},
- hide: function() {
- this.element.style.display = 'none';
+
+ cumulativeOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ } while (element);
+ return [valueL, valueT];
},
- setOpacity: function(element, opacity) {
- opacity = (opacity == 100) ? 99.999 : opacity;
- element.style.filter = "alpha(opacity:"+opacity+")";
- element.style.opacity = opacity/100 /*//*/;
- }
-}
-Effect.Appear = Class.create();
-Effect.Appear.prototype = {
- initialize: function(element) {
- this.element = $(element);
- this.start = 0;
- this.finish = 100;
- this.current = this.start;
- this.fade();
+ // caches x/y coordinate pair to use with overlap
+ within: function(element, x, y) {
+ if (this.includeScrollOffsets)
+ return this.withinIncludingScrolloffsets(element, x, y);
+ this.xcomp = x;
+ this.ycomp = y;
+ this.offset = this.cumulativeOffset(element);
+
+ return (y >= this.offset[1] &&
+ y < this.offset[1] + element.offsetHeight &&
+ x >= this.offset[0] &&
+ x < this.offset[0] + element.offsetWidth);
},
-
- fade: function() {
- if (this.isFinished()) return;
- if (this.timer) clearTimeout(this.timer);
- this.setOpacity(this.element, this.current);
- this.current += 10;
- this.timer = setTimeout(this.fade.bind(this), 50);
+
+ withinIncludingScrolloffsets: function(element, x, y) {
+ var offsetcache = this.realOffset(element);
+
+ this.xcomp = x + offsetcache[0] - this.deltaX;
+ this.ycomp = y + offsetcache[1] - this.deltaY;
+ this.offset = this.cumulativeOffset(element);
+
+ return (this.ycomp >= this.offset[1] &&
+ this.ycomp < this.offset[1] + element.offsetHeight &&
+ this.xcomp >= this.offset[0] &&
+ this.xcomp < this.offset[0] + element.offsetWidth);
},
-
- isFinished: function() {
- return this.current > this.finish;
+
+ // within must be called directly before
+ overlap: function(mode, element) {
+ if (!mode) return 0;
+ if (mode == 'vertical')
+ return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+ element.offsetHeight;
+ if (mode == 'horizontal')
+ return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+ element.offsetWidth;
},
-
- setOpacity: function(element, opacity) {
- opacity = (opacity == 100) ? 99.999 : opacity;
- element.style.filter = "alpha(opacity:"+opacity+")";
- element.style.opacity = opacity/100 /*//*/;
- element.style.display = '';
- }
-}
-Effect.ContentZoom = Class.create();
-Effect.ContentZoom.prototype = {
- initialize: function(element, percent) {
- this.element = $(element);
- if (this.element.style.fontSize=="") this.sizeEm = 1.0;
- if (this.element.style.fontSize.indexOf("em")>0)
- this.sizeEm = parseFloat(this.element.style.fontSize);
- if(this.element.effect_contentzoom) {
- this.sizeEm = this.element.effect_contentzoom.sizeEm;
- }
- this.element.effect_contentzoom = this;
- this.element.style.fontSize = this.sizeEm*(percent/100) + "em" /*//*/;
- if(navigator.appVersion.indexOf('AppleWebKit')>0) { this.element.scrollTop -= 1; };
+ clone: function(source, target) {
+ source = $(source);
+ target = $(target);
+ target.style.position = 'absolute';
+ var offsets = this.cumulativeOffset(source);
+ target.style.top = offsets[1] + 'px';
+ target.style.left = offsets[0] + 'px';
+ target.style.width = source.offsetWidth + 'px';
+ target.style.height = source.offsetHeight + 'px';
}
}
-/* openrico.org rico.js */
-
/**
*
* Copyright 2005 Sabre Airline Solutions
* and limitations under the License.
**/
-// rico.js --------------------
+//-------------------- rico.js
var Rico = {
- Version: '1.1-beta'
+ Version: '1.1-beta2'
}
Rico.ArrayExtensions = new Array();
return elements;
}
-
-
-// ricoAccordion.js --------------------
-
+
+
+//-------------------- ricoAccordion.js
Rico.Accordion = Class.create();
}
};
-
-
-// ricoAjaxEngine.js --------------------
-
+
+
+//-------------------- ricoAjaxEngine.js
Rico.AjaxEngine = Class.create();
},
_processAjaxElementUpdate: function( ajaxElement, responseElement ) {
- if ( responseElement.xml != undefined )
- this._processAjaxElementUpdateIE( ajaxElement, responseElement );
- else
- this._processAjaxElementUpdateMozilla( ajaxElement, responseElement );
- },
-
- _processAjaxElementUpdateIE: function( ajaxElement, responseElement ) {
- var newHTML = "";
- for ( var i = 0 ; i < responseElement.childNodes.length ; i++ )
- newHTML += responseElement.childNodes[i].xml;
+ ajaxElement.innerHTML = RicoUtil.getContentAsString(responseElement);
+ }
- ajaxElement.innerHTML = newHTML;
- },
+}
- _processAjaxElementUpdateMozilla: function( ajaxElement, responseElement ) {
- var xmlSerializer = new XMLSerializer();
- var newHTML = "";
- for ( var i = 0 ; i < responseElement.childNodes.length ; i++ )
- newHTML += xmlSerializer.serializeToString(responseElement.childNodes[i]);
+var ajaxEngine = new Rico.AjaxEngine();
- ajaxElement.innerHTML = newHTML;
- }
-}
-var ajaxEngine = new Rico.AjaxEngine();
-
-// ricoColor.js --------------------
-
+//-------------------- ricoColor.js
Rico.Color = Class.create();
Rico.Color.prototype = {
}
return { h : hue, s : saturation, b : brightness };
-}
-
-// ricoCorner.js --------------------
-
+}
+
+
+//-------------------- ricoCorner.js
Rico.Corner = {
_isTopRounded: function() { return this._hasString(this.options.corners, "all", "top", "tl", "tr"); },
_isBottomRounded: function() { return this._hasString(this.options.corners, "all", "bottom", "bl", "br"); },
_hasSingleTextChild: function(el) { return el.childNodes.length == 1 && el.childNodes[0].nodeType == 3; }
-}
-
-// ricoDragAndDrop.js --------------------
-
+}
+
+
+//-------------------- ricoDragAndDrop.js
Rico.DragAndDrop = Class.create();
Rico.DragAndDrop.prototype = {
if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1))
return;
- var eventTarget = e.target ? e.target : e.srcElement;
- var draggableObject = eventTarget.draggable;
+ var eventTarget = e.target ? e.target : e.srcElement;
+ var draggableObject = eventTarget.draggable;
+
+ var candidate = eventTarget;
+ while (draggableObject == null && candidate.parentNode) {
+ candidate = candidate.parentNode;
+ draggableObject = candidate.draggable;
+ }
+
+ if ( draggableObject == null )
+ return;
this.updateSelection( draggableObject, e.ctrlKey );
}
var dndMgr = new Rico.DragAndDrop();
-dndMgr.initializeEventHandlers();
-
-// ricoDraggable.js --------------------
-
+dndMgr.initializeEventHandlers();
+
+
+//-------------------- ricoDraggable.js
Rico.Draggable = Class.create();
Rico.Draggable.prototype = {
return this.type + ":" + this.htmlElement + ":";
}
-}
-
-// ricoDropzone.js --------------------
-
+}
+
+
+//-------------------- ricoDropzone.js
Rico.Dropzone = Class.create();
Rico.Dropzone.prototype = {
htmlElement.appendChild(theGUI);
}
}
-}
-
-// ricoEffects.js --------------------
-
+}
+
+
+//-------------------- ricoEffects.js
+
+/**
+ * Use the Effect namespace for effects. If using scriptaculous effects
+ * this will already be defined, otherwise we'll just create an empty
+ * object for it...
+ **/
+if ( window.Effect == undefined )
+ Effect = {};
Effect.SizeAndPosition = Class.create();
Effect.SizeAndPosition.prototype = {
}
};
-
-
-// ricoLiveGrid.js --------------------
-
+
+
+//-------------------- ricoLiveGrid.js
// Rico.LiveGridMetaData -----------------------------------------------------
Rico.LiveGridMetaData.prototype = {
- initialize: function( pageSize, totalRows, options ) {
+ initialize: function( pageSize, totalRows, columnCount, options ) {
this.pageSize = pageSize;
this.totalRows = totalRows;
this.setOptions(options);
this.scrollArrowHeight = 16;
+ this.columnCount = columnCount;
},
setOptions: function(options) {
this.options = {
largeBufferSize : 7.0, // 7 pages
- smallBufferSize : 1.0, // 1 page
nearLimitFactor : 0.2 // 20% of buffer
}.extend(options || {});
},
return parseInt(this.options.largeBufferSize * this.pageSize);
},
- getSmallBufferSize: function() {
- return parseInt(this.options.smallBufferSize * this.pageSize);
- },
-
getLimitTolerance: function() {
return parseInt(this.getLargeBufferSize() * this.options.nearLimitFactor);
- },
-
- getBufferSize: function(isFull) {
- return isFull ? this.getLargeBufferSize() : this.getSmallBufferSize();
- }
+ }
};
// Rico.LiveGridScroller -----------------------------------------------------
Rico.LiveGridScroller.prototype = {
- initialize: function(liveGrid) {
+ initialize: function(liveGrid, viewPort) {
this.isIE = navigator.userAgent.toLowerCase().indexOf("msie") >= 0;
this.liveGrid = liveGrid;
this.metaData = liveGrid.metaData;
this.createScrollBar();
this.scrollTimeout = null;
- //this.sizeIEHeaderHack();
this.lastScrollPos = 0;
+ this.viewPort = viewPort;
+ this.rows = new Array();
},
isUnPlugged: function() {
},
createScrollBar: function() {
- var table = this.liveGrid.table;
- var visibleHeight = table.offsetHeight;
-
+ var visibleHeight = this.liveGrid.viewPort.visibleHeight();
// create the outer div...
this.scrollerDiv = document.createElement("div");
var scrollerStyle = this.scrollerDiv.style;
// create the inner div...
this.heightDiv = document.createElement("div");
this.heightDiv.style.width = "1px";
+
this.heightDiv.style.height = parseInt(visibleHeight *
this.metaData.getTotalRows()/this.metaData.getPageSize()) + "px" ;
- this.lineHeight = visibleHeight/this.metaData.getPageSize();
-
this.scrollerDiv.appendChild(this.heightDiv);
this.scrollerDiv.onscroll = this.handleScroll.bindAsEventListener(this);
- table.parentNode.insertBefore( this.scrollerDiv, table.nextSibling );
+
+ var table = this.liveGrid.table;
+ table.parentNode.parentNode.insertBefore( this.scrollerDiv, table.parentNode.nextSibling );
},
updateSize: function() {
var table = this.liveGrid.table;
- var visibleHeight = table.offsetHeight;
+ var visibleHeight = this.viewPort.visibleHeight();
this.heightDiv.style.height = parseInt(visibleHeight *
this.metaData.getTotalRows()/this.metaData.getPageSize()) + "px";
},
- adjustScrollTop: function() {
- this.unplug();
- var rem = this.scrollerDiv.scrollTop % this.lineHeight;
- if (rem != 0) {
- if (this.lastScrollPos < this.scrollerDiv.scrollTop)
- this.scrollerDiv.scrollTop = this.scrollerDiv.scrollTop + this.lineHeight -rem;
- else
- this.scrollerDiv.scrollTop = this.scrollerDiv.scrollTop - rem;
- }
- this.lastScrollPos = this.scrollerDiv.scrollTop;
- this.plugin();
+ rowToPixel: function(rowOffset) {
+ return (rowOffset / this.metaData.getTotalRows()) * this.heightDiv.offsetHeight
+ },
+
+ moveScroll: function(rowOffset) {
+ this.scrollerDiv.scrollTop = this.rowToPixel(rowOffset);
+ if ( this.metaData.options.onscroll )
+ this.metaData.options.onscroll( this.liveGrid, rowOffset );
},
handleScroll: function() {
- if ( this.scrollTimeout )
+ if ( this.scrollTimeout )
clearTimeout( this.scrollTimeout );
- //this.adjustScrollTop();
- var contentOffset = parseInt(this.scrollerDiv.scrollTop *
- this.metaData.getTotalRows() / this.heightDiv.offsetHeight);
+ var contentOffset = parseInt(this.scrollerDiv.scrollTop / this.viewPort.rowHeight);
this.liveGrid.requestContentRefresh(contentOffset);
+ this.viewPort.scrollTo(this.scrollerDiv.scrollTop);
+
if ( this.metaData.options.onscroll )
- this.metaData.options.onscroll( contentOffset, this.metaData );
+ this.metaData.options.onscroll( this.liveGrid, contentOffset );
this.scrollTimeout = setTimeout( this.scrollIdle.bind(this), 1200 );
},
Rico.LiveGridBuffer.prototype = {
- initialize: function(metaData) {
+ initialize: function(metaData, viewPort) {
this.startPos = 0;
this.size = 0;
this.metaData = metaData;
this.rows = new Array();
this.updateInProgress = false;
- },
-
- update: function(ajaxResponse,start) {
-
- this.startPos = start;
- this.rows = new Array();
-
+ this.viewPort = viewPort;
+ this.maxBufferSize = metaData.getLargeBufferSize() * 2;
+ this.maxFetchSize = metaData.getLargeBufferSize();
+ this.lastOffset = 0;
+ },
+
+ getBlankRow: function() {
+ if (!this.blankRow ) {
+ this.blankRow = new Array();
+ for ( var i=0; i < this.metaData.columnCount ; i++ )
+ this.blankRow[i] = " ";
+ }
+ return this.blankRow;
+ },
+
+ loadRows: function(ajaxResponse) {
var rowsElement = ajaxResponse.getElementsByTagName('rows')[0];
this.updateUI = rowsElement.getAttribute("update_ui") == "true"
+ var newRows = new Array()
var trs = rowsElement.getElementsByTagName("tr");
for ( var i=0 ; i < trs.length; i++ ) {
- var row = this.rows[i] = new Array();
+ var row = newRows[i] = new Array();
var cells = trs[i].getElementsByTagName("td");
for ( var j=0; j < cells.length ; j++ ) {
var cell = cells[j];
var convertSpaces = cell.getAttribute("convert_spaces") == "true";
- var cellContent = cell.text != undefined ? cell.text : cell.textContent;
+ var cellContent = RicoUtil.getContentAsString(cell);
row[j] = convertSpaces ? this.convertSpaces(cellContent) : cellContent;
+ if (!row[j])
+ row[j] = ' ';
}
}
- this.size = trs.length;
+ return newRows;
+ },
+
+ update: function(ajaxResponse, start) {
+ var newRows = this.loadRows(ajaxResponse);
+ if (this.rows.length == 0) { // initial load
+ this.rows = newRows;
+ this.size = this.rows.length;
+ this.startPos = start;
+ return;
+ }
+ if (start > this.startPos) { //appending
+ if (this.startPos + this.rows.length < start) {
+ this.rows = newRows;
+ this.startPos = start;//
+ } else {
+ this.rows = this.rows.concat( newRows.slice(0, newRows.length));
+ if (this.rows.length > this.maxBufferSize) {
+ var fullSize = this.rows.length;
+ this.rows = this.rows.slice(this.rows.length - this.maxBufferSize, this.rows.length)
+ this.startPos = this.startPos + (fullSize - this.rows.length);
+ }
+ }
+ } else { //prepending
+ if (start + newRows.length < this.startPos) {
+ this.rows = newRows;
+ } else {
+ this.rows = newRows.slice(0, this.startPos).concat(this.rows);
+ if (this.rows.length > this.maxBufferSize)
+ this.rows = this.rows.slice(0, this.maxBufferSize)
+ }
+ this.startPos = start;
+ }
+ this.size = this.rows.length;
+ },
+
+ clear: function() {
+ this.rows = new Array();
+ this.startPos = 0;
+ this.size = 0;
},
- isFullP: function() {
- return this.metaData.pageSize != this.size;
- },
-
- isClose: function(start) {
- return (start < this.startPos + this.size + (this.size/2)) &&
- (start + this.size + (this.size/2) > this.startPos)
- },
-
- isInRange: function(start, count) {
- return (start < this.startPos + this.size) && (start + count > this.startPos)
+ isOverlapping: function(start, size) {
+ return ((start < this.endPos()) && (this.startPos < start + size)) || (this.endPos() == 0)
},
- isFullyInRange: function(position) {
- return (position >= this.startPos) && (position+this.metaData.getPageSize()) <= (this.startPos + this.size)
+ isInRange: function(position) {
+ return (position >= this.startPos) && (position + this.metaData.getPageSize() <= this.endPos());
+ //&& this.size() != 0;
},
isNearingTopLimit: function(position) {
return position - this.startPos < this.metaData.getLimitTolerance();
},
+ endPos: function() {
+ return this.startPos + this.rows.length;
+ },
+
isNearingBottomLimit: function(position) {
- var myEnd = position + this.metaData.getPageSize();
- var bufferEnd = this.startPos + this.size;
- return bufferEnd - myEnd < this.metaData.getLimitTolerance();
+ return this.endPos() - (position + this.metaData.getPageSize()) < this.metaData.getLimitTolerance();
},
isAtTop: function() {
},
isAtBottom: function() {
- return this.startPos + this.size == this.metaData.getTotalRows();
+ return this.endPos() == this.metaData.getTotalRows();
},
isNearingLimit: function(position) {
( !this.isAtBottom() && this.isNearingBottomLimit(position) )
},
+ getFetchSize: function(offset) {
+ var adjustedOffset = this.getFetchOffset(offset);
+ var adjustedSize = 0;
+ if (adjustedOffset >= this.startPos) { //apending
+ var endFetchOffset = this.maxFetchSize + adjustedOffset;
+ if (endFetchOffset > this.metaData.totalRows)
+ endFetchOffset = this.metaData.totalRows;
+ adjustedSize = endFetchOffset - adjustedOffset;
+ } else {//prepending
+ var adjustedSize = this.startPos - adjustedOffset;
+ if (adjustedSize > this.maxFetchSize)
+ adjustedSize = this.maxFetchSize;
+ }
+ return adjustedSize;
+ },
+
+ getFetchOffset: function(offset) {
+ var adjustedOffset = offset;
+ if (offset > this.startPos) //apending
+ adjustedOffset = (offset > this.endPos()) ? offset : this.endPos();
+ else { //prepending
+ if (offset + this.maxFetchSize >= this.startPos) {
+ var adjustedOffset = this.startPos - this.maxFetchSize;
+ if (adjustedOffset < 0)
+ adjustedOffset = 0;
+ }
+ }
+ this.lastOffset = adjustedOffset;
+ return adjustedOffset;
+ },
+
getRows: function(start, count) {
var begPos = start - this.startPos
var endPos = begPos + count
};
+
+//Rico.GridViewPort --------------------------------------------------
+Rico.GridViewPort = Class.create();
+
+Rico.GridViewPort.prototype = {
+
+ initialize: function(table, rowHeight, visibleRows, buffer, liveGrid) {
+ this.lastDisplayedStartPos = 0;
+ this.div = table.parentNode;
+ this.table = table
+ this.rowHeight = rowHeight;
+ this.div.style.height = this.rowHeight * visibleRows;
+ this.div.style.overflow = "hidden";
+ this.buffer = buffer;
+ this.liveGrid = liveGrid;
+ this.visibleRows = visibleRows + 1;
+ this.lastPixelOffset = 0;
+ this.startPos = 0;
+ },
+
+ populateRow: function(htmlRow, row) {
+ for (var j=0; j < row.length; j++) {
+ htmlRow.cells[j].innerHTML = row[j]
+ }
+ },
+
+ bufferChanged: function() {
+ this.refreshContents( parseInt(this.lastPixelOffset / this.rowHeight));
+ },
+
+ clearRows: function() {
+ if (!this.isBlank) {
+ for (var i=0; i < this.visibleRows; i++)
+ this.populateRow(this.table.rows[i], this.buffer.getBlankRow());
+ this.isBlank = true;
+ }
+ },
+
+ clearContents: function() {
+ this.clearRows();
+ this.scrollTo(0);
+ this.startPos = 0;
+ this.lastStartPos = -1;
+ },
+
+ refreshContents: function(startPos) {
+ if (startPos == this.lastRowPos && !this.isPartialBlank && !this.isBlank) {
+ return;
+ }
+ if ((startPos + this.visibleRows < this.buffer.startPos)
+ || (this.buffer.startPos + this.buffer.size < startPos)
+ || (this.buffer.size == 0)) {
+ this.clearRows();
+ return;
+ }
+ this.isBlank = false;
+ var viewPrecedesBuffer = this.buffer.startPos > startPos
+ var contentStartPos = viewPrecedesBuffer ? this.buffer.startPos: startPos;
+
+ var contentEndPos = (this.buffer.startPos + this.buffer.size < startPos + this.visibleRows)
+ ? this.buffer.startPos + this.buffer.size
+ : startPos + this.visibleRows;
+ var rowSize = contentEndPos - contentStartPos;
+ var rows = this.buffer.getRows(contentStartPos, rowSize );
+ var blankSize = this.visibleRows - rowSize;
+ var blankOffset = viewPrecedesBuffer ? 0: rowSize;
+ var contentOffset = viewPrecedesBuffer ? blankSize: 0;
+
+ for (var i=0; i < rows.length; i++) {//initialize what we have
+ this.populateRow(this.table.rows[i + contentOffset], rows[i]);
+ }
+ for (var i=0; i < blankSize; i++) {// blank out the rest
+ this.populateRow(this.table.rows[i + blankOffset], this.buffer.getBlankRow());
+ }
+ this.isPartialBlank = blankSize > 0;
+ this.lastRowPos = startPos;
+ },
+
+ scrollTo: function(pixelOffset) {
+ if (this.lastPixelOffset == pixelOffset)
+ return;
+
+ this.refreshContents(parseInt(pixelOffset / this.rowHeight))
+ this.div.scrollTop = pixelOffset % this.rowHeight
+
+ this.lastPixelOffset = pixelOffset;
+ },
+
+ visibleHeight: function() {
+ return parseInt(this.div.style.height);
+ }
+
+};
+
+
Rico.LiveGridRequest = Class.create();
Rico.LiveGridRequest.prototype = {
initialize: function( requestOffset, options ) {
- this.requestOffset = requestOffset;
+ this.requestOffset = requestOffset;
}
};
Rico.LiveGrid.prototype = {
initialize: function( tableId, visibleRows, totalRows, url, options ) {
-
if ( options == null )
options = {};
- this.tableId = tableId;
+ this.tableId = tableId;
this.table = $(tableId);
- this.metaData = new Rico.LiveGridMetaData(visibleRows, totalRows, options);
+ var columnCount = this.table.rows[0].cells.length
+ this.metaData = new Rico.LiveGridMetaData(visibleRows, totalRows, columnCount, options);
this.buffer = new Rico.LiveGridBuffer(this.metaData);
- this.scroller = new Rico.LiveGridScroller(this);
- this.lastDisplayedStartPos = 0;
- this.timeoutHander = null;
+ var rowCount = this.table.rows.length;
+ this.viewPort = new Rico.GridViewPort(this.table,
+ this.table.offsetHeight/rowCount,
+ visibleRows,
+ this.buffer, this);
+ this.scroller = new Rico.LiveGridScroller(this,this.viewPort);
+
this.additionalParms = options.requestParameters || [];
+
+ options.sortHandler = this.sortHandler.bind(this);
+
+ if ( $(tableId + '_header') )
+ this.sort = new Rico.LiveGridSort(tableId + '_header', options)
this.processingRequest = null;
this.unprocessedRequest = null;
this.initAjax(url);
- if ( options.prefetchBuffer )
- this.fetchBuffer(0, true);
+ if ( options.prefetchBuffer || options.prefetchOffset > 0) {
+ var offset = 0;
+ if (options.offset ) {
+ offset = options.offset;
+ this.scroller.moveScroll(offset);
+ this.viewPort.scrollTo(this.scroller.rowToPixel(offset));
+ }
+ if (options.sortCol) {
+ this.sortCol = options.sortCol;
+ this.sortDir = options.sortDir;
+ }
+ this.requestContentRefresh(offset);
+ }
},
+ resetContents: function() {
+ this.scroller.moveScroll(0);
+ this.buffer.clear();
+ this.viewPort.clearContents();
+ },
+
+ sortHandler: function(column) {
+ this.sortCol = column.name;
+ this.sortDir = column.currentSort;
+
+ this.resetContents();
+ this.requestContentRefresh(0)
+ },
+
setRequestParams: function() {
this.additionalParms = [];
for ( var i=0 ; i < arguments.length ; i++ )
},
setTotalRows: function( newTotalRows ) {
+ this.resetContents();
this.metaData.setTotalRows(newTotalRows);
this.scroller.updateSize();
},
invokeAjax: function() {
},
- largeBufferWindowStart: function(offset) {
- val = offset - ( (.5 * this.metaData.getLargeBufferSize()) - (.5 * this.metaData.getPageSize()) )
- return Math.max(parseInt(val), 0);
- },
-
handleTimedOut: function() {
//server did not respond in 4 seconds... assume that there could have been
//an error or something, and allow requests to be processed again...
this.processingRequest = null;
+ this.processQueuedRequest();
},
- fetchBuffer: function(offset, fullBufferp) {
+ fetchBuffer: function(offset) {
+ if ( this.buffer.isInRange(offset) &&
+ !this.buffer.isNearingLimit(offset)) {
+ return;
+ }
if (this.processingRequest) {
- this.unprocessedRequest = new Rico.LiveGridRequest(offset);
- return;
+ this.unprocessedRequest = new Rico.LiveGridRequest(offset);
+ return;
}
-
- var fetchSize = this.metaData.getBufferSize(fullBufferp);
- bufferStartPos = Math.max(0,fullBufferp ? this.largeBufferWindowStart(offset) : offset);
-
+ var bufferStartPos = this.buffer.getFetchOffset(offset);
this.processingRequest = new Rico.LiveGridRequest(offset);
- this.processingRequest.bufferOffset = bufferStartPos;
-
- var callParms = [];
+ this.processingRequest.bufferOffset = bufferStartPos;
+ var fetchSize = this.buffer.getFetchSize(offset);
+ var partialLoaded = false;
+ var callParms = [];
callParms.push(this.tableId + '_request');
callParms.push('id=' + this.tableId);
callParms.push('page_size=' + fetchSize);
callParms.push('offset=' + bufferStartPos);
-
+ if ( this.sortCol) {
+ callParms.push('sort_col=' + this.sortCol);
+ callParms.push('sort_dir=' + this.sortDir);
+ }
+
for( var i=0 ; i < this.additionalParms.length ; i++ )
callParms.push(this.additionalParms[i]);
-
ajaxEngine.sendRequest.apply( ajaxEngine, callParms );
- this.timeoutHandler = setTimeout( this.handleTimedOut.bind(this), 4000 );
+
+ this.timeoutHandler = setTimeout( this.handleTimedOut.bind(this), 20000 ); //todo: make as option
},
requestContentRefresh: function(contentOffset) {
- if ( this.buffer && this.buffer.isFullyInRange(contentOffset) ) {
- this.updateContent(contentOffset);
- if (this.buffer.isNearingLimit(contentOffset))
- this.fetchBuffer(contentOffset, true);
- }
- else if (this.buffer && this.buffer.isClose(contentOffset))
- this.fetchBuffer(contentOffset, true);
- else
- this.fetchBuffer(contentOffset, false);
+ this.fetchBuffer(contentOffset);
},
ajaxUpdate: function(ajaxResponse) {
try {
clearTimeout( this.timeoutHandler );
- this.buffer = new Rico.LiveGridBuffer(this.metaData);
this.buffer.update(ajaxResponse,this.processingRequest.bufferOffset);
- if (this.unprocessedRequest == null) {
- offset = this.processingRequest.requestOffset;
- this.updateContent (offset);
- }
- this.processingRequest = null;
- if (this.unprocessedRequest != null) {
- this.requestContentRefresh(this.unprocessedRequest.requestOffset);
- this.unprocessedRequest = null
+ this.viewPort.bufferChanged();
+ }
+ catch(err) {}
+ finally {this.processingRequest = null; }
+ this.processQueuedRequest();
+ },
+
+ processQueuedRequest: function() {
+ if (this.unprocessedRequest != null) {
+ this.requestContentRefresh(this.unprocessedRequest.requestOffset);
+ this.unprocessedRequest = null
+ }
+ }
+
+};
+
+
+//-------------------- ricoLiveGridSort.js
+Rico.LiveGridSort = Class.create();
+
+Rico.LiveGridSort.prototype = {
+
+ initialize: function(headerTableId, options) {
+ this.headerTableId = headerTableId;
+ this.headerTable = $(headerTableId);
+ this.setOptions(options);
+ this.applySortBehavior();
+
+ if ( this.options.sortCol ) {
+ this.setSortUI( this.options.sortCol, this.options.sortDir );
+ }
+ },
+
+ setSortUI: function( columnName, sortDirection ) {
+ var cols = this.options.columns;
+ for ( var i = 0 ; i < cols.length ; i++ ) {
+ if ( cols[i].name == columnName ) {
+ this.setColumnSort(i, sortDirection);
+ break;
}
}
- catch(err) {
+ },
+
+ setOptions: function(options) {
+ this.options = {
+ sortAscendImg: 'images/sort_asc.gif',
+ sortDescendImg: 'images/sort_desc.gif',
+ imageWidth: 9,
+ imageHeight: 5,
+ ajaxSortURLParms: []
+ }.extend(options);
+
+ // preload the images...
+ new Image().src = this.options.sortAscendImg;
+ new Image().src = this.options.sortDescendImg;
+
+ this.sort = options.sortHandler;
+ if ( !this.options.columns )
+ this.options.columns = this.introspectForColumnInfo();
+ else {
+ // allow client to pass { columns: [ ["a", true], ["b", false] ] }
+ // and convert to an array of Rico.TableColumn objs...
+ this.options.columns = this.convertToTableColumns(this.options.columns);
}
},
- updateContent: function( offset ) {
- this.replaceCellContents(this.buffer, offset);
+ applySortBehavior: function() {
+ var headerRow = this.headerTable.rows[0];
+ var headerCells = headerRow.cells;
+ for ( var i = 0 ; i < headerCells.length ; i++ ) {
+ this.addSortBehaviorToColumn( i, headerCells[i] );
+ }
},
- replaceCellContents: function(buffer, startPos) {
- if (startPos == this.lastDisplayedStartPos){
- return;
+ addSortBehaviorToColumn: function( n, cell ) {
+ if ( this.options.columns[n].isSortable() ) {
+ cell.id = this.headerTableId + '_' + n;
+ cell.style.cursor = 'pointer';
+ cell.onclick = this.headerCellClicked.bindAsEventListener(this);
+ cell.innerHTML = cell.innerHTML + '<span id="' + this.headerTableId + '_img_' + n + '">'
+ + ' </span>';
}
+ },
- this.lastDisplayedStartPos = startPos
- var rows = buffer.getRows(startPos, this.metaData.getPageSize());
- for (var i=0; i < rows.length; i++) {
- var row = rows[i];
- for (var j=0; j < row.length; j++) {
- this.table.rows[i].cells[j].innerHTML = rows[i][j]
+ // event handler....
+ headerCellClicked: function(evt) {
+ var eventTarget = evt.target ? evt.target : evt.srcElement;
+ var cellId = eventTarget.id;
+ var columnNumber = parseInt(cellId.substring( cellId.lastIndexOf('_') + 1 ));
+ var sortedColumnIndex = this.getSortedColumnIndex();
+ if ( sortedColumnIndex != -1 ) {
+ if ( sortedColumnIndex != columnNumber ) {
+ this.removeColumnSort(sortedColumnIndex);
+ this.setColumnSort(columnNumber, Rico.TableColumn.SORT_ASC);
}
+ else
+ this.toggleColumnSort(sortedColumnIndex);
+ }
+ else
+ this.setColumnSort(columnNumber, Rico.TableColumn.SORT_ASC);
+
+ if (this.options.sortHandler) {
+ this.options.sortHandler(this.options.columns[columnNumber]);
+ }
+ },
+
+ removeColumnSort: function(n) {
+ this.options.columns[n].setUnsorted();
+ this.setSortImage(n);
+ },
+
+ setColumnSort: function(n, direction) {
+ this.options.columns[n].setSorted(direction);
+ this.setSortImage(n);
+ },
+
+ toggleColumnSort: function(n) {
+ this.options.columns[n].toggleSort();
+ this.setSortImage(n);
+ },
+
+ setSortImage: function(n) {
+ var sortDirection = this.options.columns[n].getSortDirection();
+
+ var sortImageSpan = $( this.headerTableId + '_img_' + n );
+ if ( sortDirection == Rico.TableColumn.UNSORTED )
+ sortImageSpan.innerHTML = ' ';
+ else if ( sortDirection == Rico.TableColumn.SORT_ASC )
+ sortImageSpan.innerHTML = ' <img width="' + this.options.imageWidth + '" ' +
+ 'height="'+ this.options.imageHeight + '" ' +
+ 'src="' + this.options.sortAscendImg + '"/>';
+ else if ( sortDirection == Rico.TableColumn.SORT_DESC )
+ sortImageSpan.innerHTML = ' <img width="' + this.options.imageWidth + '" ' +
+ 'height="'+ this.options.imageHeight + '" ' +
+ 'src="' + this.options.sortDescendImg + '"/>';
+ },
+
+ getSortedColumnIndex: function() {
+ var cols = this.options.columns;
+ for ( var i = 0 ; i < cols.length ; i++ ) {
+ if ( cols[i].isSorted() )
+ return i;
}
+
+ return -1;
+ },
+
+ introspectForColumnInfo: function() {
+ var columns = new Array();
+ var headerRow = this.headerTable.rows[0];
+ var headerCells = headerRow.cells;
+ for ( var i = 0 ; i < headerCells.length ; i++ )
+ columns.push( new Rico.TableColumn( this.deriveColumnNameFromCell(headerCells[i],i), true ) );
+ return columns;
+ },
+
+ convertToTableColumns: function(cols) {
+ var columns = new Array();
+ for ( var i = 0 ; i < cols.length ; i++ )
+ columns.push( new Rico.TableColumn( cols[i][0], cols[i][1] ) );
+ },
+
+ deriveColumnNameFromCell: function(cell,columnNumber) {
+ var cellContent = cell.innerText != undefined ? cell.innerText : cell.textContent;
+ return cellContent ? cellContent.toLowerCase().split(' ').join('_') : "col_" + columnNumber;
}
-};
-
-// ricoUtil.js --------------------
-
+};
+
+Rico.TableColumn = Class.create();
+
+Rico.TableColumn.UNSORTED = 0;
+Rico.TableColumn.SORT_ASC = "ASC";
+Rico.TableColumn.SORT_DESC = "DESC";
+
+Rico.TableColumn.prototype = {
+ initialize: function(name, sortable) {
+ this.name = name;
+ this.sortable = sortable;
+ this.currentSort = Rico.TableColumn.UNSORTED;
+ },
+
+ isSortable: function() {
+ return this.sortable;
+ },
+
+ isSorted: function() {
+ return this.currentSort != Rico.TableColumn.UNSORTED;
+ },
+
+ getSortDirection: function() {
+ return this.currentSort;
+ },
+
+ toggleSort: function() {
+ if ( this.currentSort == Rico.TableColumn.UNSORTED || this.currentSort == Rico.TableColumn.SORT_DESC )
+ this.currentSort = Rico.TableColumn.SORT_ASC;
+ else if ( this.currentSort == Rico.TableColumn.SORT_ASC )
+ this.currentSort = Rico.TableColumn.SORT_DESC;
+ },
+
+ setUnsorted: function(direction) {
+ this.setSorted(Rico.TableColumn.UNSORTED);
+ },
+
+ setSorted: function(direction) {
+ // direction must by one of Rico.TableColumn.UNSORTED, .SORT_ASC, or .SET_DESC...
+ this.currentSort = direction;
+ }
+
+};
+
+
+//-------------------- ricoUtil.js
var RicoUtil = {
return null;
},
+ getContentAsString: function( parentNode ) {
+ return parentNode.xml != undefined ?
+ this._getContentAsStringIE(parentNode) :
+ this._getContentAsStringMozilla(parentNode);
+ },
+
+ _getContentAsStringIE: function(parentNode) {
+ var contentStr = "";
+ for ( var i = 0 ; i < parentNode.childNodes.length ; i++ )
+ contentStr += parentNode.childNodes[i].xml;
+ return contentStr;
+ },
+
+ _getContentAsStringMozilla: function(parentNode) {
+ var xmlSerializer = new XMLSerializer();
+ var contentStr = "";
+ for ( var i = 0 ; i < parentNode.childNodes.length ; i++ )
+ contentStr += xmlSerializer.serializeToString(parentNode.childNodes[i]);
+ return contentStr;
+ },
+
toViewportPosition: function(element) {
return this._toAbsolute(element,true);
},
return 0;
}
-};
\ No newline at end of file
+};
--- /dev/null
+var Scriptaculous = {
+ Version: '1.5_pre4',
+ require: function(libraryName) {
+ // inserting via DOM fails in Safari 2.0, so brute force approach
+ document.write('<script type="text/javascript" src="'+libraryName+'"></script>');
+ },
+ load: function() {
+ if((typeof Prototype=='undefined') ||
+ parseFloat(Prototype.Version.split(".")[0] + "." +
+ Prototype.Version.split(".")[1]) < 1.4)
+ throw("script.aculo.us requires the Prototype JavaScript framework >= 1.4.0");
+ var scriptTags = document.getElementsByTagName("script");
+ for(var i=0;i<scriptTags.length;i++) {
+ if(scriptTags[i].src && scriptTags[i].src.match(/scriptaculous\.js$/)) {
+ var path = scriptTags[i].src.replace(/scriptaculous\.js$/,'');
+ this.require(path + 'util.js');
+ this.require(path + 'effects.js');
+ this.require(path + 'dragdrop.js');
+ this.require(path + 'controls.js');
+ break;
+ }
+ }
+ }
+}
+
+Scriptaculous.load();
\ No newline at end of file
--- /dev/null
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// (c) 2005 Jon Tirsen (http://www.tirsen.com)
+// (c) 2005 Michael Schuerig (http://www.schuerig.de/michael/)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+// experimental, Firefox-only
+Event.simulateMouse = function(element, eventName) {
+ var options = Object.extend({
+ pointerX: 0,
+ pointerY: 0,
+ buttons: 0
+ }, arguments[2] || {});
+ var oEvent = document.createEvent("MouseEvents");
+ oEvent.initMouseEvent(eventName, true, true, document.defaultView,
+ options.buttons, options.pointerX, options.pointerY, options.pointerX, options.pointerY,
+ false, false, false, false, 0, $(element));
+
+ if(this.mark) Element.remove(this.mark);
+ this.mark = document.createElement('div');
+ this.mark.appendChild(document.createTextNode(" "));
+ document.body.appendChild(this.mark);
+ this.mark.style.position = 'absolute';
+ this.mark.style.top = options.pointerY + "px";
+ this.mark.style.left = options.pointerX + "px";
+ this.mark.style.width = "5px";
+ this.mark.style.height = "5px;";
+ this.mark.style.borderTop = "1px solid red;"
+ this.mark.style.borderLeft = "1px solid red;"
+
+ if(this.step)
+ alert('['+new Date().getTime().toString()+'] '+eventName+'/'+Test.Unit.inspect(options));
+
+ $(element).dispatchEvent(oEvent);
+};
+
+// Note: Due to a fix in Firefox 1.0.5/6 that probably fixed "too much", this doesn't work in 1.0.6 or DP2.
+// You need to downgrade to 1.0.4 for now to get this working
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=289940 for the fix that fixed too much
+Event.simulateKey = function(element, eventName) {
+ var options = Object.extend({
+ ctrlKey: false,
+ altKey: false,
+ shiftKey: false,
+ metaKey: false,
+ keyCode: 0,
+ charCode: 0
+ }, arguments[2] || {});
+
+ var oEvent = document.createEvent("KeyEvents");
+ oEvent.initKeyEvent(eventName, true, true, window,
+ options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
+ options.keyCode, options.charCode );
+ $(element).dispatchEvent(oEvent);
+};
+
+Event.simulateKeys = function(element, command) {
+ for(var i=0; i<command.length; i++) {
+ Event.simulateKey(element,'keypress',{charCode:command.charCodeAt(i)});
+ }
+};
+
+var Test = {}
+Test.Unit = {};
+
+// security exception workaround
+Test.Unit.inspect = function(obj) {
+ var info = [];
+
+ if(typeof obj=="string" ||
+ typeof obj=="number") {
+ return obj;
+ } else {
+ for(property in obj)
+ if(typeof obj[property]!="function")
+ info.push(property + ' => ' +
+ (typeof obj[property] == "string" ?
+ '"' + obj[property] + '"' :
+ obj[property]));
+ }
+
+ return ("'" + obj + "' #" + typeof obj +
+ ": {" + info.join(", ") + "}");
+}
+
+Test.Unit.Logger = Class.create();
+Test.Unit.Logger.prototype = {
+ initialize: function(log) {
+ this.log = $(log);
+ if (this.log) {
+ this._createLogTable();
+ }
+ },
+ start: function(testName) {
+ if (!this.log) return;
+ this.testName = testName;
+ this.lastLogLine = document.createElement('tr');
+ this.statusCell = document.createElement('td');
+ this.nameCell = document.createElement('td');
+ this.nameCell.appendChild(document.createTextNode(testName));
+ this.messageCell = document.createElement('td');
+ this.lastLogLine.appendChild(this.statusCell);
+ this.lastLogLine.appendChild(this.nameCell);
+ this.lastLogLine.appendChild(this.messageCell);
+ this.loglines.appendChild(this.lastLogLine);
+ },
+ finish: function(status, summary) {
+ if (!this.log) return;
+ this.lastLogLine.className = status;
+ this.statusCell.innerHTML = status;
+ this.messageCell.innerHTML = this._toHTML(summary);
+ },
+ message: function(message) {
+ if (!this.log) return;
+ this.messageCell.innerHTML = this._toHTML(message);
+ },
+ summary: function(summary) {
+ if (!this.log) return;
+ this.logsummary.innerHTML = this._toHTML(summary);
+ },
+ _createLogTable: function() {
+ this.log.innerHTML =
+ '<div id="logsummary"></div>' +
+ '<table id="logtable">' +
+ '<thead><tr><th>Status</th><th>Test</th><th>Message</th></tr></thead>' +
+ '<tbody id="loglines"></tbody>' +
+ '</table>';
+ this.logsummary = $('logsummary')
+ this.loglines = $('loglines');
+ },
+ _toHTML: function(txt) {
+ return txt.escapeHTML().replace(/\n/g,"<br/>");
+ }
+}
+
+Test.Unit.Runner = Class.create();
+Test.Unit.Runner.prototype = {
+ initialize: function(testcases) {
+ this.options = Object.extend({
+ testLog: 'testlog'
+ }, arguments[1] || {});
+ this.options.resultsURL = this.parseResultsURLQueryParameter();
+ if (this.options.testLog) {
+ this.options.testLog = $(this.options.testLog) || null;
+ }
+ if(this.options.tests) {
+ this.tests = [];
+ for(var i = 0; i < this.options.tests.length; i++) {
+ if(/^test/.test(this.options.tests[i])) {
+ this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases["setup"], testcases["teardown"]));
+ }
+ }
+ } else {
+ if (this.options.test) {
+ this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases["setup"], testcases["teardown"])];
+ } else {
+ this.tests = [];
+ for(var testcase in testcases) {
+ if(/^test/.test(testcase)) {
+ this.tests.push(new Test.Unit.Testcase(testcase, testcases[testcase], testcases["setup"], testcases["teardown"]));
+ }
+ }
+ }
+ }
+ this.currentTest = 0;
+ this.logger = new Test.Unit.Logger(this.options.testLog);
+ setTimeout(this.runTests.bind(this), 1000);
+ },
+ parseResultsURLQueryParameter: function() {
+ return window.location.search.parseQuery()["resultsURL"];
+ },
+ // Returns:
+ // "ERROR" if there was an error,
+ // "FAILURE" if there was a failure, or
+ // "SUCCESS" if there was neither
+ getResult: function() {
+ var hasFailure = false;
+ for(var i=0;i<this.tests.length;i++) {
+ if (this.tests[i].errors > 0) {
+ return "ERROR";
+ }
+ if (this.tests[i].failures > 0) {
+ hasFailure = true;
+ }
+ }
+ if (hasFailure) {
+ return "FAILURE";
+ } else {
+ return "SUCCESS";
+ }
+ },
+ postResults: function() {
+ if (this.options.resultsURL) {
+ new Ajax.Request(this.options.resultsURL,
+ { method: 'get', parameters: 'result=' + this.getResult(), asynchronous: false });
+ }
+ },
+ runTests: function() {
+ var test = this.tests[this.currentTest];
+ if (!test) {
+ // finished!
+ this.postResults();
+ this.logger.summary(this.summary());
+ return;
+ }
+ if(!test.isWaiting) {
+ this.logger.start(test.name);
+ }
+ test.run();
+ if(test.isWaiting) {
+ this.logger.message("Waiting for " + test.timeToWait + "ms");
+ setTimeout(this.runTests.bind(this), test.timeToWait || 1000);
+ } else {
+ this.logger.finish(test.status(), test.summary());
+ this.currentTest++;
+ // tail recursive, hopefully the browser will skip the stackframe
+ this.runTests();
+ }
+ },
+ summary: function() {
+ var assertions = 0;
+ var failures = 0;
+ var errors = 0;
+ var messages = [];
+ for(var i=0;i<this.tests.length;i++) {
+ assertions += this.tests[i].assertions;
+ failures += this.tests[i].failures;
+ errors += this.tests[i].errors;
+ }
+ return (
+ this.tests.length + " tests, " +
+ assertions + " assertions, " +
+ failures + " failures, " +
+ errors + " errors");
+ }
+}
+
+Test.Unit.Assertions = Class.create();
+Test.Unit.Assertions.prototype = {
+ initialize: function() {
+ this.assertions = 0;
+ this.failures = 0;
+ this.errors = 0;
+ this.messages = [];
+ },
+ summary: function() {
+ return (
+ this.assertions + " assertions, " +
+ this.failures + " failures, " +
+ this.errors + " errors" + "\n" +
+ this.messages.join("\n"));
+ },
+ pass: function() {
+ this.assertions++;
+ },
+ fail: function(message) {
+ this.failures++;
+ this.messages.push("Failure: " + message);
+ },
+ error: function(error) {
+ this.errors++;
+ this.messages.push(error.name + ": "+ error.message + "(" + Test.Unit.inspect(error) +")");
+ },
+ status: function() {
+ if (this.failures > 0) return 'failed';
+ if (this.errors > 0) return 'error';
+ return 'passed';
+ },
+ assert: function(expression) {
+ var message = arguments[1] || 'assert: got "' + Test.Unit.inspect(expression) + '"';
+ try { expression ? this.pass() :
+ this.fail(message); }
+ catch(e) { this.error(e); }
+ },
+ assertEqual: function(expected, actual) {
+ var message = arguments[2] || "assertEqual";
+ try { (expected == actual) ? this.pass() :
+ this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
+ '", actual "' + Test.Unit.inspect(actual) + '"'); }
+ catch(e) { this.error(e); }
+ },
+ assertNotEqual: function(expected, actual) {
+ var message = arguments[2] || "assertNotEqual";
+ try { (expected != actual) ? this.pass() :
+ this.fail(message + ': got "' + Test.Unit.inspect(actual) + '"'); }
+ catch(e) { this.error(e); }
+ },
+ assertNull: function(obj) {
+ var message = arguments[1] || 'assertNull'
+ try { (obj==null) ? this.pass() :
+ this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); }
+ catch(e) { this.error(e); }
+ },
+ assertHidden: function(element) {
+ var message = arguments[1] || 'assertHidden';
+ this.assertEqual("none", element.style.display, message);
+ },
+ assertNotNull: function(object) {
+ var message = arguments[1] || 'assertNotNull';
+ this.assert(object != null, message);
+ },
+ assertInstanceOf: function(expected, actual) {
+ var message = arguments[2] || 'assertInstanceOf';
+ try {
+ (actual instanceof expected) ? this.pass() :
+ this.fail(message + ": object was not an instance of the expected type"); }
+ catch(e) { this.error(e); }
+ },
+ assertNotInstanceOf: function(expected, actual) {
+ var message = arguments[2] || 'assertNotInstanceOf';
+ try {
+ !(actual instanceof expected) ? this.pass() :
+ this.fail(message + ": object was an instance of the not expected type"); }
+ catch(e) { this.error(e); }
+ },
+ _isVisible: function(element) {
+ element = $(element);
+ if(!element.parentNode) return true;
+ this.assertNotNull(element);
+ if(element.style && Element.getStyle(element, 'display') == 'none')
+ return false;
+
+ return this._isVisible(element.parentNode);
+ },
+ assertNotVisible: function(element) {
+ this.assert(!this._isVisible(element), Test.Unit.inspect(element) + " was not hidden and didn't have a hidden parent either. " + ("" || arguments[1]));
+ },
+ assertVisible: function(element) {
+ this.assert(this._isVisible(element), Test.Unit.inspect(element) + " was not visible. " + ("" || arguments[1]));
+ }
+}
+
+Test.Unit.Testcase = Class.create();
+Object.extend(Object.extend(Test.Unit.Testcase.prototype, Test.Unit.Assertions.prototype), {
+ initialize: function(name, test, setup, teardown) {
+ Test.Unit.Assertions.prototype.initialize.bind(this)();
+ this.name = name;
+ this.test = test || function() {};
+ this.setup = setup || function() {};
+ this.teardown = teardown || function() {};
+ this.isWaiting = false;
+ this.timeToWait = 1000;
+ },
+ wait: function(time, nextPart) {
+ this.isWaiting = true;
+ this.test = nextPart;
+ this.timeToWait = time;
+ },
+ run: function() {
+ try {
+ try {
+ if (!this.isWaiting) this.setup.bind(this)();
+ this.isWaiting = false;
+ this.test.bind(this)();
+ } finally {
+ if(!this.isWaiting) {
+ this.teardown.bind(this)();
+ }
+ }
+ }
+ catch(e) { this.error(e); }
+ }
+});
\ No newline at end of file
--- /dev/null
+// small but works-for-me stuff for testing javascripts
+// not ready for "production" use
+
+Object.inspect = function(obj) {
+ var info = [];
+
+ if(typeof obj in ["string","number"]) {
+ return obj;
+ } else {
+ for(property in obj)
+ if(typeof obj[property]!="function")
+ info.push(property + ' => ' +
+ (typeof obj[property] == "string" ?
+ '"' + obj[property] + '"' :
+ obj[property]));
+ }
+
+ return ("'" + obj + "' #" + typeof obj +
+ ": {" + info.join(", ") + "}");
+}
+
+// borrowed from http://www.schuerig.de/michael/javascript/stdext.js
+// Copyright (c) 2005, Michael Schuerig, michael@schuerig.de
+
+Array.flatten = function(array, excludeUndefined) {
+ if (excludeUndefined === undefined) {
+ excludeUndefined = false;
+ }
+ var result = [];
+ var len = array.length;
+ for (var i = 0; i < len; i++) {
+ var el = array[i];
+ if (el instanceof Array) {
+ var flat = el.flatten(excludeUndefined);
+ result = result.concat(flat);
+ } else if (!excludeUndefined || el != undefined) {
+ result.push(el);
+ }
+ }
+ return result;
+};
+
+if (!Array.prototype.flatten) {
+ Array.prototype.flatten = function(excludeUndefined) {
+ return Array.flatten(this, excludeUndefined);
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Builder = {
+ node: function(elementName) {
+ var element = document.createElement('div');
+ element.innerHTML =
+ "<" + elementName + "></" + elementName + ">";
+
+ // attributes (or text)
+ if(arguments[1])
+ if(this._isStringOrNumber(arguments[1]) ||
+ (arguments[1] instanceof Array)) {
+ this._children(element.firstChild, arguments[1]);
+ } else {
+ var attrs = this._attributes(arguments[1]);
+ if(attrs.length)
+ element.innerHTML = "<" +elementName + " " +
+ attrs + "></" + elementName + ">";
+ }
+
+ // text, or array of children
+ if(arguments[2])
+ this._children(element.firstChild, arguments[2]);
+
+ return element.firstChild;
+ },
+ _text: function(text) {
+ return document.createTextNode(text);
+ },
+ _attributes: function(attributes) {
+ var attrs = [];
+ for(attribute in attributes)
+ attrs.push((attribute=='className' ? 'class' : attribute) +
+ '="' + attributes[attribute].toString().escapeHTML() + '"');
+ return attrs.join(" ");
+ },
+ _children: function(element, children) {
+ if(typeof children=='object') { // array can hold nodes and text
+ children = children.flatten();
+ for(var i = 0; i<children.length; i++)
+ if(typeof children[i]=='object')
+ element.appendChild(children[i]);
+ else
+ if(this._isStringOrNumber(children[i]))
+ element.appendChild(this._text(children[i]));
+ } else
+ if(this._isStringOrNumber(children))
+ element.appendChild(this._text(children));
+ },
+ _isStringOrNumber: function(param) {
+ return(typeof param=='string' || typeof param=='number');
+ }
+}
+
+/* ------------- element ext -------------- */
+
+// adapted from http://dhtmlkitchen.com/learn/js/setstyle/index4.jsp
+// note: Safari return null on elements with display:none; see http://bugzilla.opendarwin.org/show_bug.cgi?id=4125
+// instead of "auto" values returns null so it's easier to use with || constructs
+
+String.prototype.camelize = function() {
+ var oStringList = this.split('-');
+ if(oStringList.length == 1)
+ return oStringList[0];
+ var ret = this.indexOf("-") == 0 ?
+ oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1) : oStringList[0];
+ for(var i = 1, len = oStringList.length; i < len; i++){
+ var s = oStringList[i];
+ ret += s.charAt(0).toUpperCase() + s.substring(1)
+ }
+ return ret;
+}
+
+Element.getStyle = function(element, style) {
+ element = $(element);
+ var value = element.style[style.camelize()];
+ if(!value)
+ if(document.defaultView && document.defaultView.getComputedStyle) {
+ var css = document.defaultView.getComputedStyle(element, null);
+ value = (css!=null) ? css.getPropertyValue(style) : null;
+ } else if(element.currentStyle) {
+ value = element.currentStyle[style.camelize()];
+ }
+ if(value=='auto') value = null;
+ return value;
+}
+
+Element.makePositioned = function(element) {
+ element = $(element);
+ if(Element.getStyle(element, 'position')=='static')
+ element.style.position = "relative";
+}
+
+Element.makeClipping = function(element) {
+ element = $(element);
+ element._overflow = Element.getStyle(element, 'overflow') || 'visible';
+ if(element._overflow!='hidden') element.style.overflow = 'hidden';
+}
+
+Element.undoClipping = function(element) {
+ element = $(element);
+ if(element._overflow!='hidden') element.style.overflow = element._overflow;
+}
+
+Element.collectTextNodesIgnoreClass = function(element, ignoreclass) {
+ var children = $(element).childNodes;
+ var text = "";
+ var classtest = new RegExp("^([^ ]+ )*" + ignoreclass+ "( [^ ]+)*$","i");
+
+ for (var i = 0; i < children.length; i++) {
+ if(children[i].nodeType==3) {
+ text+=children[i].nodeValue;
+ } else {
+ if((!children[i].className.match(classtest)) && children[i].hasChildNodes())
+ text += Element.collectTextNodesIgnoreClass(children[i], ignoreclass);
+ }
+ }
+
+ return text;
+}
+
+/*--------------------------------------------------------------------------*/
+
+Position.positionedOffset = function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ if (element) {
+ p = Element.getStyle(element,'position');
+ if(p == 'relative' || p == 'absolute') break;
+ }
+ } while (element);
+ return [valueL, valueT];
+}
+
+// Safari returns margins on body which is incorrect if the child is absolutely positioned.
+// for performance reasons, we create a specialized version of Position.positionedOffset for
+// KHTML/WebKit only
+
+if(/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
+ Position.cumulativeOffset = function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+
+ if (element.offsetParent==document.body)
+ if (Element.getStyle(element,'position')=='absolute') break;
+
+ element = element.offsetParent;
+ } while (element);
+ return [valueL, valueT];
+ }
+}
+
+Position.page = function(forElement) {
+ if(element == document.body) return [0, 0];
+ var valueT = 0, valueL = 0;
+
+ var element = forElement;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+
+ // Safari fix
+ if (element.offsetParent==document.body)
+ if (Element.getStyle(element,'position')=='absolute') break;
+
+ } while (element = element.offsetParent);
+
+ element = forElement;
+ do {
+ valueT -= element.scrollTop || 0;
+ valueL -= element.scrollLeft || 0;
+ } while (element = element.parentNode);
+
+ return [valueL, valueT];
+}
+
+// elements with display:none don't return an offsetParent,
+// fall back to manual calculation
+Position.offsetParent = function(element) {
+ if(element.offsetParent) return element.offsetParent;
+ if(element == document.body) return element;
+
+ while ((element = element.parentNode) && element != document.body)
+ if (Element.getStyle(element,'position')!='static')
+ return element;
+
+ return document.body;
+}
+
+Position.clone = function(source, target) {
+ var options = Object.extend({
+ setLeft: true,
+ setTop: true,
+ setWidth: true,
+ setHeight: true,
+ offsetTop: 0,
+ offsetLeft: 0
+ }, arguments[2] || {})
+
+ // find page position of source
+ source = $(source);
+ var p = Position.page(source);
+
+ // find coordinate system to use
+ target = $(target);
+ var delta = [0, 0];
+ var parent = null;
+ // delta [0,0] will do fine with position: fixed elements,
+ // position:absolute needs offsetParent deltas
+ if (Element.getStyle(target,'position') == 'absolute') {
+ parent = Position.offsetParent(target);
+ delta = Position.page(parent);
+ }
+
+ // correct by body offsets (fixes Safari)
+ if (parent==document.body) {
+ delta[0] -= document.body.offsetLeft;
+ delta[1] -= document.body.offsetTop;
+ }
+
+ // set position
+ if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + "px";
+ if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + "px";
+ if(options.setWidth) target.style.width = source.offsetWidth + "px";
+ if(options.setHeight) target.style.height = source.offsetHeight + "px";
+}
+
+Position.absolutize = function(element) {
+ element = $(element);
+ if(element.style.position=='absolute') return;
+ Position.prepare();
+
+ var offsets = Position.positionedOffset(element);
+ var top = offsets[1];
+ var left = offsets[0];
+ var width = element.clientWidth;
+ var height = element.clientHeight;
+
+ element._originalLeft = left - parseFloat(element.style.left || 0);
+ element._originalTop = top - parseFloat(element.style.top || 0);
+ element._originalWidth = element.style.width;
+ element._originalHeight = element.style.height;
+
+ element.style.position = 'absolute';
+ element.style.top = top + 'px';;
+ element.style.left = left + 'px';;
+ element.style.width = width + 'px';;
+ element.style.height = height + 'px';;
+}
+
+Position.relativize = function(element) {
+ element = $(element);
+ if(element.style.position=='relative') return;
+ Position.prepare();
+
+ element.style.position = 'relative';
+ var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
+ var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+ element.style.top = top + 'px';
+ element.style.left = left + 'px';
+ element.style.height = element._originalHeight;
+ element.style.width = element._originalWidth;
+}
+
+/*--------------------------------------------------------------------------*/
+
+Element.Class = {
+ // Element.toggleClass(element, className) toggles the class being on/off
+ // Element.toggleClass(element, className1, className2) toggles between both classes,
+ // defaulting to className1 if neither exist
+ toggle: function(element, className) {
+ if(Element.Class.has(element, className)) {
+ Element.Class.remove(element, className);
+ if(arguments.length == 3) Element.Class.add(element, arguments[2]);
+ } else {
+ Element.Class.add(element, className);
+ if(arguments.length == 3) Element.Class.remove(element, arguments[2]);
+ }
+ },
+
+ // gets space-delimited classnames of an element as an array
+ get: function(element) {
+ element = $(element);
+ return element.className.split(' ');
+ },
+
+ // functions adapted from original functions by Gavin Kistner
+ remove: function(element) {
+ element = $(element);
+ var regEx;
+ for(var i = 1; i < arguments.length; i++) {
+ regEx = new RegExp("(^|\\s)" + arguments[i] + "(\\s|$)", 'g');
+ element.className = element.className.replace(regEx, '')
+ }
+ },
+
+ add: function(element) {
+ element = $(element);
+ for(var i = 1; i < arguments.length; i++) {
+ Element.Class.remove(element, arguments[i]);
+ element.className += (element.className.length > 0 ? ' ' : '') + arguments[i];
+ }
+ },
+
+ // returns true if all given classes exist in said element
+ has: function(element) {
+ element = $(element);
+ if(!element || !element.className) return false;
+ var regEx;
+ for(var i = 1; i < arguments.length; i++) {
+ if((typeof arguments[i] == 'object') &&
+ (arguments[i].constructor == Array)) {
+ for(var j = 0; j < arguments[i].length; j++) {
+ regEx = new RegExp("(^|\\s)" + arguments[i][j] + "(\\s|$)");
+ if(!regEx.test(element.className)) return false;
+ }
+ } else {
+ regEx = new RegExp("(^|\\s)" + arguments[i] + "(\\s|$)");
+ if(!regEx.test(element.className)) return false;
+ }
+ }
+ return true;
+ },
+
+ // expects arrays of strings and/or strings as optional paramters
+ // Element.Class.has_any(element, ['classA','classB','classC'], 'classD')
+ has_any: function(element) {
+ element = $(element);
+ if(!element || !element.className) return false;
+ var regEx;
+ for(var i = 1; i < arguments.length; i++) {
+ if((typeof arguments[i] == 'object') &&
+ (arguments[i].constructor == Array)) {
+ for(var j = 0; j < arguments[i].length; j++) {
+ regEx = new RegExp("(^|\\s)" + arguments[i][j] + "(\\s|$)");
+ if(regEx.test(element.className)) return true;
+ }
+ } else {
+ regEx = new RegExp("(^|\\s)" + arguments[i] + "(\\s|$)");
+ if(regEx.test(element.className)) return true;
+ }
+ }
+ return false;
+ },
+
+ childrenWith: function(element, className) {
+ var children = $(element).getElementsByTagName('*');
+ var elements = new Array();
+
+ for (var i = 0; i < children.length; i++) {
+ if (Element.Class.has(children[i], className)) {
+ elements.push(children[i]);
+ break;
+ }
+ }
+
+ return elements;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+String.prototype.parseQuery = function() {
+ var str = this;
+ if(str.substring(0,1) == '?') {
+ str = this.substring(1);
+ }
+ var result = {};
+ var pairs = str.split('&');
+ for(var i = 0; i < pairs.length; i++) {
+ var pair = pairs[i].split('=');
+ result[pair[0]] = pair[1];
+ }
+ return result;
+}
\ No newline at end of file
}
function hide_imsg_popup() {
- new Effect.FadeTo('important_message', 0.0, 1000, 20, {complete:function() { hide_imsg_popup_old(); }} );
- // we still do it the old way afterwards, just in case the browser didn't dazzle us enough
+ // new Effect.FadeTo('important_message', 0.0, 1000, 20, {complete:function() { hide_imsg_popup_old(); }} );
+ hide_imsg_popup_old(); // Do it the old way for now, to avoid library conflicts
}