diff --git a/modules/moduleorder/controllers/admin_moduleorder.php b/modules/moduleorder/controllers/admin_moduleorder.php
new file mode 100644
index 00000000..b3d9586d
--- /dev/null
+++ b/modules/moduleorder/controllers/admin_moduleorder.php
@@ -0,0 +1,105 @@
+_get_view();
+ }
+
+ private function _get_view() {
+ $view = new Admin_View("admin.html");
+ $view->page_title = t("Manage Module Order");
+ $view->content = new View("admin_moduleorder.html");
+ $view->content->csrf = access::csrf_token();
+ $view->content->available = new View("admin_moduleorder_blocks.html");
+ $view->content->active = new View("admin_moduleorder_blocks.html");
+ $view->content->available->modules = $this->_get_modules();
+ return $view;
+ }
+
+ public function update() {
+ //Get the ordered list of modules
+ $modulerawlist = explode("&", trim($_POST['modulelist'], "&"));
+
+ //Make sure that gallery and user modules are first in the list
+ $currentindex = 2;
+ $indent_provider = module::get_var("gallery", "identity_provider")
+ foreach ($modulerawlist as $row) {
+ $currentry = explode("=", $row);
+ $currentry = explode(":", $currentry[1]);
+ if ($currentry[0] == "gallery") {
+ $modulelist[0] = $row;
+ } elseif ($currentry[0] == $indent_provider) {
+ $modulelist[1] = $row;
+ } else {
+ $modulelist[$currentindex] = $row;
+ $currentindex++;
+ }
+ }
+ ksort($modulelist);
+
+ //Get the highest used index
+ $highestindex = 0;
+ foreach ($modulelist as $row) {
+ $currentry = explode(":", $row);
+ if ($currentry[1] > $highestindex) {
+ $highestindex = $currentry[1];
+ }
+ }
+ $highestindex++; //Have a safety margin just in case
+ //To avoid conflicts on the index we now rewrite all indices of all modules
+ foreach ($modulelist as $row) {
+ $highestindex++;
+ $currentry = explode("=", $row);
+ $currentry = explode(":", $currentry[1]);
+ db::build()
+ ->update("modules")
+ ->set("id", $highestindex)
+ ->where("name", "=", $currentry[0])
+ ->execute();
+ }
+
+ //Now we are ready to write the correct id values
+ $highestindex = 0;
+ foreach ($modulelist as $row) {
+ $highestindex++;
+ $currentry = explode("=", $row);
+ $currentry = explode(":", $currentry[1]);
+ db::build()
+ ->update("modules")
+ ->set("id", $highestindex)
+ ->where("name", "=", $currentry[0])
+ ->execute();
+ }
+
+ //As last step we optimize the table
+ db::query("OPTIMIZE TABLE `modules`")
+ ->execute();
+ message::success(t("Your settings have been saved."));
+ url::redirect("admin/moduleorder");
+ print $this->_get_view();
+ }
+
+ private function _get_modules() {
+ $active_blocks = array();
+ $available_modules = module_manager::get_available_site_modules();
+ return $available_modules;
+ }
+}
+
diff --git a/modules/moduleorder/helpers/module_manager.php b/modules/moduleorder/helpers/module_manager.php
new file mode 100644
index 00000000..ec9b6e00
--- /dev/null
+++ b/modules/moduleorder/helpers/module_manager.php
@@ -0,0 +1,33 @@
+execute() as $row) {
+ $modules["{$row->name}:$row->id"] = $row->name;
+ }
+ return $modules;
+ }
+}
\ No newline at end of file
diff --git a/modules/moduleorder/helpers/moduleorder_event.php b/modules/moduleorder/helpers/moduleorder_event.php
new file mode 100644
index 00000000..5fdcc923
--- /dev/null
+++ b/modules/moduleorder/helpers/moduleorder_event.php
@@ -0,0 +1,28 @@
+get("settings_menu")
+ ->append(Menu::factory("link")
+ ->id("moduleorder_menu")
+ ->label(t("Manage Module Order"))
+ ->url(url::site("admin/moduleorder")));
+ }
+}
diff --git a/modules/moduleorder/module.info b/modules/moduleorder/module.info
new file mode 100644
index 00000000..108e813d
--- /dev/null
+++ b/modules/moduleorder/module.info
@@ -0,0 +1,3 @@
+name = "Module Order"
+description = "Allows you to change the order in which modules are executed"
+version = 1
diff --git a/modules/moduleorder/views/admin_moduleorder.html.php b/modules/moduleorder/views/admin_moduleorder.html.php
new file mode 100644
index 00000000..e649c800
--- /dev/null
+++ b/modules/moduleorder/views/admin_moduleorder.html.php
@@ -0,0 +1,56 @@
+
+
+
+
+
= t("Manage Module Order") ?>
+
+ = t("Select and drag blocks to change the order. Click 'Save' to save your changes.") ?>
+
+
= t("Important notes") ?>
+
+ = t("You will only need to change this order in rare circumstances (e.g. if two modules display content at the bottom of the image and you want to change the order in which this content is being shown). If everything on your Gallery Site is looking normal then please do not touch this.") ?>
+
+
+ = t("The core module ('gallery') and the identity provider module (default is 'user') cannot be sorted. When saving, these two modules will be automatically put at the top of the list.") ?>
+
+
+
+
+
+
= t("Installed Modules") ?>
+
+
+
+
+
+
Save
+
diff --git a/modules/moduleorder/views/admin_moduleorder_blocks.html.php b/modules/moduleorder/views/admin_moduleorder_blocks.html.php
new file mode 100644
index 00000000..2cb6ca74
--- /dev/null
+++ b/modules/moduleorder/views/admin_moduleorder_blocks.html.php
@@ -0,0 +1,9 @@
+
+ $indent_provider = module::get_var("gallery", "identity_provider") ?>
+ foreach ($modules as $ref => $text): ?>
+ if ($text == "gallery" || $text == $indent_provider): ?>
+= $text ?>
+ else: ?>
+= $text ?>
+ endif ?>
+ endforeach ?>
diff --git a/modules/navcarousel/controllers/admin_navcarousel.php b/modules/navcarousel/controllers/admin_navcarousel.php
new file mode 100644
index 00000000..77643fe7
--- /dev/null
+++ b/modules/navcarousel/controllers/admin_navcarousel.php
@@ -0,0 +1,130 @@
+_get_view();
+ }
+
+ public function handler() {
+ access::verify_csrf();
+
+ $form = $this->_get_form();
+ if ($form->validate()) {
+ $scrollsize = intval($form->navcarousel->scrollsize->value);
+ $showelements = intval($form->navcarousel->showelements->value);
+ $carouselwidth = intval($form->navcarousel->carouselwidth->value);
+ $thumbsize = intval($form->thumbsettings->thumbsize->value);
+ if ($showelements < 1) {
+ $showelements = 1;
+ message::error(t("You must show at least one item."));
+ }
+ if ($scrollsize < 1) {
+ $scrollsize = 1;
+ message::error(t("You must scroll by at least one item."));
+ }
+ if ($thumbsize > 150 || $thumbsize < 25) {
+ $thumbsize = 50;
+ message::error(t("The size of the thumbnails must be between 25 and 150 pixel."));
+ }
+ if ($carouselwidth < ($thumbsize + 75) && $carouselwidth > 0) {
+ $carouselwidth = $thumbsize + 75;
+ message::error(t("The carousel must be at least %pixel wide.", array("pixel" => $carouselwidth)));
+ }
+ if ($carouselwidth > 0) {
+ if ($carouselwidth < ((($thumbsize + 11) * $showelements) + 64)) {
+ $showelements = ($carouselwidth - 64) / ($thumbsize + 11);
+ $showelements = intval(floor($showelements));
+ message::error(t("With the selected carousel width and thumbnail size you can show a maximum of %itemno items.", array("itemno" => $showelements)));
+ }
+ } else {
+ message::warning(t("The maximum number of displayable items cannot be calculated when the carousel width is set to 0."));
+ }
+ if ($scrollsize > $showelements) {
+ $scrollsize = $showelements;
+ message::error(t("The number of items to scroll must not exceed the number of items to show."));
+ }
+ module::set_var(
+ "navcarousel", "scrollsize", $scrollsize);
+ module::set_var(
+ "navcarousel", "showelements", $showelements);
+ module::set_var(
+ "navcarousel", "carouselwidth", $carouselwidth);
+ module::set_var(
+ "navcarousel", "thumbsize", $thumbsize);
+ module::set_var(
+ "navcarousel", "abovephoto", $form->navcarousel->abovephoto->value, true);
+ module::set_var(
+ "navcarousel", "noajax", $form->navcarousel->noajax->value, true);
+ module::set_var(
+ "navcarousel", "maintainaspect", $form->thumbsettings->maintainaspect->value, true);
+ module::set_var(
+ "navcarousel", "nomouseover", $form->thumbsettings->nomouseover->value, true);
+ module::set_var(
+ "navcarousel", "noresize", $form->thumbsettings->noresize->value, true);
+
+ message::success(t("Your settings have been saved."));
+ url::redirect("admin/navcarousel");
+ }
+ print $this->_get_view($form);
+ }
+
+ private function _get_view($form=null) {
+ $v = new Admin_View("admin.html");
+ $v->content = new View("admin_navcarousel.html");
+ $v->content->form = empty($form) ? $this->_get_form() : $form;
+ return $v;
+ }
+
+ private function _get_form() {
+ $form = new Forge("admin/navcarousel/handler", "", "post", array("id" => "g-admin-form"));
+
+ $group = $form->group("navcarousel")->label(t("Navigation carousel settings"));
+ $group->input("scrollsize")->label(t('Enter how many items you want to scroll when clicking next or previous'))
+ ->value(module::get_var("navcarousel", "scrollsize", "7"))
+ ->rules("valid_numeric|length[1,2]");
+ $group->input("showelements")->label(t('Enter how many items you want to be visible'))
+ ->value(module::get_var("navcarousel", "showelements", "7"))
+ ->rules("valid_numeric|length[1,2]");
+ $group->input("carouselwidth")->label(t('Carousel width (in pixel). If set to 0 the carousel will use the full available width.'))
+ ->value(module::get_var("navcarousel", "carouselwidth", "600"))
+ ->rules("valid_numeric|length[1,3]");
+ $group->checkbox("abovephoto")->label(t("Show carousel above photo"))
+ ->checked(module::get_var("navcarousel", "abovephoto", false));
+ $group->checkbox("noajax")->label(t("Disable dynamic loading of thumbnails (might be slow for big albums)"))
+ ->checked(module::get_var("navcarousel", "noajax", false));
+
+ $group = $form->group("thumbsettings")->label(t("Change how thumnails are displayed"));
+ $group->input("thumbsize")->label(t('Thumbnail size (in pixel)'))
+ ->value(module::get_var("navcarousel", "thumbsize", "50"))
+ ->rules("valid_numeric|length[1,3]");
+ $group->checkbox("nomouseover")->label(t("Do not show item title and number on mouse over"))
+ ->checked(module::get_var("navcarousel", "nomouseover", false));
+ $group->checkbox("noresize")->label(t("Crop thumbails instead of resizing them."))
+ ->onClick("changeaspectstate()")
+ ->id("noresize")
+ ->checked(module::get_var("navcarousel", "noresize", false));
+ $group->checkbox("maintainaspect")->label(t("Maintain aspect ratio of the items for the thumbnails."))
+ ->id("maintainaspect")
+ ->checked(module::get_var("navcarousel", "maintainaspect", false));
+
+ $form->submit("submit")->value(t("Save"));
+ return $form;
+ }
+}
diff --git a/modules/navcarousel/controllers/navcarousel.php b/modules/navcarousel/controllers/navcarousel.php
new file mode 100644
index 00000000..7b8125a7
--- /dev/null
+++ b/modules/navcarousel/controllers/navcarousel.php
@@ -0,0 +1,62 @@
+parent();
+ $item_count = -1;
+
+ // Array indexes are 0-based, jCarousel positions are 1-based.
+ $first = max(0, intval($_GET['first']) - 1);
+ $last = max($first + 1, intval($_GET['last']) - 1);
+
+ $length = $last - $first + 1;
+
+ // Build the array with the thumbnail URLs
+ foreach ($parent->viewable()->children() as $photo) {
+ if (!$photo->is_album()) {
+ $item_count++;
+ $itemlist[$item_count] = $photo->thumb_url();
+ }
+ }
+
+ $total = count($itemlist);
+ $selected = array_slice($itemlist, $first, $length);
+
+ // ---
+
+ header('Content-Type: text/xml');
+
+ echo '';
+
+ // Return total number of images so the callback
+ // can set the size of the carousel.
+ echo ' ' . $total . '';
+
+ foreach ($selected as $img) {
+ echo ' ' . $img . '';
+ }
+
+ echo '';
+
+ }
+}
diff --git a/modules/navcarousel/css/credits.txt b/modules/navcarousel/css/credits.txt
new file mode 100644
index 00000000..e5ec8c29
--- /dev/null
+++ b/modules/navcarousel/css/credits.txt
@@ -0,0 +1 @@
+Button images copyright by Tango Icon Library Team (http://tango.freedesktop.org/Tango_Icon_Library)
\ No newline at end of file
diff --git a/modules/navcarousel/css/skin.css b/modules/navcarousel/css/skin.css
new file mode 100644
index 00000000..47a16180
--- /dev/null
+++ b/modules/navcarousel/css/skin.css
@@ -0,0 +1,124 @@
+.jcarousel-skin-tango .jcarousel-container {
+ -moz-border-radius: 10px;
+ background: transparent;
+ border: 0;
+}
+
+.jcarousel-skin-tango .jcarousel-container-horizontal {
+ padding: 0 60px;
+ margin: 0 auto;
+}
+
+.jcarousel-skin-tango .jcarousel-item {
+ background-color: transparent !important;
+}
+
+.jcarousel-skin-tango .jcarousel-item-horizontal {
+ margin-right: 2px;
+ margin-right: 2px;
+ padding-top: 2px;
+ text-align: center;
+}
+
+.jcarousel-skin-tango .jcarousel-item-placeholder {
+ background: #fff;
+ color: #000;
+}
+
+/**
+ * Horizontal Buttons
+ */
+.jcarousel-skin-tango .jcarousel-next-horizontal {
+ position: absolute;
+ right: 18px;
+ width: 32px;
+ height: 32px;
+ cursor: pointer;
+ background: transparent url('../images/next-horizontal.png') no-repeat 0 0;
+ visibility: hidden;
+}
+
+.jcarousel-skin-tango .jcarousel-next-horizontal:hover {
+ background-position: -32px 0;
+}
+
+.jcarousel-skin-tango .jcarousel-next-horizontal:active {
+ background-position: -64px 0;
+}
+
+.jcarousel-skin-tango .jcarousel-next-disabled-horizontal,
+.jcarousel-skin-tango .jcarousel-next-disabled-horizontal:hover,
+.jcarousel-skin-tango .jcarousel-next-disabled-horizontal:active {
+ cursor: default;
+ background-position: -96px 0;
+}
+
+.jcarousel-skin-tango .jcarousel-prev-horizontal {
+ position: absolute;
+ left: 18px;
+ width: 32px;
+ height: 32px;
+ cursor: pointer;
+ background: transparent url('../images/prev-horizontal.png') no-repeat 0 0;
+ visibility: hidden;
+}
+
+.jcarousel-skin-tango .jcarousel-prev-horizontal:hover {
+ background-position: -32px 0;
+}
+
+.jcarousel-skin-tango .jcarousel-prev-horizontal:active {
+ background-position: -64px 0;
+}
+
+.jcarousel-skin-tango .jcarousel-prev-disabled-horizontal,
+.jcarousel-skin-tango .jcarousel-prev-disabled-horizontal:hover,
+.jcarousel-skin-tango .jcarousel-prev-disabled-horizontal:active {
+ cursor: default;
+ background-position: -96px 0;
+}
+
+.carousel-thumbnail {
+ padding: 5px;
+ margin: 0 !important;
+}
+
+.carousel-thumbnail:hover {
+ box-shadow: 1px 0px 8px #d7e1fa;
+ -moz-box-shadow: 1px 0px 8px #d7e1fa;
+ -webkit-box-shadow: 1px 0px 8px #d7e1fa;
+ behavior: url(ie-css3.htc);
+}
+
+.carousel-current {
+ box-shadow: 1px 0px 8px #d7e1fa;
+ -moz-box-shadow: 1px 0px 8px #d7e1fa;
+ -webkit-box-shadow: 1px 0px 8px #d7e1fa;
+ behavior: url(ie-css3.htc);
+ padding: 5px;
+ margin: 0 !important;
+}
+
+#navcarousel-wrapper {
+ width: 100%;
+ background: url('../images/ajax-loader.gif') no-repeat center center;
+}
+
+#navcarousel {
+ visibility: hidden;
+}
+
+/**
+ * RTL Support
+ */
+
+.jcarousel-skin-tango .jcarousel-direction-rtl {direction:rtl;}
+.jcarousel-skin-tango .jcarousel-direction-rtl .jcarousel-item-horizontal{ margin-right:0; margin-left:10px;}
+
+/*horizontal buttons*/
+.jcarousel-skin-tango .jcarousel-direction-rtl .jcarousel-next-horizontal{
+ background-image:url('../images/prev-horizontal.png'); right:auto; left:5px;
+}
+.jcarousel-skin-tango .jcarousel-direction-rtl .jcarousel-prev-horizontal{
+ background-image:url('../images/next-horizontal.png'); left:auto; right:5px;
+}
diff --git a/modules/navcarousel/helpers/navcarousel_event.php b/modules/navcarousel/helpers/navcarousel_event.php
new file mode 100644
index 00000000..a636ea23
--- /dev/null
+++ b/modules/navcarousel/helpers/navcarousel_event.php
@@ -0,0 +1,28 @@
+get("settings_menu")
+ ->append(Menu::factory("link")
+ ->id("navcarousel_menu")
+ ->label(t("Navigation carousel"))
+ ->url(url::site("admin/navcarousel")));
+ }
+}
diff --git a/modules/navcarousel/helpers/navcarousel_theme.php b/modules/navcarousel/helpers/navcarousel_theme.php
new file mode 100644
index 00000000..5c757548
--- /dev/null
+++ b/modules/navcarousel/helpers/navcarousel_theme.php
@@ -0,0 +1,121 @@
+page_type == "item") {
+ if (locales::is_rtl()) {
+ $rtl_support = "horizontalDirection: 'rtl',\n";
+ } else {
+ $rtl_support = "";
+ }
+ $carouselwidth = module::get_var("navcarousel", "carouselwidth", "600");
+ if ($carouselwidth == 0) {
+ $carouselwidth = "100%";
+ $containerwidth = "";
+ } else {
+ $carouselwidth = $carouselwidth ."px";
+ $containerwidth = ".jcarousel-skin-tango .jcarousel-container-horizontal {\n
+ width: ". $carouselwidth .";\n
+ }\n";
+ }
+ $thumbsize = module::get_var("navcarousel", "thumbsize", "50");
+ $theme->script("jquery.jcarousel.min.js");
+ $theme->css("skin.css");
+ $showelements = module::get_var("navcarousel", "showelements", "7");
+ $childcount = $theme->item->parent()->viewable()->children_count();
+ $itemoffset = intval(floor($showelements / 2));
+ if ($childcount <= $showelements) {
+ $itemoffset = 1;
+ } else {
+ $itempos = $theme->item->parent()->get_position($theme->item);
+ $itemoffset = $itempos - $itemoffset;
+ if ($itemoffset < 1) {
+ $itemoffset = 1;
+ }
+ if (($itemoffset + $showelements) > $childcount) {
+ $itemoffset = $childcount - $showelements + 1;
+ }
+ }
+ if (module::get_var("navcarousel", "noajax", false)) {
+ $ajaxhandler = "";
+ } else {
+ $ajaxhandler = "itemLoadCallback: navcarousel_itemLoadCallback,\n";
+ }
+
+ Return "\n
+ \n
+ \n
+ ";
+ }
+ }
+
+ static function photo_bottom($theme) {
+ if (!module::get_var("navcarousel", "abovephoto", false)) {
+ if ($theme->page_type == "item") {
+ return new View("navcarousel.html");
+ }
+ }
+ }
+
+ static function photo_top($theme) {
+ if (module::get_var("navcarousel", "abovephoto", false)) {
+ if ($theme->page_type == "item") {
+ return new View("navcarousel.html");
+ }
+ }
+ }
+}
diff --git a/modules/navcarousel/images/ajax-loader.gif b/modules/navcarousel/images/ajax-loader.gif
new file mode 100644
index 00000000..3288d103
Binary files /dev/null and b/modules/navcarousel/images/ajax-loader.gif differ
diff --git a/modules/navcarousel/images/next-horizontal.png b/modules/navcarousel/images/next-horizontal.png
new file mode 100644
index 00000000..c9943fda
Binary files /dev/null and b/modules/navcarousel/images/next-horizontal.png differ
diff --git a/modules/navcarousel/images/prev-horizontal.png b/modules/navcarousel/images/prev-horizontal.png
new file mode 100644
index 00000000..e963d28e
Binary files /dev/null and b/modules/navcarousel/images/prev-horizontal.png differ
diff --git a/modules/navcarousel/js/jquery.jcarousel.min.js b/modules/navcarousel/js/jquery.jcarousel.min.js
new file mode 100644
index 00000000..f7c76886
--- /dev/null
+++ b/modules/navcarousel/js/jquery.jcarousel.min.js
@@ -0,0 +1,18 @@
+/*!
+ * jCarousel - Riding carousels with jQuery
+ * http://sorgalla.com/jcarousel/
+ *
+ * Copyright (c) 2006 Jan Sorgalla (http://sorgalla.com)
+ * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
+ * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
+ *
+ * Built on top of the jQuery library
+ * http://jquery.com
+ *
+ * Inspired by the "Carousel Component" by Bill Scott
+ * http://billwscott.com/carousel/
+ *
+ * RTLed by nightS @ RTL-This
+ * http://rtl-this.com/tutorial/rtling-jcarousel-jquery-plugin
+ */
+(function($){$.fn.jcarousel=function(o){if(typeof o=='string'){var instance=$(this).data('jcarousel'),args=Array.prototype.slice.call(arguments,1);return instance[o].apply(instance,args)}else return this.each(function(){$(this).data('jcarousel',new $jc(this,o))})};var defaults={vertical:false,horizontalDirection:'ltr',start:1,offset:1,size:null,scroll:3,visible:null,animation:'normal',easing:'swing',auto:0,wrap:null,initCallback:null,reloadCallback:null,itemLoadCallback:null,itemFirstInCallback:null,itemFirstOutCallback:null,itemLastInCallback:null,itemLastOutCallback:null,itemVisibleInCallback:null,itemVisibleOutCallback:null,buttonNextHTML:'',buttonPrevHTML:'',buttonNextEvent:'click',buttonPrevEvent:'click',buttonNextCallback:null,buttonPrevCallback:null};$.jcarousel=function(e,o){this.options=$.extend({},defaults,o||{});this.locked=false;this.container=null;this.clip=null;this.list=null;this.buttonNext=null;this.buttonPrev=null;this.wh=!this.options.vertical?'width':'height';this.lt=!this.options.vertical?(this.options.horizontalDirection=='rtl'?'right':'left'):'top';var skin='',split=e.className.split(' ');for(var i=0;i');this.container=this.container.parent()}else if(!this.container.hasClass('jcarousel-container'))this.container=this.list.wrap('').parent()}else{this.container=$(e);this.list=this.container.find('ul,ol').eq(0)}if(skin!=''&&this.container.parent()[0].className.indexOf('jcarousel-skin')==-1)this.container.wrap('');this.clip=this.list.parent();if(!this.clip.length||!this.clip.hasClass('jcarousel-clip'))this.clip=this.list.wrap('').parent();this.buttonNext=$('.jcarousel-next',this.container);if(this.buttonNext.size()==0&&this.options.buttonNextHTML!=null)this.buttonNext=this.clip.after(this.options.buttonNextHTML).next();this.buttonNext.addClass(this.className('jcarousel-next'));this.buttonPrev=$('.jcarousel-prev',this.container);if(this.buttonPrev.size()==0&&this.options.buttonPrevHTML!=null)this.buttonPrev=this.clip.after(this.options.buttonPrevHTML).next();this.buttonPrev.addClass(this.className('jcarousel-prev'));this.clip.addClass(this.className('jcarousel-clip')).css({overflow:'hidden',position:'relative'});this.list.addClass(this.className('jcarousel-list')).css({overflow:'hidden',position:'relative',top:0,left:0,margin:0,padding:0});this.container.addClass(this.className('jcarousel-container')).addClass(!this.options.vertical?'jcarousel-direction-'+this.options.horizontalDirection:'').css({position:'relative'});var di=this.options.visible!=null?Math.ceil(this.clipping()/this.options.visible):null;var li=this.list.children('li');var self=this;if(li.size()>0){var wh=0,i=this.options.offset;li.each(function(){self.format(this,i++);wh+=self.dimension(this,di)});this.list.css(this.wh,wh+'px');if(!o||o.size===undefined)this.options.size=li.size()}this.container.css('display','block');this.buttonNext.css('display','block');this.buttonPrev.css('display','block');this.funcNext=function(){self.next()};this.funcPrev=function(){self.prev()};this.funcResize=function(){self.reload()};if(this.options.initCallback!=null)this.options.initCallback(this,'init');if($.browser.safari){this.buttons(false,false);$(window).bind('load.jcarousel',function(){self.setup()})}else this.setup()};var $jc=$.jcarousel;$jc.fn=$jc.prototype={jcarousel:'0.2.4'};$jc.fn.extend=$jc.extend=$.extend;$jc.fn.extend({setup:function(){this.first=null;this.last=null;this.prevFirst=null;this.prevLast=null;this.animating=false;this.timer=null;this.tail=null;this.inTail=false;if(this.locked)return;this.list.css(this.lt,this.pos(this.options.offset)+'px');var p=this.pos(this.options.start);this.prevFirst=this.prevLast=null;this.animate(p,false);$(window).unbind('resize.jcarousel',this.funcResize).bind('resize.jcarousel',this.funcResize)},reset:function(){this.list.empty();this.list.css(this.lt,'0px');this.list.css(this.wh,'10px');if(this.options.initCallback!=null)this.options.initCallback(this,'reset');this.setup()},reload:function(){if(this.tail!=null&&this.inTail)this.list.css(this.lt,$jc.intval(this.list.css(this.lt))+this.tail);this.tail=null;this.inTail=false;if(this.options.reloadCallback!=null)this.options.reloadCallback(this);if(this.options.visible!=null){var self=this;var di=Math.ceil(this.clipping()/this.options.visible),wh=0,lt=0;$('li',this.list).each(function(i){wh+=self.dimension(this,di);if(i+1this.options.size)i2=this.options.size;for(var j=i;j<=i2;j++){var e=this.get(j);if(!e.length||e.hasClass('jcarousel-item-placeholder'))return false}return true},get:function(i){return $('.jcarousel-item-'+i,this.list)},add:function(i,s){var e=this.get(i),old=0,add=0;if(e.length==0){var c,e=this.create(i),j=$jc.intval(i);while(c=this.get(--j)){if(j<=0||c.length){j<=0?this.list.prepend(e):c.after(e);break}}}else old=this.dimension(e);e.removeClass(this.className('jcarousel-item-placeholder'));typeof s=='string'?e.html(s):e.empty().append(s);var di=this.options.visible!=null?Math.ceil(this.clipping()/this.options.visible):null;var wh=this.dimension(e,di)-old;if(i>0&&i=this.first&&i<=this.last))return;var d=this.dimension(e);if(ithis.options.size?this.options.size:i);var back=this.first>i;var f=this.options.wrap!='circular'&&this.first<=1?1:this.first;var c=back?this.get(f):this.get(this.last);var j=back?f:f-1;var e=null,l=0,p=false,d=0,g;while(back?--j>=i:++jthis.options.size)){g=this.get(this.index(j));if(g.length)this.add(j,g.children().clone(true))}}c=e;d=this.dimension(e);if(p)l+=d;if(this.first!=null&&(this.options.wrap=='circular'||(j>=1&&(this.options.size==null||j<=this.options.size))))pos=back?pos+d:pos-d}var clipping=this.clipping();var cache=[];var visible=0,j=i,v=0;var c=this.get(i-1);while(++visible){e=this.get(j);p=!e.length;if(e.length==0){e=this.create(j).addClass(this.className('jcarousel-item-placeholder'));c.length==0?this.list.prepend(e):c[back?'before':'after'](e);if(this.first!=null&&this.options.wrap=='circular'&&this.options.size!==null&&(j<=0||j>this.options.size)){g=this.get(this.index(j));if(g.length)this.add(j,g.find('>*').clone(true))}}c=e;var d=this.dimension(e);if(d==0){alert('jCarousel: No width/height set for items. This will cause an infinite loop. Aborting...');return 0}if(this.options.wrap!='circular'&&this.options.size!==null&&j>this.options.size)cache.push(e);else if(p)l+=d;v+=d;if(v>=clipping)break;j++}for(var x=0;x0){this.list.css(this.wh,this.dimension(this.list)+l+'px');if(back){pos-=l;this.list.css(this.lt,$jc.intval(this.list.css(this.lt))-l+'px')}}var last=i+visible-1;if(this.options.wrap!='circular'&&this.options.size&&last>this.options.size)last=this.options.size;if(j>last){visible=0,j=last,v=0;while(++visible){var e=this.get(j--);if(!e.length)break;v+=this.dimension(e);if(v>=clipping)break}}var first=last-visible+1;if(this.options.wrap!='circular'&&first<1)first=1;if(this.inTail&&back){pos+=this.tail;this.inTail=false}this.tail=null;if(this.options.wrap!='circular'&&last==this.options.size&&(last-visible+1)>=1){var m=$jc.margin(this.get(last),!this.options.vertical?'marginRight':'marginBottom');if((v-m)>clipping)this.tail=v-clipping-m}while(i-->first)pos+=this.dimension(this.get(i));this.prevFirst=this.first;this.prevLast=this.last;this.first=first;this.last=last;return pos},animate:function(p,a){if(this.locked||this.animating)return;this.animating=true;var self=this;var scrolled=function(){self.animating=false;if(p==0)self.list.css(self.lt,0);if(self.options.wrap=='circular'||self.options.wrap=='both'||self.options.wrap=='last'||self.options.size==null||self.last=this.options.size)n=this.tail!=null&&this.inTail}if(p==undefined||p==null){var p=!this.locked&&this.options.size!==0&&((this.options.wrap&&this.options.wrap!='last')||this.first>1);if(!this.locked&&(!this.options.wrap||this.options.wrap=='last')&&this.options.size!=null&&this.first==1)p=this.tail!=null&&this.inTail}var self=this;this.buttonNext[n?'bind':'unbind'](this.options.buttonNextEvent+'.jcarousel',this.funcNext)[n?'removeClass':'addClass'](this.className('jcarousel-next-disabled')).attr('disabled',n?false:true);this.buttonPrev[p?'bind':'unbind'](this.options.buttonPrevEvent+'.jcarousel',this.funcPrev)[p?'removeClass':'addClass'](this.className('jcarousel-prev-disabled')).attr('disabled',p?false:true);if(this.buttonNext.length>0&&(this.buttonNext[0].jcarouselstate==undefined||this.buttonNext[0].jcarouselstate!=n)&&this.options.buttonNextCallback!=null){this.buttonNext.each(function(){self.options.buttonNextCallback(self,this,n)});this.buttonNext[0].jcarouselstate=n}if(this.buttonPrev.length>0&&(this.buttonPrev[0].jcarouselstate==undefined||this.buttonPrev[0].jcarouselstate!=p)&&this.options.buttonPrevCallback!=null){this.buttonPrev.each(function(){self.options.buttonPrevCallback(self,this,p)});this.buttonPrev[0].jcarouselstate=p}},notify:function(evt){var state=this.prevFirst==null?'init':(this.prevFirst=i3&&i<=i4))this.get(i).each(function(){callback(self,this,i,state,evt)})}},create:function(i){return this.format('',i)},format:function(e,i){var $e=$(e).addClass(this.className('jcarousel-item')).addClass(this.className('jcarousel-item-'+i)).css({'float':(this.options.horizontalDirection=='rtl'?'right':'left'),'list-style':'none'});$e.attr('jcarouselindex',i);return $e},className:function(c){return c+' '+c+(!this.options.vertical?'-horizontal':'-vertical')},dimension:function(e,d){var el=e.jquery!=undefined?e[0]:e;var old=!this.options.vertical?el.offsetWidth+$jc.margin(el,'marginLeft')+$jc.margin(el,'marginRight'):el.offsetHeight+$jc.margin(el,'marginTop')+$jc.margin(el,'marginBottom');if(d==undefined||old==d)return old;var w=!this.options.vertical?d-$jc.margin(el,'marginLeft')-$jc.margin(el,'marginRight'):d-$jc.margin(el,'marginTop')-$jc.margin(el,'marginBottom');$(el).css(this.wh,w+'px');return this.dimension(el)},clipping:function(){return!this.options.vertical?this.clip[0].offsetWidth-$jc.intval(this.clip.css('borderLeftWidth'))-$jc.intval(this.clip.css('borderRightWidth')):this.clip[0].offsetHeight-$jc.intval(this.clip.css('borderTopWidth'))-$jc.intval(this.clip.css('borderBottomWidth'))},index:function(i,s){if(s==undefined)s=this.options.size;return Math.round((((i-1)/s)-Math.floor((i-1)/s))*s)+1}});$jc.extend({defaults:function(d){return $.extend(defaults,d||{})},margin:function(e,p){if(!e)return 0;var el=e.jquery!=undefined?e[0]:e;if(p=='marginRight'&&$.browser.safari){var old={'display':'block','float':'none','width':'auto'},oWidth,oWidth2;$.swap(el,old,function(){oWidth=el.offsetWidth});old['marginRight']=0;$.swap(el,old,function(){oWidth2=el.offsetWidth});return oWidth2-oWidth}return $jc.intval($.css(el,p))},intval:function(v){v=parseInt(v);return isNaN(v)?0:v}})})(jQuery);
diff --git a/modules/navcarousel/module.info b/modules/navcarousel/module.info
new file mode 100644
index 00000000..75800871
--- /dev/null
+++ b/modules/navcarousel/module.info
@@ -0,0 +1,3 @@
+name = "Navigation Carousel"
+description = "Adds a navigation carousel under the photo."
+version = 3
diff --git a/modules/navcarousel/views/admin_navcarousel.html.php b/modules/navcarousel/views/admin_navcarousel.html.php
new file mode 100644
index 00000000..5a116104
--- /dev/null
+++ b/modules/navcarousel/views/admin_navcarousel.html.php
@@ -0,0 +1,17 @@
+
+
+
+
= t("Navigation carousel administration") ?>
+
= t("Notes:") ?>
+
= t("There is a known bug with the positioning and scrolling of the album.
+ If you are experiencing this bug then please enable the option 'Disable dynamic loading of thumbnails'.
+ I am working on fixing this bug and will release an update as soon as possible.") ?>
+ = $form ?>
+
diff --git a/modules/navcarousel/views/navcarousel.html.php b/modules/navcarousel/views/navcarousel.html.php
new file mode 100644
index 00000000..40af0d8e
--- /dev/null
+++ b/modules/navcarousel/views/navcarousel.html.php
@@ -0,0 +1,118 @@
+parent();
+ $item_counter = 0;
+ $item_offset = 0;
+ $maintain_aspect = module::get_var("navcarousel", "maintainaspect", false);
+ $no_resize = module::get_var("navcarousel", "noresize", false);
+ $no_ajax = module::get_var("navcarousel", "noajax", false);
+?>
+
+
+