[damned-lies] Helper JS, CSS and image to create overlays (bug #666968)
- From: Gil Forcada Codinachs <gforcada src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [damned-lies] Helper JS, CSS and image to create overlays (bug #666968)
- Date: Sun, 30 Sep 2012 18:11:42 +0000 (UTC)
commit 88dd48f1c7a113dca89aec93cdcfc0e9f6090646
Author: Gil Forcada <gforcada gnome org>
Date: Thu Dec 29 00:42:07 2011 +0100
Helper JS, CSS and image to create overlays (bug #666968)
media/css/overlays.css | 57 ++
media/img/close.png | Bin 0 -> 1996 bytes
media/js/jquery.tools.js | 1672 ++++++++++++++++++++++++++++++++++++++++++
media/js/jquery.tools.min.js | 11 +
media/js/overlayhelpers.js | 595 +++++++++++++++
5 files changed, 2335 insertions(+), 0 deletions(-)
---
diff --git a/media/css/overlays.css b/media/css/overlays.css
new file mode 100644
index 0000000..8f9b43e
--- /dev/null
+++ b/media/css/overlays.css
@@ -0,0 +1,57 @@
+div.overlay {
+ width: auto;
+ height: auto;
+ /* initially overlay is hidden */
+ display: none;
+ /* some padding to layout nested elements nicely */
+ margin: 1em;
+ color: black;
+ font-weight: normal;
+}
+
+div.overlay-ajax {
+ width: 60%;
+ min-height: 100px;
+ z-index: 99; /* overlays should be above everything */
+}
+
+
+/* default close button positioned on upper-left corner */
+div.overlaybg div.close,
+div.overlay div.close {
+ background-image: url(/media/img/close.png);
+ position: absolute;
+ left: -16px;
+ top: -16px;
+ cursor: pointer;
+ height: 36px;
+ width: 36px;
+}
+
+.pb-ajax {
+ overflow-y: auto;
+}
+
+.pb-ajax, .pb-image {
+ background-color: #fff;
+ border: 1px solid #999;
+ white-space: normal;
+ box-shadow: 0 0 3em 0.5em #666;
+ -moz-box-shadow: 0 0 3em 0.5em #666;
+ -webkit-box-shadow: 0 0 3em #666;
+ padding-left: 30px;
+}
+
+.pb-ajax > div {
+ width: 92%;
+ padding: 0em;
+}
+
+div.overlay div.close span {
+ display: block;
+ height: 1px;
+ margin: -1px 0 0 -1px;
+ overflow: hidden;
+ padding: 0;
+ width: 1px;
+}
diff --git a/media/img/close.png b/media/img/close.png
new file mode 100644
index 0000000..d247e09
Binary files /dev/null and b/media/img/close.png differ
diff --git a/media/js/jquery.tools.js b/media/js/jquery.tools.js
new file mode 100644
index 0000000..5896b1a
--- /dev/null
+++ b/media/js/jquery.tools.js
@@ -0,0 +1,1672 @@
+/**
+ * @license
+ * jQuery Tools v1.2.6 Overlay - Overlay base. Extend it.
+ *
+ * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
+ *
+ * http://flowplayer.org/tools/overlay/
+ *
+ * Since: March 2008
+ * Date: 2011-10-26 11:02
+ */
+(function($) {
+
+ // static constructs
+ $.tools = $.tools || {version: 'v1.2.6'};
+
+ $.tools.overlay = {
+
+ addEffect: function(name, loadFn, closeFn) {
+ effects[name] = [loadFn, closeFn];
+ },
+
+ conf: {
+ close: null,
+ closeOnClick: true,
+ closeOnEsc: true,
+ closeSpeed: 'fast',
+ effect: 'default',
+
+ // since 1.2. fixed positioning not supported by IE6
+ fixed: !$.browser.msie || $.browser.version > 6,
+
+ left: 'center',
+ load: false, // 1.2
+ mask: null,
+ oneInstance: true,
+ speed: 'normal',
+ target: null, // target element to be overlayed. by default taken from [rel]
+ top: '10%'
+ }
+ };
+
+
+ var instances = [], effects = {};
+
+ // the default effect. nice and easy!
+ $.tools.overlay.addEffect('default',
+
+ /*
+ onLoad/onClose functions must be called otherwise none of the
+ user supplied callback methods won't be called
+ */
+ function(pos, onLoad) {
+
+ var conf = this.getConf(),
+ w = $(window);
+
+ if (!conf.fixed) {
+ pos.top += w.scrollTop();
+ pos.left += w.scrollLeft();
+ }
+
+ pos.position = conf.fixed ? 'fixed' : 'absolute';
+ this.getOverlay().css(pos).fadeIn(conf.speed, onLoad);
+
+ }, function(onClose) {
+ this.getOverlay().fadeOut(this.getConf().closeSpeed, onClose);
+ }
+ );
+
+
+ function Overlay(trigger, conf) {
+
+ // private variables
+ var self = this,
+ fire = trigger.add(self),
+ w = $(window),
+ closers,
+ overlay,
+ opened,
+ maskConf = $.tools.expose && (conf.mask || conf.expose),
+ uid = Math.random().toString().slice(10);
+
+
+ // mask configuration
+ if (maskConf) {
+ if (typeof maskConf == 'string') { maskConf = {color: maskConf}; }
+ maskConf.closeOnClick = maskConf.closeOnEsc = false;
+ }
+
+ // get overlay and trigger
+ var jq = conf.target || trigger.attr("rel");
+ overlay = jq ? $(jq) : null || trigger;
+
+ // overlay not found. cannot continue
+ if (!overlay.length) { throw "Could not find Overlay: " + jq; }
+
+ // trigger's click event
+ if (trigger && trigger.index(overlay) == -1) {
+ trigger.click(function(e) {
+ self.load(e);
+ return e.preventDefault();
+ });
+ }
+
+ // API methods
+ $.extend(self, {
+
+ load: function(e) {
+
+ // can be opened only once
+ if (self.isOpened()) { return self; }
+
+ // find the effect
+ var eff = effects[conf.effect];
+ if (!eff) { throw "Overlay: cannot find effect : \"" + conf.effect + "\""; }
+
+ // close other instances?
+ if (conf.oneInstance) {
+ $.each(instances, function() {
+ this.close(e);
+ });
+ }
+
+ // onBeforeLoad
+ e = e || $.Event();
+ e.type = "onBeforeLoad";
+ fire.trigger(e);
+ if (e.isDefaultPrevented()) { return self; }
+
+ // opened
+ opened = true;
+
+ // possible mask effect
+ if (maskConf) { $(overlay).expose(maskConf); }
+
+ // position & dimensions
+ var top = conf.top,
+ left = conf.left,
+ oWidth = overlay.outerWidth({margin:true}),
+ oHeight = overlay.outerHeight({margin:true});
+
+ if (typeof top == 'string') {
+ top = top == 'center' ? Math.max((w.height() - oHeight) / 2, 0) :
+ parseInt(top, 10) / 100 * w.height();
+ }
+
+ if (left == 'center') { left = Math.max((w.width() - oWidth) / 2, 0); }
+
+
+ // load effect
+ eff[0].call(self, {top: top, left: left}, function() {
+ if (opened) {
+ e.type = "onLoad";
+ fire.trigger(e);
+ }
+ });
+
+ // mask.click closes overlay
+ if (maskConf && conf.closeOnClick) {
+ $.mask.getMask().one("click", self.close);
+ }
+
+ // when window is clicked outside overlay, we close
+ if (conf.closeOnClick) {
+ $(document).bind("click." + uid, function(e) {
+ if (!$(e.target).parents(overlay).length) {
+ self.close(e);
+ }
+ });
+ }
+
+ // keyboard::escape
+ if (conf.closeOnEsc) {
+
+ // one callback is enough if multiple instances are loaded simultaneously
+ $(document).bind("keydown." + uid, function(e) {
+ if (e.keyCode == 27) {
+ self.close(e);
+ }
+ });
+ }
+
+
+ return self;
+ },
+
+ close: function(e) {
+
+ if (!self.isOpened()) { return self; }
+
+ e = e || $.Event();
+ e.type = "onBeforeClose";
+ fire.trigger(e);
+ if (e.isDefaultPrevented()) { return; }
+
+ opened = false;
+
+ // close effect
+ effects[conf.effect][1].call(self, function() {
+ e.type = "onClose";
+ fire.trigger(e);
+ });
+
+ // unbind the keyboard / clicking actions
+ $(document).unbind("click." + uid).unbind("keydown." + uid);
+
+ if (maskConf) {
+ $.mask.close();
+ }
+
+ return self;
+ },
+
+ getOverlay: function() {
+ return overlay;
+ },
+
+ getTrigger: function() {
+ return trigger;
+ },
+
+ getClosers: function() {
+ return closers;
+ },
+
+ isOpened: function() {
+ return opened;
+ },
+
+ // manipulate start, finish and speeds
+ getConf: function() {
+ return conf;
+ }
+
+ });
+
+ // callbacks
+ $.each("onBeforeLoad,onStart,onLoad,onBeforeClose,onClose".split(","), function(i, name) {
+
+ // configuration
+ if ($.isFunction(conf[name])) {
+ $(self).bind(name, conf[name]);
+ }
+
+ // API
+ self[name] = function(fn) {
+ if (fn) { $(self).bind(name, fn); }
+ return self;
+ };
+ });
+
+ // close button
+ closers = overlay.find(conf.close || ".close");
+
+ if (!closers.length && !conf.close) {
+ closers = $('<a class="close"></a>');
+ overlay.prepend(closers);
+ }
+
+ closers.click(function(e) {
+ self.close(e);
+ });
+
+ // autoload
+ if (conf.load) { self.load(); }
+
+ }
+
+ // jQuery plugin initialization
+ $.fn.overlay = function(conf) {
+
+ // already constructed --> return API
+ var el = this.data("overlay");
+ if (el) { return el; }
+
+ if ($.isFunction(conf)) {
+ conf = {onBeforeLoad: conf};
+ }
+
+ conf = $.extend(true, {}, $.tools.overlay.conf, conf);
+
+ this.each(function() {
+ el = new Overlay($(this), conf);
+ instances.push(el);
+ $(this).data("overlay", el);
+ });
+
+ return conf.api ? el: this;
+ };
+
+})(jQuery);
+
+
+/**
+ * @license
+ * jQuery Tools v1.2.6 Scrollable - New wave UI design
+ *
+ * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
+ *
+ * http://flowplayer.org/tools/scrollable.html
+ *
+ * Since: March 2008
+ * Date: 2011-10-26 11:02
+ */
+(function($) {
+
+ // static constructs
+ $.tools = $.tools || {version: 'v1.2.6'};
+
+ $.tools.scrollable = {
+
+ conf: {
+ activeClass: 'active',
+ circular: false,
+ clonedClass: 'cloned',
+ disabledClass: 'disabled',
+ easing: 'swing',
+ initialIndex: 0,
+ item: '> *',
+ items: '.items',
+ keyboard: true,
+ mousewheel: false,
+ next: '.next',
+ prev: '.prev',
+ size: 1,
+ speed: 400,
+ vertical: false,
+ touch: true,
+ wheelSpeed: 0
+ }
+ };
+
+ // get hidden element's width or height even though it's hidden
+ function dim(el, key) {
+ var v = parseInt(el.css(key), 10);
+ if (v) { return v; }
+ var s = el[0].currentStyle;
+ return s && s.width && parseInt(s.width, 10);
+ }
+
+ function find(root, query) {
+ var el = $(query);
+ return el.length < 2 ? el : root.parent().find(query);
+ }
+
+ var current;
+
+ // constructor
+ function Scrollable(root, conf) {
+
+ // current instance
+ var self = this,
+ fire = root.add(self),
+ itemWrap = root.children(),
+ index = 0,
+ vertical = conf.vertical;
+
+ if (!current) { current = self; }
+ if (itemWrap.length > 1) { itemWrap = $(conf.items, root); }
+
+
+ // in this version circular not supported when size > 1
+ if (conf.size > 1) { conf.circular = false; }
+
+ // methods
+ $.extend(self, {
+
+ getConf: function() {
+ return conf;
+ },
+
+ getIndex: function() {
+ return index;
+ },
+
+ getSize: function() {
+ return self.getItems().size();
+ },
+
+ getNaviButtons: function() {
+ return prev.add(next);
+ },
+
+ getRoot: function() {
+ return root;
+ },
+
+ getItemWrap: function() {
+ return itemWrap;
+ },
+
+ getItems: function() {
+ return itemWrap.find(conf.item).not("." + conf.clonedClass);
+ },
+
+ move: function(offset, time) {
+ return self.seekTo(index + offset, time);
+ },
+
+ next: function(time) {
+ return self.move(conf.size, time);
+ },
+
+ prev: function(time) {
+ return self.move(-conf.size, time);
+ },
+
+ begin: function(time) {
+ return self.seekTo(0, time);
+ },
+
+ end: function(time) {
+ return self.seekTo(self.getSize() -1, time);
+ },
+
+ focus: function() {
+ current = self;
+ return self;
+ },
+
+ addItem: function(item) {
+ item = $(item);
+
+ if (!conf.circular) {
+ itemWrap.append(item);
+ next.removeClass("disabled");
+
+ } else {
+ itemWrap.children().last().before(item);
+ itemWrap.children().first().replaceWith(item.clone().addClass(conf.clonedClass));
+ }
+
+ fire.trigger("onAddItem", [item]);
+ return self;
+ },
+
+
+ /* all seeking functions depend on this */
+ seekTo: function(i, time, fn) {
+
+ // ensure numeric index
+ if (!i.jquery) { i *= 1; }
+
+ // avoid seeking from end clone to the beginning
+ if (conf.circular && i === 0 && index == -1 && time !== 0) { return self; }
+
+ // check that index is sane
+ if (!conf.circular && i < 0 || i > self.getSize() || i < -1) { return self; }
+
+ var item = i;
+
+ if (i.jquery) {
+ i = self.getItems().index(i);
+
+ } else {
+ item = self.getItems().eq(i);
+ }
+
+ // onBeforeSeek
+ var e = $.Event("onBeforeSeek");
+ if (!fn) {
+ fire.trigger(e, [i, time]);
+ if (e.isDefaultPrevented() || !item.length) { return self; }
+ }
+
+ var props = vertical ? {top: -item.position().top} : {left: -item.position().left};
+
+ index = i;
+ current = self;
+ if (time === undefined) { time = conf.speed; }
+
+ itemWrap.animate(props, time, conf.easing, fn || function() {
+ fire.trigger("onSeek", [i]);
+ });
+
+ return self;
+ }
+
+ });
+
+ // callbacks
+ $.each(['onBeforeSeek', 'onSeek', 'onAddItem'], function(i, name) {
+
+ // configuration
+ if ($.isFunction(conf[name])) {
+ $(self).bind(name, conf[name]);
+ }
+
+ self[name] = function(fn) {
+ if (fn) { $(self).bind(name, fn); }
+ return self;
+ };
+ });
+
+ // circular loop
+ if (conf.circular) {
+
+ var cloned1 = self.getItems().slice(-1).clone().prependTo(itemWrap),
+ cloned2 = self.getItems().eq(1).clone().appendTo(itemWrap);
+
+ cloned1.add(cloned2).addClass(conf.clonedClass);
+
+ self.onBeforeSeek(function(e, i, time) {
+
+ if (e.isDefaultPrevented()) { return; }
+
+ /*
+ 1. animate to the clone without event triggering
+ 2. seek to correct position with 0 speed
+ */
+ if (i == -1) {
+ self.seekTo(cloned1, time, function() {
+ self.end(0);
+ });
+ return e.preventDefault();
+
+ } else if (i == self.getSize()) {
+ self.seekTo(cloned2, time, function() {
+ self.begin(0);
+ });
+ }
+
+ });
+
+ // seek over the cloned item
+
+ // if the scrollable is hidden the calculations for seekTo position
+ // will be incorrect (eg, if the scrollable is inside an overlay).
+ // ensure the elements are shown, calculate the correct position,
+ // then re-hide the elements. This must be done synchronously to
+ // prevent the hidden elements being shown to the user.
+
+ // See: https://github.com/jquerytools/jquerytools/issues#issue/87
+
+ var hidden_parents = root.parents().add(root).filter(function () {
+ if ($(this).css('display') === 'none') {
+ return true;
+ }
+ });
+ if (hidden_parents.length) {
+ hidden_parents.show();
+ self.seekTo(0, 0, function() {});
+ hidden_parents.hide();
+ }
+ else {
+ self.seekTo(0, 0, function() {});
+ }
+
+ }
+
+ // next/prev buttons
+ var prev = find(root, conf.prev).click(function(e) { e.stopPropagation(); self.prev(); }),
+ next = find(root, conf.next).click(function(e) { e.stopPropagation(); self.next(); });
+
+ if (!conf.circular) {
+ self.onBeforeSeek(function(e, i) {
+ setTimeout(function() {
+ if (!e.isDefaultPrevented()) {
+ prev.toggleClass(conf.disabledClass, i <= 0);
+ next.toggleClass(conf.disabledClass, i >= self.getSize() -1);
+ }
+ }, 1);
+ });
+
+ if (!conf.initialIndex) {
+ prev.addClass(conf.disabledClass);
+ }
+ }
+
+ if (self.getSize() < 2) {
+ prev.add(next).addClass(conf.disabledClass);
+ }
+
+ // mousewheel support
+ if (conf.mousewheel && $.fn.mousewheel) {
+ root.mousewheel(function(e, delta) {
+ if (conf.mousewheel) {
+ self.move(delta < 0 ? 1 : -1, conf.wheelSpeed || 50);
+ return false;
+ }
+ });
+ }
+
+ // touch event
+ if (conf.touch) {
+ var touch = {};
+
+ itemWrap[0].ontouchstart = function(e) {
+ var t = e.touches[0];
+ touch.x = t.clientX;
+ touch.y = t.clientY;
+ };
+
+ itemWrap[0].ontouchmove = function(e) {
+
+ // only deal with one finger
+ if (e.touches.length == 1 && !itemWrap.is(":animated")) {
+ var t = e.touches[0],
+ deltaX = touch.x - t.clientX,
+ deltaY = touch.y - t.clientY;
+
+ self[vertical && deltaY > 0 || !vertical && deltaX > 0 ? 'next' : 'prev']();
+ e.preventDefault();
+ }
+ };
+ }
+
+ if (conf.keyboard) {
+
+ $(document).bind("keydown.scrollable", function(evt) {
+
+ // skip certain conditions
+ if (!conf.keyboard || evt.altKey || evt.ctrlKey || evt.metaKey || $(evt.target).is(":input")) {
+ return;
+ }
+
+ // does this instance have focus?
+ if (conf.keyboard != 'static' && current != self) { return; }
+
+ var key = evt.keyCode;
+
+ if (vertical && (key == 38 || key == 40)) {
+ self.move(key == 38 ? -1 : 1);
+ return evt.preventDefault();
+ }
+
+ if (!vertical && (key == 37 || key == 39)) {
+ self.move(key == 37 ? -1 : 1);
+ return evt.preventDefault();
+ }
+
+ });
+ }
+
+ // initial index
+ if (conf.initialIndex) {
+ self.seekTo(conf.initialIndex, 0, function() {});
+ }
+ }
+
+
+ // jQuery plugin implementation
+ $.fn.scrollable = function(conf) {
+
+ // already constructed --> return API
+ var el = this.data("scrollable");
+ if (el) { return el; }
+
+ conf = $.extend({}, $.tools.scrollable.conf, conf);
+
+ this.each(function() {
+ el = new Scrollable($(this), conf);
+ $(this).data("scrollable", el);
+ });
+
+ return conf.api ? el: this;
+
+ };
+
+
+})(jQuery);
+
+/**
+ * @license
+ * jQuery Tools v1.2.6 Tabs- The basics of UI design.
+ *
+ * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
+ *
+ * http://flowplayer.org/tools/tabs/
+ *
+ * Since: November 2008
+ * Date: 2011-10-26 11:02
+ */
+(function($) {
+
+ // static constructs
+ $.tools = $.tools || {version: 'v1.2.6'};
+
+ $.tools.tabs = {
+
+ conf: {
+ tabs: 'a',
+ current: 'current',
+ onBeforeClick: null,
+ onClick: null,
+ effect: 'default',
+ initialIndex: 0,
+ event: 'click',
+ rotate: false,
+
+ // slide effect
+ slideUpSpeed: 400,
+ slideDownSpeed: 400,
+
+ // 1.2
+ history: false
+ },
+
+ addEffect: function(name, fn) {
+ effects[name] = fn;
+ }
+
+ };
+
+ var effects = {
+
+ // simple "toggle" effect
+ 'default': function(i, done) {
+ this.getPanes().hide().eq(i).show();
+ done.call();
+ },
+
+ /*
+ configuration:
+ - fadeOutSpeed (positive value does "crossfading")
+ - fadeInSpeed
+ */
+ fade: function(i, done) {
+
+ var conf = this.getConf(),
+ speed = conf.fadeOutSpeed,
+ panes = this.getPanes();
+
+ if (speed) {
+ panes.fadeOut(speed);
+ } else {
+ panes.hide();
+ }
+
+ panes.eq(i).fadeIn(conf.fadeInSpeed, done);
+ },
+
+ // for basic accordions
+ slide: function(i, done) {
+ var conf = this.getConf();
+
+ this.getPanes().slideUp(conf.slideUpSpeed);
+ this.getPanes().eq(i).slideDown(conf.slideDownSpeed, done);
+ },
+
+ /**
+ * AJAX effect
+ */
+ ajax: function(i, done) {
+ this.getPanes().eq(0).load(this.getTabs().eq(i).attr("href"), done);
+ }
+ };
+
+ /**
+ * Horizontal accordion
+ *
+ * @deprecated will be replaced with a more robust implementation
+ */
+
+ var
+ /**
+ * @type {Boolean}
+ *
+ * Mutex to control horizontal animation
+ * Disables clicking of tabs while animating
+ * They mess up otherwise as currentPane gets set *after* animation is done
+ */
+ animating,
+ /**
+ * @type {Number}
+ *
+ * Initial width of tab panes
+ */
+ w;
+
+ $.tools.tabs.addEffect("horizontal", function(i, done) {
+ if (animating) return; // don't allow other animations
+
+ var nextPane = this.getPanes().eq(i),
+ currentPane = this.getCurrentPane();
+
+ // store original width of a pane into memory
+ w || ( w = this.getPanes().eq(0).width() );
+ animating = true;
+
+ nextPane.show(); // hidden by default
+
+ // animate current pane's width to zero
+ // animate next pane's width at the same time for smooth animation
+ currentPane.animate({width: 0}, {
+ step: function(now){
+ nextPane.css("width", w-now);
+ },
+ complete: function(){
+ $(this).hide();
+ done.call();
+ animating = false;
+ }
+ });
+ // Dirty hack... onLoad, currentPant will be empty and nextPane will be the first pane
+ // If this is the case, manually run callback since the animation never occured, and reset animating
+ if (!currentPane.length){
+ done.call();
+ animating = false;
+ }
+ });
+
+
+ function Tabs(root, paneSelector, conf) {
+
+ var self = this,
+ trigger = root.add(this),
+ tabs = root.find(conf.tabs),
+ panes = paneSelector.jquery ? paneSelector : root.children(paneSelector),
+ current;
+
+
+ // make sure tabs and panes are found
+ if (!tabs.length) { tabs = root.children(); }
+ if (!panes.length) { panes = root.parent().find(paneSelector); }
+ if (!panes.length) { panes = $(paneSelector); }
+
+
+ // public methods
+ $.extend(this, {
+ click: function(i, e) {
+
+ var tab = tabs.eq(i);
+
+ if (typeof i == 'string' && i.replace("#", "")) {
+ tab = tabs.filter("[href*=" + i.replace("#", "") + "]");
+ i = Math.max(tabs.index(tab), 0);
+ }
+
+ if (conf.rotate) {
+ var last = tabs.length -1;
+ if (i < 0) { return self.click(last, e); }
+ if (i > last) { return self.click(0, e); }
+ }
+
+ if (!tab.length) {
+ if (current >= 0) { return self; }
+ i = conf.initialIndex;
+ tab = tabs.eq(i);
+ }
+
+ // current tab is being clicked
+ if (i === current) { return self; }
+
+ // possibility to cancel click action
+ e = e || $.Event();
+ e.type = "onBeforeClick";
+ trigger.trigger(e, [i]);
+ if (e.isDefaultPrevented()) { return; }
+
+ // call the effect
+ effects[conf.effect].call(self, i, function() {
+ current = i;
+ // onClick callback
+ e.type = "onClick";
+ trigger.trigger(e, [i]);
+ });
+
+ // default behaviour
+ tabs.removeClass(conf.current);
+ tab.addClass(conf.current);
+
+ return self;
+ },
+
+ getConf: function() {
+ return conf;
+ },
+
+ getTabs: function() {
+ return tabs;
+ },
+
+ getPanes: function() {
+ return panes;
+ },
+
+ getCurrentPane: function() {
+ return panes.eq(current);
+ },
+
+ getCurrentTab: function() {
+ return tabs.eq(current);
+ },
+
+ getIndex: function() {
+ return current;
+ },
+
+ next: function() {
+ return self.click(current + 1);
+ },
+
+ prev: function() {
+ return self.click(current - 1);
+ },
+
+ destroy: function() {
+ tabs.unbind(conf.event).removeClass(conf.current);
+ panes.find("a[href^=#]").unbind("click.T");
+ return self;
+ }
+
+ });
+
+ // callbacks
+ $.each("onBeforeClick,onClick".split(","), function(i, name) {
+
+ // configuration
+ if ($.isFunction(conf[name])) {
+ $(self).bind(name, conf[name]);
+ }
+
+ // API
+ self[name] = function(fn) {
+ if (fn) { $(self).bind(name, fn); }
+ return self;
+ };
+ });
+
+
+ if (conf.history && $.fn.history) {
+ $.tools.history.init(tabs);
+ conf.event = 'history';
+ }
+
+ // setup click actions for each tab
+ tabs.each(function(i) {
+ $(this).bind(conf.event, function(e) {
+ self.click(i, e);
+ return e.preventDefault();
+ });
+ });
+
+ // cross tab anchor link
+ panes.find("a[href^=#]").bind("click.T", function(e) {
+ self.click($(this).attr("href"), e);
+ });
+
+ // open initial tab
+ if (location.hash && conf.tabs == "a" && root.find("[href=" +location.hash+ "]").length) {
+ self.click(location.hash);
+
+ } else {
+ if (conf.initialIndex === 0 || conf.initialIndex > 0) {
+ self.click(conf.initialIndex);
+ }
+ }
+
+ }
+
+
+ // jQuery plugin implementation
+ $.fn.tabs = function(paneSelector, conf) {
+
+ // return existing instance
+ var el = this.data("tabs");
+ if (el) {
+ el.destroy();
+ this.removeData("tabs");
+ }
+
+ if ($.isFunction(conf)) {
+ conf = {onBeforeClick: conf};
+ }
+
+ // setup conf
+ conf = $.extend({}, $.tools.tabs.conf, conf);
+
+
+ this.each(function() {
+ el = new Tabs($(this), paneSelector, conf);
+ $(this).data("tabs", el);
+ });
+
+ return conf.api ? el: this;
+ };
+
+}) (jQuery);
+
+
+
+/**
+ * @license
+ * jQuery Tools v1.2.6 / Expose - Dim the lights
+ *
+ * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
+ *
+ * http://flowplayer.org/tools/toolbox/expose.html
+ *
+ * Since: Mar 2010
+ * Date: 2011-10-26 11:02
+ */
+(function($) {
+
+ // static constructs
+ $.tools = $.tools || {version: 'v1.2.6'};
+
+ var tool;
+
+ tool = $.tools.expose = {
+
+ conf: {
+ maskId: 'exposeMask',
+ loadSpeed: 'slow',
+ closeSpeed: 'fast',
+ closeOnClick: true,
+ closeOnEsc: true,
+
+ // css settings
+ zIndex: 9998,
+ opacity: 0.8,
+ startOpacity: 0,
+ color: '#fff',
+
+ // callbacks
+ onLoad: null,
+ onClose: null
+ }
+ };
+
+ /* one of the greatest headaches in the tool. finally made it */
+ function viewport() {
+
+ // the horror case
+ if ($.browser.msie) {
+
+ // if there are no scrollbars then use window.height
+ var d = $(document).height(), w = $(window).height();
+
+ return [
+ window.innerWidth || // ie7+
+ document.documentElement.clientWidth || // ie6
+ document.body.clientWidth, // ie6 quirks mode
+ d - w < 20 ? w : d
+ ];
+ }
+
+ // other well behaving browsers
+ return [$(document).width(), $(document).height()];
+ }
+
+ function call(fn) {
+ if (fn) { return fn.call($.mask); }
+ }
+
+ var mask, exposed, loaded, config, overlayIndex;
+
+
+ $.mask = {
+
+ load: function(conf, els) {
+
+ // already loaded ?
+ if (loaded) { return this; }
+
+ // configuration
+ if (typeof conf == 'string') {
+ conf = {color: conf};
+ }
+
+ // use latest config
+ conf = conf || config;
+
+ config = conf = $.extend($.extend({}, tool.conf), conf);
+
+ // get the mask
+ mask = $("#" + conf.maskId);
+
+ // or create it
+ if (!mask.length) {
+ mask = $('<div/>').attr("id", conf.maskId);
+ $("body").append(mask);
+ }
+
+ // set position and dimensions
+ var size = viewport();
+
+ mask.css({
+ position:'absolute',
+ top: 0,
+ left: 0,
+ width: size[0],
+ height: size[1],
+ display: 'none',
+ opacity: conf.startOpacity,
+ zIndex: conf.zIndex
+ });
+
+ if (conf.color) {
+ mask.css("backgroundColor", conf.color);
+ }
+
+ // onBeforeLoad
+ if (call(conf.onBeforeLoad) === false) {
+ return this;
+ }
+
+ // esc button
+ if (conf.closeOnEsc) {
+ $(document).bind("keydown.mask", function(e) {
+ if (e.keyCode == 27) {
+ $.mask.close(e);
+ }
+ });
+ }
+
+ // mask click closes
+ if (conf.closeOnClick) {
+ mask.bind("click.mask", function(e) {
+ $.mask.close(e);
+ });
+ }
+
+ // resize mask when window is resized
+ $(window).bind("resize.mask", function() {
+ $.mask.fit();
+ });
+
+ // exposed elements
+ if (els && els.length) {
+
+ overlayIndex = els.eq(0).css("zIndex");
+
+ // make sure element is positioned absolutely or relatively
+ $.each(els, function() {
+ var el = $(this);
+ if (!/relative|absolute|fixed/i.test(el.css("position"))) {
+ el.css("position", "relative");
+ }
+ });
+
+ // make elements sit on top of the mask
+ exposed = els.css({ zIndex: Math.max(conf.zIndex + 1, overlayIndex == 'auto' ? 0 : overlayIndex)});
+ }
+
+ // reveal mask
+ mask.css({display: 'block'}).fadeTo(conf.loadSpeed, conf.opacity, function() {
+ $.mask.fit();
+ call(conf.onLoad);
+ loaded = "full";
+ });
+
+ loaded = true;
+ return this;
+ },
+
+ close: function() {
+ if (loaded) {
+
+ // onBeforeClose
+ if (call(config.onBeforeClose) === false) { return this; }
+
+ mask.fadeOut(config.closeSpeed, function() {
+ call(config.onClose);
+ if (exposed) {
+ exposed.css({zIndex: overlayIndex});
+ }
+ loaded = false;
+ });
+
+ // unbind various event listeners
+ $(document).unbind("keydown.mask");
+ mask.unbind("click.mask");
+ $(window).unbind("resize.mask");
+ }
+
+ return this;
+ },
+
+ fit: function() {
+ if (loaded) {
+ var size = viewport();
+ mask.css({width: size[0], height: size[1]});
+ }
+ },
+
+ getMask: function() {
+ return mask;
+ },
+
+ isLoaded: function(fully) {
+ return fully ? loaded == 'full' : loaded;
+ },
+
+ getConf: function() {
+ return config;
+ },
+
+ getExposed: function() {
+ return exposed;
+ }
+ };
+
+ $.fn.mask = function(conf) {
+ $.mask.load(conf);
+ return this;
+ };
+
+ $.fn.expose = function(conf) {
+ $.mask.load(conf, this);
+ return this;
+ };
+
+
+})(jQuery);
+
+/**
+ * @license
+ * jQuery Tools v1.2.6 History "Back button for AJAX apps"
+ *
+ * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
+ *
+ * http://flowplayer.org/tools/toolbox/history.html
+ *
+ * Since: Mar 2010
+ * Date: 2011-10-26 11:02
+ */
+(function($) {
+
+ var hash, iframe, links, inited;
+
+ $.tools = $.tools || {version: 'v1.2.6'};
+
+ $.tools.history = {
+
+ init: function(els) {
+
+ if (inited) { return; }
+
+ // IE
+ if ($.browser.msie && $.browser.version < '8') {
+
+ // create iframe that is constantly checked for hash changes
+ if (!iframe) {
+ iframe = $("<iframe/>").attr("src", "javascript:false;").hide().get(0);
+ $("body").prepend(iframe);
+
+ setInterval(function() {
+ var idoc = iframe.contentWindow.document,
+ h = idoc.location.hash;
+
+ if (hash !== h) {
+ $(window).trigger("hash", h);
+ }
+ }, 100);
+
+ setIframeLocation(location.hash || '#');
+ }
+
+
+ // other browsers scans for location.hash changes directly without iframe hack
+ } else {
+ setInterval(function() {
+ var h = location.hash;
+ if (h !== hash) {
+ $(window).trigger("hash", h);
+ }
+ }, 100);
+ }
+
+ links = !links ? els : links.add(els);
+
+ els.click(function(e) {
+ var href = $(this).attr("href");
+ if (iframe) { setIframeLocation(href); }
+
+ // handle non-anchor links
+ if (href.slice(0, 1) != "#") {
+ location.href = "#" + href;
+ return e.preventDefault();
+ }
+
+ });
+
+ inited = true;
+ }
+ };
+
+
+ function setIframeLocation(h) {
+ if (h) {
+ var doc = iframe.contentWindow.document;
+ doc.open().close();
+ doc.location.hash = h;
+ }
+ }
+
+ // global histroy change listener
+ $(window).bind("hash", function(e, h) {
+ if (h) {
+ links.filter(function() {
+ var href = $(this).attr("href");
+ return href == h || href == h.replace("#", "");
+ }).trigger("history", [h]);
+ } else {
+ links.eq(0).trigger("history", [h]);
+ }
+
+ hash = h;
+
+ });
+
+
+ // jQuery plugin implementation
+ $.fn.history = function(fn) {
+
+ $.tools.history.init(this);
+
+ // return jQuery
+ return this.bind("history", fn);
+ };
+
+})(jQuery);
+
+
+/**
+ * @license
+ * jQuery Tools v1.2.6 Tooltip - UI essentials
+ *
+ * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
+ *
+ * http://flowplayer.org/tools/tooltip/
+ *
+ * Since: November 2008
+ * Date: 2011-10-26 11:02
+ */
+(function($) {
+ // static constructs
+ $.tools = $.tools || {version: 'v1.2.6'};
+
+ $.tools.tooltip = {
+
+ conf: {
+
+ // default effect variables
+ effect: 'toggle',
+ fadeOutSpeed: "fast",
+ predelay: 0,
+ delay: 30,
+ opacity: 1,
+ tip: 0,
+ fadeIE: false, // enables fade effect in IE
+
+ // 'top', 'bottom', 'right', 'left', 'center'
+ position: ['top', 'center'],
+ offset: [0, 0],
+ relative: false,
+ cancelDefault: true,
+
+ // type to event mapping
+ events: {
+ def: "mouseenter,mouseleave",
+ input: "focus,blur",
+ widget: "focus mouseenter,blur mouseleave",
+ tooltip: "mouseenter,mouseleave"
+ },
+
+ // 1.2
+ layout: '<div/>',
+ tipClass: 'tooltip'
+ },
+
+ addEffect: function(name, loadFn, hideFn) {
+ effects[name] = [loadFn, hideFn];
+ }
+ };
+
+
+ var effects = {
+ toggle: [
+ function(done) {
+ var conf = this.getConf(), tip = this.getTip(), o = conf.opacity;
+ if (o < 1) { tip.css({opacity: o}); }
+ tip.show();
+ done.call();
+ },
+
+ function(done) {
+ this.getTip().hide();
+ done.call();
+ }
+ ],
+
+ fade: [
+ function(done) {
+ var conf = this.getConf();
+ if (!$.browser.msie || conf.fadeIE) {
+ this.getTip().fadeTo(conf.fadeInSpeed, conf.opacity, done);
+ }
+ else {
+ this.getTip().show();
+ done();
+ }
+ },
+ function(done) {
+ var conf = this.getConf();
+ if (!$.browser.msie || conf.fadeIE) {
+ this.getTip().fadeOut(conf.fadeOutSpeed, done);
+ }
+ else {
+ this.getTip().hide();
+ done();
+ }
+ }
+ ]
+ };
+
+
+ /* calculate tip position relative to the trigger */
+ function getPosition(trigger, tip, conf) {
+
+
+ // get origin top/left position
+ var top = conf.relative ? trigger.position().top : trigger.offset().top,
+ left = conf.relative ? trigger.position().left : trigger.offset().left,
+ pos = conf.position[0];
+
+ top -= tip.outerHeight() - conf.offset[0];
+ left += trigger.outerWidth() + conf.offset[1];
+
+ // iPad position fix
+ if (/iPad/i.test(navigator.userAgent)) {
+ top -= $(window).scrollTop();
+ }
+
+ // adjust Y
+ var height = tip.outerHeight() + trigger.outerHeight();
+ if (pos == 'center') { top += height / 2; }
+ if (pos == 'bottom') { top += height; }
+
+
+ // adjust X
+ pos = conf.position[1];
+ var width = tip.outerWidth() + trigger.outerWidth();
+ if (pos == 'center') { left -= width / 2; }
+ if (pos == 'left') { left -= width; }
+
+ return {top: top, left: left};
+ }
+
+
+
+ function Tooltip(trigger, conf) {
+
+ var self = this,
+ fire = trigger.add(self),
+ tip,
+ timer = 0,
+ pretimer = 0,
+ title = trigger.attr("title"),
+ tipAttr = trigger.attr("data-tooltip"),
+ effect = effects[conf.effect],
+ shown,
+
+ // get show/hide configuration
+ isInput = trigger.is(":input"),
+ isWidget = isInput && trigger.is(":checkbox, :radio, select, :button, :submit"),
+ type = trigger.attr("type"),
+ evt = conf.events[type] || conf.events[isInput ? (isWidget ? 'widget' : 'input') : 'def'];
+
+
+ // check that configuration is sane
+ if (!effect) { throw "Nonexistent effect \"" + conf.effect + "\""; }
+
+ evt = evt.split(/,\s*/);
+ if (evt.length != 2) { throw "Tooltip: bad events configuration for " + type; }
+
+
+ // trigger --> show
+ trigger.bind(evt[0], function(e) {
+
+ clearTimeout(timer);
+ if (conf.predelay) {
+ pretimer = setTimeout(function() { self.show(e); }, conf.predelay);
+
+ } else {
+ self.show(e);
+ }
+
+ // trigger --> hide
+ }).bind(evt[1], function(e) {
+ clearTimeout(pretimer);
+ if (conf.delay) {
+ timer = setTimeout(function() { self.hide(e); }, conf.delay);
+
+ } else {
+ self.hide(e);
+ }
+
+ });
+
+
+ // remove default title
+ if (title && conf.cancelDefault) {
+ trigger.removeAttr("title");
+ trigger.data("title", title);
+ }
+
+ $.extend(self, {
+
+ show: function(e) {
+
+ // tip not initialized yet
+ if (!tip) {
+
+ // data-tooltip
+ if (tipAttr) {
+ tip = $(tipAttr);
+
+ // single tip element for all
+ } else if (conf.tip) {
+ tip = $(conf.tip).eq(0);
+
+ // autogenerated tooltip
+ } else if (title) {
+ tip = $(conf.layout).addClass(conf.tipClass).appendTo(document.body)
+ .hide().append(title);
+
+ // manual tooltip
+ } else {
+ tip = trigger.next();
+ if (!tip.length) { tip = trigger.parent().next(); }
+ }
+
+ if (!tip.length) { throw "Cannot find tooltip for " + trigger; }
+ }
+
+ if (self.isShown()) { return self; }
+
+ // stop previous animation
+ tip.stop(true, true);
+
+ // get position
+ var pos = getPosition(trigger, tip, conf);
+
+ // restore title for single tooltip element
+ if (conf.tip) {
+ tip.html(trigger.data("title"));
+ }
+
+ // onBeforeShow
+ e = $.Event();
+ e.type = "onBeforeShow";
+ fire.trigger(e, [pos]);
+ if (e.isDefaultPrevented()) { return self; }
+
+
+ // onBeforeShow may have altered the configuration
+ pos = getPosition(trigger, tip, conf);
+
+ // set position
+ tip.css({position:'absolute', top: pos.top, left: pos.left});
+
+ shown = true;
+
+ // invoke effect
+ effect[0].call(self, function() {
+ e.type = "onShow";
+ shown = 'full';
+ fire.trigger(e);
+ });
+
+
+ // tooltip events
+ var event = conf.events.tooltip.split(/,\s*/);
+
+ if (!tip.data("__set")) {
+
+ tip.unbind(event[0]).bind(event[0], function() {
+ clearTimeout(timer);
+ clearTimeout(pretimer);
+ });
+
+ if (event[1] && !trigger.is("input:not(:checkbox, :radio), textarea")) {
+ tip.unbind(event[1]).bind(event[1], function(e) {
+
+ // being moved to the trigger element
+ if (e.relatedTarget != trigger[0]) {
+ trigger.trigger(evt[1].split(" ")[0]);
+ }
+ });
+ }
+
+ // bind agein for if same tip element
+ if (!conf.tip) tip.data("__set", true);
+ }
+
+ return self;
+ },
+
+ hide: function(e) {
+
+ if (!tip || !self.isShown()) { return self; }
+
+ // onBeforeHide
+ e = $.Event();
+ e.type = "onBeforeHide";
+ fire.trigger(e);
+ if (e.isDefaultPrevented()) { return; }
+
+ shown = false;
+
+ effects[conf.effect][1].call(self, function() {
+ e.type = "onHide";
+ fire.trigger(e);
+ });
+
+ return self;
+ },
+
+ isShown: function(fully) {
+ return fully ? shown == 'full' : shown;
+ },
+
+ getConf: function() {
+ return conf;
+ },
+
+ getTip: function() {
+ return tip;
+ },
+
+ getTrigger: function() {
+ return trigger;
+ }
+
+ });
+
+ // callbacks
+ $.each("onHide,onBeforeShow,onShow,onBeforeHide".split(","), function(i, name) {
+
+ // configuration
+ if ($.isFunction(conf[name])) {
+ $(self).bind(name, conf[name]);
+ }
+
+ // API
+ self[name] = function(fn) {
+ if (fn) { $(self).bind(name, fn); }
+ return self;
+ };
+ });
+
+ }
+
+
+ // jQuery plugin implementation
+ $.fn.tooltip = function(conf) {
+
+ // return existing instance
+ var api = this.data("tooltip");
+ if (api) { return api; }
+
+ conf = $.extend(true, {}, $.tools.tooltip.conf, conf);
+
+ // position can also be given as string
+ if (typeof conf.position == 'string') {
+ conf.position = conf.position.split(/,?\s/);
+ }
+
+ // install tooltip for each entry in jQuery object
+ this.each(function() {
+ api = new Tooltip($(this), conf);
+ $(this).data("tooltip", api);
+ });
+
+ return conf.api ? api: this;
+ };
+
+}) (jQuery);
diff --git a/media/js/jquery.tools.min.js b/media/js/jquery.tools.min.js
new file mode 100644
index 0000000..5e047b1
--- /dev/null
+++ b/media/js/jquery.tools.min.js
@@ -0,0 +1,11 @@
+/*!
+ * jQuery Tools v1.2.6 - The missing UI library for the Web
+ *
+ * overlay/overlay.js
+ *
+ * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
+ *
+ * http://flowplayer.org/tools/
+ *
+ */
+(function(a){a.tools=a.tools||{version:"v1.2.6"},a.tools.overlay={addEffect:function(a,b,d){c[a]=[b,d]},conf:{close:null,closeOnClick:!0,closeOnEsc:!0,closeSpeed:"fast",effect:"default",fixed:!a.browser.msie||a.browser.version>6,left:"center",load:!1,mask:null,oneInstance:!0,speed:"normal",target:null,top:"10%"}};var b=[],c={};a.tools.overlay.addEffect("default",function(b,c){var d=this.getConf(),e=a(window);d.fixed||(b.top+=e.scrollTop(),b.left+=e.scrollLeft()),b.position=d.fixed?"fixed":"absolute",this.getOverlay().css(b).fadeIn(d.speed,c)},function(a){this.getOverlay().fadeOut(this.getConf().closeSpeed,a)});function d(d,e){var f=this,g=d.add(f),h=a(window),i,j,k,l=a.tools.expose&&(e.mask||e.expose),m=Math.random().toString().slice(10);l&&(typeof l=="string"&&(l={color:l}),l.closeOnClick=l.closeOnEsc=!1);var n=e.target||d.attr("rel");j=n?a(n):null||d;if(!j.length)throw"Could not find Overlay: "+n;d&&d.index(j)==-1&&d.click(function(a){f.load(a);return a.preventDefault()}),
a.extend(f,{load:function(d){if(f.isOpened())return f;var i=c[e.effect];if(!i)throw"Overlay: cannot find effect : \""+e.effect+"\"";e.oneInstance&&a.each(b,function(){this.close(d)}),d=d||a.Event(),d.type="onBeforeLoad",g.trigger(d);if(d.isDefaultPrevented())return f;k=!0,l&&a(j).expose(l);var n=e.top,o=e.left,p=j.outerWidth({margin:!0}),q=j.outerHeight({margin:!0});typeof n=="string"&&(n=n=="center"?Math.max((h.height()-q)/2,0):parseInt(n,10)/100*h.height()),o=="center"&&(o=Math.max((h.width()-p)/2,0)),i[0].call(f,{top:n,left:o},function(){k&&(d.type="onLoad",g.trigger(d))}),l&&e.closeOnClick&&a.mask.getMask().one("click",f.close),e.closeOnClick&&a(document).bind("click."+m,function(b){a(b.target).parents(j).length||f.close(b)}),e.closeOnEsc&&a(document).bind("keydown."+m,function(a){a.keyCode==27&&f.close(a)});return f},close:function(b){if(!f.isOpened())return f;b=b||a.Event(),b.type="onBeforeClose",g.trigger(b);if(!b.isDefaultPrevented()){k=!1,c[e.effect][1].call(f,funct
ion(){b.type="onClose",g.trigger(b)}),a(document).unbind("click."+m).unbind("keydown."+m),l&&a.mask.close();return f}},getOverlay:function(){return j},getTrigger:function(){return d},getClosers:function(){return i},isOpened:function(){return k},getConf:function(){return e}}),a.each("onBeforeLoad,onStart,onLoad,onBeforeClose,onClose".split(","),function(b,c){a.isFunction(e[c])&&a(f).bind(c,e[c]),f[c]=function(b){b&&a(f).bind(c,b);return f}}),i=j.find(e.close||".close"),!i.length&&!e.close&&(i=a("<a class=\"close\"></a>"),j.prepend(i)),i.click(function(a){f.close(a)}),e.load&&f.load()}a.fn.overlay=function(c){var e=this.data("overlay");if(e)return e;a.isFunction(c)&&(c={onBeforeLoad:c}),c=a.extend(!0,{},a.tools.overlay.conf,c),this.each(function(){e=new d(a(this),c),b.push(e),a(this).data("overlay",e)});return c.api?e:this}})(jQuery);
diff --git a/media/js/overlayhelpers.js b/media/js/overlayhelpers.js
new file mode 100644
index 0000000..0137d5f
--- /dev/null
+++ b/media/js/overlayhelpers.js
@@ -0,0 +1,595 @@
+/*****************
+
+ jQuery Tools overlay helpers.
+
+ Copyright  2010, The Plone Foundation
+ Licensed under the GPL, see LICENSE.txt for details.
+
+*****************/
+
+/*jslint browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, newcap: true, immed: true, regexp: false, white:true */
+/*global jQuery, ajax_noresponse_message, window */
+
+// Name space object for pipbox
+var pb = {spinner: {}, overlay_counter: 1};
+
+jQuery.tools.overlay.conf.oneInstance = false;
+
+(function($) {
+ // override jqt default effect to take account of position
+ // of parent elements
+ jQuery.tools.overlay.addEffect('default',
+ function(pos, onLoad) {
+ var conf = this.getConf(),
+ w = $(window),
+ ovl = this.getOverlay(),
+ op = ovl.parent().offsetParent().offset();
+
+ if (!conf.fixed) {
+ pos.top += w.scrollTop() - op.top;
+ pos.left += w.scrollLeft() - op.left;
+ }
+
+ pos.position = conf.fixed ? 'fixed' : 'absolute';
+ ovl.css(pos).fadeIn(conf.speed, onLoad);
+
+ }, function(onClose) {
+ this.getOverlay().fadeOut(this.getConf().closeSpeed, onClose);
+ }
+ );
+
+ pb.spinner.show = function () {
+ $('body').css('cursor', 'wait');
+ };
+ pb.spinner.hide = function () {
+ $('body').css('cursor', '');
+ };
+}) (jQuery);
+
+
+
+jQuery(function ($) {
+
+ /******
+ $.fn.prepOverlay jQuery plugin to inject overlay target into DOM and
+ annotate it with the data we'll need in order to display it.
+ ******/
+ $.fn.prepOverlay = function (pba) {
+ return this.each(function () {
+ var o, pbo, config, onBeforeLoad, onLoad, src, parts;
+
+ o = $(this);
+
+ // copy options so that it's not just a reference
+ // to the parameter.
+ pbo = $.extend(true, {'width':'60%'}, pba);
+
+ // set overlay configuration
+ config = pbo.config || {};
+
+ // set onBeforeLoad handler
+ onBeforeLoad = pb[pbo.subtype];
+ if (onBeforeLoad) {
+ config.onBeforeLoad = onBeforeLoad;
+ }
+ onLoad = config.onLoad;
+ config.onLoad = function () {
+ if (onLoad) {
+ onLoad.apply(this, arguments);
+ }
+ pb.fi_focus(this.getOverlay());
+ };
+
+ // be promiscuous, pick up the url from
+ // href, src or action attributes
+ src = o.attr('href') || o.attr('src') || o.attr('action');
+
+ // translate url with config specifications
+ if (pbo.urlmatch) {
+ src = src.replace(new RegExp(pbo.urlmatch), pbo.urlreplace);
+ }
+
+ if (pbo.subtype === 'inline') {
+ // we're going to let tools' overlay do all the real
+ // work. Just get the markers in place.
+ src = src.replace(/^.+#/, '#');
+ $("[id='" + src.replace('#', '') + "']")
+ .addClass('overlay');
+ o.removeAttr('href').attr('rel', src);
+ // use overlay on the source (clickable) element
+ o.overlay();
+ } else {
+ // save various bits of information from the pbo options,
+ // and enable the overlay.
+
+ // this is not inline, so in one fashion or another
+ // we'll be loading it via the beforeLoad callback.
+ // create a unique id for a target element
+ pbo.nt = 'pb_' + pb.overlay_counter;
+ pb.overlay_counter += 1;
+
+ pbo.selector = pbo.filter || pbo.selector;
+ if (!pbo.selector) {
+ // see if one's been supplied in the src
+ parts = src.split(' ');
+ src = parts.shift();
+ pbo.selector = parts.join(' ');
+ }
+
+ pbo.src = src;
+ pbo.config = config;
+ pbo.source = o;
+
+ // remove any existing overlay and overlay handler
+ pb.remove_overlay(o);
+
+ // save options on trigger element
+ o.data('pbo', pbo);
+
+ // mark the source with a rel attribute so we can find
+ // the overlay, and a special class for styling
+ o.attr('rel', '#' + pbo.nt);
+ o.addClass('link-overlay');
+
+ // for some subtypes, we're setting click handlers
+ // and attaching overlay to the target element. That's
+ // so we'll know the dimensions early.
+ // Others, like iframe, just use overlay.
+ switch (pbo.subtype) {
+ case 'image':
+ o.click(pb.image_click);
+ break;
+ case 'ajax':
+ o.click(pb.ajax_click);
+ break;
+ case 'iframe':
+ pb.create_content_div(pbo);
+ o.overlay(config);
+ break;
+ default:
+ throw "Unsupported overlay type";
+ }
+
+ // in case the click source wasn't
+ // already a link.
+ o.css('cursor', 'pointer');
+ }
+ });
+ };
+
+
+ /******
+ pb.remove_overlay
+ Remove the overlay and handler associated with a jquery wrapped
+ trigger object
+ ******/
+ pb.remove_overlay = function (o) {
+ var old_data = o.data('pbo');
+ if (old_data) {
+ switch (old_data.subtype) {
+ case 'image':
+ o.unbind('click', pb.image_click);
+ break;
+ case 'ajax':
+ o.unbind('click', pb.ajax_click);
+ break;
+ default:
+ // it's probably the jqt overlay click handler,
+ // but we don't know the handler and are forced
+ // to do a generic unbind of click handlers.
+ o.unbind('click');
+ }
+ if (old_data.nt) {
+ $('#' + old_data.nt).remove();
+ }
+ }
+ };
+
+
+ /******
+ pb.create_content_div
+ create a div to act as an overlay; append it to
+ the body; return it
+ ******/
+ pb.create_content_div = function (pbo, trigger) {
+ var content,
+ top,
+ pbw = pbo.width;
+
+ content = $(
+ '<div id="' + pbo.nt +
+ '" class="overlay overlay-' + pbo.subtype +
+ ' ' + (pbo.cssclass || '') +
+ '"><div class="close"><span>Close</span></div></div>'
+ );
+
+ content.data('pbo', pbo);
+
+ // if a width option is specified, set it on the overlay div,
+ // computing against the window width if a % was specified.
+ if (pbw) {
+ if (pbw.indexOf('%') > 0) {
+ content.width(parseInt(pbw, 10) / 100 * $(window).width());
+ } else {
+ content.width(pbw);
+ }
+ }
+
+ // add the target element at the end of the body.
+ if (trigger) {
+ trigger.after(content);
+ } else {
+ content.appendTo($("body"));
+ }
+
+ return content;
+ };
+
+
+ /******
+ pb.image_click
+ click handler for image loads
+ ******/
+ pb.image_click = function (event) {
+ var ethis, content, api, img, el, pbo;
+
+ ethis = $(this);
+ pbo = ethis.data('pbo');
+
+ // find target container
+ content = $(ethis.attr('rel'));
+ if (!content.length) {
+ content = pb.create_content_div(pbo);
+ content.overlay(pbo.config);
+ }
+ api = content.overlay();
+
+ // is the image loaded yet?
+ if (content.find('img').length === 0) {
+ // load the image.
+ if (pbo.src) {
+ pb.spinner.show();
+
+ // create the image and stuff it
+ // into our target
+ img = new Image();
+ img.src = pbo.src;
+ el = $(img);
+ content.append(el.addClass('pb-image'));
+
+ // Now, we'll cause the overlay to
+ // load when the image is loaded.
+ el.load(function () {
+ pb.spinner.hide();
+ api.load();
+ });
+
+ }
+ } else {
+ api.load();
+ }
+
+ return false;
+ };
+
+
+ /******
+ pb.fi_focus
+ First-input focus inside $ selection.
+ ******/
+ pb.fi_focus = function (jqo) {
+ if (! jqo.find("form div.error :input:first").focus().length) {
+ jqo.find("form :input:visible:first").focus();
+ }
+ };
+
+
+ /******
+ pb.ajax_error_recover
+ jQuery's ajax load function does not load error responses.
+ This routine returns the cooked error response.
+ ******/
+ pb.ajax_error_recover = function (responseText, selector) {
+ var tcontent = $('<div/>')
+ .append(responseText.replace(/<script(.|\s)*?\/script>/gi, ""));
+ return selector ? tcontent.find(selector) : tcontent;
+ };
+
+
+ /******
+ pb.add_ajax_load
+ Adds a hidden ajax_load input to form
+ ******/
+ pb.add_ajax_load = function (form) {
+ if (form.find('input[name=ajax_load]').length === 0) {
+ form.prepend($('<input type="hidden" name="ajax_load" value="' +
+ (new Date().getTime()) +
+ '" />'));
+ }
+ };
+
+ /******
+ pb.prep_ajax_form
+ Set up form with ajaxForm, including success and error handlers.
+ ******/
+ pb.prep_ajax_form = function (form) {
+ var ajax_parent = form.closest('.pb-ajax'),
+ data_parent = ajax_parent.closest('.overlay-ajax'),
+ pbo = data_parent.data('pbo'),
+ formtarget = pbo.formselector,
+ closeselector = pbo.closeselector,
+ beforepost = pbo.beforepost,
+ afterpost = pbo.afterpost,
+ noform = pbo.noform,
+ api = data_parent.overlay(),
+ selector = pbo.selector,
+ options = {};
+
+ options.beforeSerialize = function () {
+ pb.spinner.show();
+ };
+
+ if (beforepost) {
+ options.beforeSubmit = function (arr, form, options) {
+ return beforepost(form, arr, options);
+ };
+ }
+ options.success = function (responseText, statusText, xhr, form) {
+ $(document).trigger('formOverlayStart', [this, responseText, statusText, xhr, form]);
+ // success comes in many forms, some of which are errors;
+ //
+
+ var el, myform, success, target;
+
+ success = statusText === "success" || statusText === "notmodified";
+
+ if (! success) {
+ // The responseText parameter is actually xhr
+ responseText = responseText.responseText;
+ }
+ // strip inline script tags
+ responseText = responseText.replace(/<script(.|\s)*?\/script>/gi, "");
+
+ // create a div containing the optionally filtered response
+ el = $('<div />').append(
+ selector ?
+ // a lesson learned from the jQuery source: $(responseText)
+ // will not work well unless responseText is well-formed;
+ // appending to a div is more robust, and automagically
+ // removes the html/head/body outer tagging.
+ $('<div />').append(responseText).find(selector)
+ :
+ responseText
+ );
+
+ // afterpost callback
+ if (success && afterpost) {
+ afterpost(el, data_parent);
+ }
+
+ myform = el.find(formtarget);
+ if (success && myform.length) {
+ ajax_parent.empty().append(el);
+ pb.fi_focus(ajax_parent);
+
+ pb.add_ajax_load(myform);
+ // attach submit handler with the same options
+ myform.ajaxForm(options);
+
+ // attach close to element id'd by closeselector
+ if (closeselector) {
+ el.find(closeselector).click(function (event) {
+ api.close();
+ return false;
+ });
+ }
+ $(document).trigger('formOverlayLoadSuccess', [this, myform, api, pb, ajax_parent]);
+ } else {
+ // there's no form in our new content or there's been an error
+ if (success) {
+ if (typeof(noform) === "function") {
+ // get action from callback
+ noform = noform(this);
+ }
+ } else {
+ noform = statusText;
+ }
+
+
+ switch (noform) {
+ case 'close':
+ api.close();
+ break;
+ case 'reload':
+ api.close();
+ pb.spinner.show();
+ // location.reload results in a repost
+ // dialog in some browsers; very unlikely to
+ // be what we want.
+ location.replace(location.href);
+ break;
+ case 'redirect':
+ api.close();
+ pb.spinner.show();
+ target = pbo.redirect;
+ if (typeof(target) === "function") {
+ // get target from callback
+ target = target(this, responseText);
+ }
+ location.replace(target);
+ break;
+ default:
+ if (el.children()) {
+ // show what we've got
+ ajax_parent.empty().append(el);
+ } else {
+ api.close();
+ }
+ }
+ $(document).trigger('formOverlayLoadFailure', [this, myform, api, pb, ajax_parent, noform]);
+ }
+ pb.spinner.hide();
+ };
+ // error and success callbacks are the same
+ options.error = options.success;
+
+ pb.add_ajax_load(form);
+ form.ajaxForm(options);
+ };
+
+
+ /******
+ pb.ajax_click
+ Click handler for ajax sources. The job of this routine
+ is to do the ajax load of the overlay element, then
+ call the JQT overlay loader.
+ ******/
+ pb.ajax_click = function (event) {
+ var ethis = $(this),
+ pbo,
+ content,
+ api,
+ src,
+ el,
+ selector,
+ formtarget,
+ closeselector,
+ sep;
+
+ e = $.Event();
+ e.type = "beforeAjaxClickHandled";
+ $(document).trigger(e, [this, event]);
+ if (e.isDefaultPrevented()) { return; }
+
+ pbo = ethis.data('pbo');
+
+ content = pb.create_content_div(pbo, ethis);
+ // pbo.config.top = $(window).height() * 0.1 - ethis.offsetParent().offset().top;
+ content.overlay(pbo.config, ethis);
+ api = content.overlay();
+ src = pbo.src;
+ selector = pbo.selector;
+ formtarget = pbo.formselector;
+ closeselector = pbo.closeselector;
+
+ pb.spinner.show();
+
+ // prevent double click warning for this form
+ $(this).find("input.submitting").removeClass('submitting');
+
+ el = $('<div class="pb-ajax" />');
+ if (api.getConf().fixed) {
+ // don't let it be over 75% of the viewport's height
+ el.css('max-height', Math.floor($(window).height() * 0.75));
+ }
+ content.append(el);
+
+ // affix a random query argument to prevent
+ // loading from browser cache
+ sep = (src.indexOf('?') >= 0) ? '&': '?';
+ src += sep + "ajax_load=" + (new Date().getTime());
+
+ // add selector, if any
+ if (selector) {
+ src += ' ' + selector;
+ }
+
+ // set up callback to be used whenever new contents are loaded
+ // into the overlay, to prepare links and forms to stay within
+ // the overlay
+ el[0].handle_load_inside_overlay = function(responseText, errorText) {
+ var el = $(this);
+
+ if (errorText === 'error') {
+ el.append(pb.ajax_error_recover(responseText, selector));
+ } else if (!responseText.length) {
+ el.append(ajax_noresponse_message || 'No response from server.');
+ }
+
+ // a non-semantic div here will make sure we can
+ // do enough formatting.
+ el.wrapInner('<div />');
+
+ // add the submit handler if we've a formtarget
+ if (formtarget) {
+ var target = el.find(formtarget);
+ if (target.length > 0) {
+ pb.prep_ajax_form(target);
+ }
+ }
+
+ // if a closeselector has been specified, tie it to the overlay's
+ // close method via closure
+ if (closeselector) {
+ el.find(closeselector).click(function (event) {
+ api.close();
+ return false;
+ });
+ }
+
+ // This may be a complex form.
+ if ($.fn.ploneTabInit) {
+ el.ploneTabInit();
+ }
+
+ // remove element on close so that it doesn't congest the DOM
+ api.onClose = function () {
+ content.remove();
+ };
+ $(document).trigger('loadInsideOverlay', [this, responseText, errorText, api]);
+ }
+
+ // and load the div
+ el.load(src, null, function (responseText, errorText) {
+ // post-process the overlay contents
+ el[0].handle_load_inside_overlay.apply(this, [responseText, errorText]);
+
+ // Now, it's all ready to display; hide the
+ // spinner and call JQT overlay load.
+ pb.spinner.hide();
+ api.load();
+
+ return true;
+ });
+
+ // don't do the default action
+ return false;
+ };
+
+
+ /******
+ pb.iframe
+ onBeforeLoad handler for iframe overlays.
+
+ Note that the spinner is handled a little differently
+ so that we can keep it displayed while the iframe's
+ content is loading.
+ ******/
+ pb.iframe = function () {
+ var content, pbo;
+
+ pb.spinner.show();
+
+ content = this.getOverlay();
+ pbo = this.getTrigger().data('pbo');
+
+ if (content.find('iframe').length === 0 && pbo.src) {
+ content.append(
+ '<iframe src="' + pbo.src + '" width="' +
+ content.width() + '" height="' + content.height() +
+ '" onload="pb.spinner.hide()"/>'
+ );
+ } else {
+ pb.spinner.hide();
+ }
+ return true;
+ };
+
+ // $('.newsImageContainer a')
+ // .prepOverlay({
+ // subtype: 'image',
+ // urlmatch: '/image_view_fullscreen$',
+ // urlreplace: '_preview'
+ // });
+
+});
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]