import re
regex = re.compile(r"removed?\W*remove", flags=re.IGNORECASE)
test_str = (" I'd like to stop Galleriffic slider at the last thumbs image, but I don't know how.\n"
"Here is the code:\n\n"
"// Primary Galleriffic initialization function that should be called on the thumbnail container.\n"
"`$.fn.galleriffic = function(settings) {\n"
" // Extend Gallery Object\n"
" $.extend(this, {\n"
" // Returns the version of the script\n"
" version: $.galleriffic.version,\n\n"
" // Current state of the slideshow\n"
" isSlideshowRunning: false,\n"
" slideshowTimeout: undefined,\n\n"
" // This function is attached to the click event of generated hyperlinks within the gallery\n"
" clickHandler: function(e, link) {\n"
" this.play();\n\n"
" if (!this.enableHistory) {\n"
" // The href attribute holds the unique hash for an image\n"
" var hash = $.galleriffic.normalizeHash($(link).attr('href'));\n"
" $.galleriffic.gotoImage(hash);\n"
" e.preventDefault();\n"
" }\n"
" },\n\n"
" // Appends an image to the end of the set of images. Argument listItem can be either a jQuery DOM element or arbitrary html.\n"
" // @param listItem Either a jQuery object or a string of html of the list item that is to be added to the gallery.\n"
" appendImage: function(listItem) {\n"
" this.addImage(listItem, false, false);\n"
" return this;\n"
" },\n\n"
" // Inserts an image into the set of images. Argument listItem can be either a jQuery DOM element or arbitrary html.\n"
" // @param listItem Either a jQuery object or a string of html of the list item that is to be added to the gallery.\n"
" // @param {Integer} position The index within the gallery where the item shouold be added.\n"
" insertImage: function(listItem, position) {\n"
" this.addImage(listItem, false, true, position);\n"
" return this;\n"
" },\n\n"
" // Adds an image to the gallery and optionally inserts/appends it to the DOM (thumbExists)\n"
" // @param listItem Either a jQuery object or a string of html of the list item that is to be added to the gallery.\n"
" // @param {Boolean} thumbExists Specifies whether the thumbnail already exists in the DOM or if it needs to be added.\n"
" // @param {Boolean} insert Specifies whether the the image is appended to the end or inserted into the gallery.\n"
" // @param {Integer} position The index within the gallery where the item shouold be added.\n"
" addImage: function(listItem, thumbExists, insert, position) {\n"
" var $li = ( typeof listItem === \"string\" ) ? $(listItem) : listItem; \n"
" var $aThumb = $li.find('a.thumb');\n"
" var slideUrl = $aThumb.attr('href');\n"
" var title = $aThumb.attr('title');\n"
" var $caption = $li.find('.caption').remove();\n"
" var hash = $aThumb.attr('name');\n\n"
" // Increment the image counter\n"
" imageCounter++;\n\n"
" // Autogenerate a hash value if none is present or if it is a duplicate\n"
" if (!hash || allImages[''+hash]) {\n"
" hash = imageCounter;\n"
" }\n\n"
" // Set position to end when not specified\n"
" if (!insert)\n"
" position = this.data.length;\n"
" \n"
" var imageData = {\n"
" title:title,\n"
" slideUrl:slideUrl,\n"
" caption:$caption,\n"
" hash:hash,\n"
" gallery:this,\n"
" index:position\n"
" };\n\n"
" // Add the imageData to this gallery's array of images\n"
" if (insert) {\n"
" this.data.splice(position, 0, imageData);\n\n"
" // Reset index value on all imageData objects\n"
" this.updateIndices(position);\n"
" }\n"
" else {\n"
" this.data.push(imageData);\n"
" }\n\n"
" var gallery = this;\n\n"
" // Add the element to the DOM\n"
" if (!thumbExists) {\n"
" // Update thumbs passing in addition post transition out handler\n"
" this.updateThumbs(function() {\n"
" var $thumbsUl = gallery.find('ul.thumbs');\n"
" if (insert)\n"
" $thumbsUl.children(':eq('+position+')').before($li);\n"
" else\n"
" $thumbsUl.append($li);\n"
" \n"
" if (gallery.onImageAdded)\n"
" gallery.onImageAdded(imageData, $li);\n"
" });\n"
" }\n\n"
" // Register the image globally\n"
" allImages[''+hash] = imageData;\n\n"
" // Setup attributes and click handler\n"
" $aThumb.attr('rel', 'history')\n"
" .attr('href', '#'+hash)\n"
" .removeAttr('name')\n"
" .click(function(e) {\n"
" gallery.clickHandler(e, this);\n"
" });\n\n"
" return this;\n"
" },\n\n"
" // Removes an image from the gallery based on its index.\n"
" // Returns false when the index is out of range.\n"
" removeImageByIndex: function(index) {\n"
" if (index < 0 || index >= this.data.length)\n"
" return false;\n"
" \n"
" var imageData = this.data[index];\n"
" if (!imageData)\n"
" return false;\n"
" \n"
" this.removeImage(imageData);\n"
" \n"
" return true;\n"
" },\n\n"
" // Convenience method that simply calls the global removeImageByHash method.\n"
" removeImageByHash: function(hash) {\n"
" return $.galleriffic.removeImageByHash(hash, this);\n"
" },\n\n"
" // Removes an image from the gallery.\n"
" removeImage: function(imageData) {\n"
" var index = imageData.index;\n"
" \n"
" // Remove the image from the gallery data array\n"
" this.data.splice(index, 1);\n"
" \n"
" // Remove the global registration\n"
" delete allImages[''+imageData.hash];\n"
" \n"
" // Remove the image's list item from the DOM\n"
" this.updateThumbs(function() {\n"
" var $li = gallery.find('ul.thumbs')\n"
" .children(':eq('+index+')')\n"
" .remove();\n\n"
" if (gallery.onImageRemoved)\n"
" gallery.onImageRemoved(imageData, $li);\n"
" });\n\n"
" // Update each image objects index value\n"
" this.updateIndices(index);\n\n"
" return this;\n"
" },\n\n"
" // Updates the index values of the each of the images in the gallery after the specified index\n"
" updateIndices: function(startIndex) {\n"
" for (i = startIndex; i < this.data.length; i++) {\n"
" this.data[i].index = i;\n"
" }\n"
" \n"
" return this;\n"
" },\n\n"
" // Scraped the thumbnail container for thumbs and adds each to the gallery\n"
" initializeThumbs: function() {\n"
" this.data = [];\n"
" var gallery = this;\n\n"
" this.find('ul.thumbs > li').each(function(i) {\n"
" gallery.addImage($(this), true, false);\n"
" });\n\n"
" return this;\n"
" },\n\n"
" isPreloadComplete: false,\n\n"
" // Initalizes the image preloader\n"
" preloadInit: function() {\n"
" if (this.preloadAhead == 0) return this;\n"
" \n"
" this.preloadStartIndex = this.currentImage.index;\n"
" var nextIndex = this.getNextIndex(this.preloadStartIndex);\n"
" return this.preloadRecursive(this.preloadStartIndex, nextIndex);\n"
" },\n\n"
" // Changes the location in the gallery the preloader should work\n"
" // @param {Integer} index The index of the image where the preloader should restart at.\n"
" preloadRelocate: function(index) {\n"
" // By changing this startIndex, the current preload script will restart\n"
" this.preloadStartIndex = index;\n"
" return this;\n"
" },\n\n"
" // Recursive function that performs the image preloading\n"
" // @param {Integer} startIndex The index of the first image the current preloader started on.\n"
" // @param {Integer} currentIndex The index of the current image to preload.\n"
" preloadRecursive: function(startIndex, currentIndex) {\n"
" // Check if startIndex has been relocated\n"
" if (startIndex != this.preloadStartIndex) {\n"
" var nextIndex = this.getNextIndex(this.preloadStartIndex);\n"
" return this.preloadRecursive(this.preloadStartIndex, nextIndex);\n"
" }\n\n"
" var gallery = this;\n\n"
" // Now check for preloadAhead count\n"
" var preloadCount = currentIndex - startIndex;\n"
" if (preloadCount < 0)\n"
" preloadCount = this.data.length-1-startIndex+currentIndex;\n"
" if (this.preloadAhead >= 0 && preloadCount > this.preloadAhead) {\n"
" // Do this in order to keep checking for relocated start index\n"
" setTimeout(function() { gallery.preloadRecursive(startIndex, currentIndex); }, 500);\n"
" return this;\n"
" }\n\n"
" var imageData = this.data[currentIndex];\n"
" if (!imageData)\n"
" return this;\n\n"
" // If already loaded, continue\n"
" if (imageData.image)\n"
" return this.preloadNext(startIndex, currentIndex); \n"
" \n"
" // Preload the image\n"
" var image = new Image();\n"
" \n"
" image.onload = function() {\n"
" imageData.image = this;\n"
" gallery.preloadNext(startIndex, currentIndex);\n"
" };\n\n"
" image.alt = imageData.title;\n"
" image.src = imageData.slideUrl;\n\n"
" return this;\n"
" },\n"
" \n"
" // Called by preloadRecursive in order to preload the next image after the previous has loaded.\n"
" // @param {Integer} startIndex The index of the first image the current preloader started on.\n"
" // @param {Integer} currentIndex The index of the current image to preload.\n"
" preloadNext: function(startIndex, currentIndex) {\n"
" var nextIndex = this.getNextIndex(currentIndex);\n"
" if (nextIndex == startIndex) {\n"
" this.isPreloadComplete = true;\n"
" } else {\n"
" // Use setTimeout to free up thread\n"
" var gallery = this;\n"
" setTimeout(function() { gallery.preloadRecursive(startIndex, nextIndex); }, 100);\n"
" }\n\n"
" return this;\n"
" },\n\n"
" // Safe way to get the next image index relative to the current image.\n"
" // If the current image is the last, returns 0\n"
" getNextIndex: function(index) {\n"
" var nextIndex = index+1;\n"
" if (nextIndex >= this.data.length)\n"
" nextIndex = 0;\n"
" return nextIndex;\n"
" },\n\n"
" // Safe way to get the previous image index relative to the current image.\n"
" // If the current image is the first, return the index of the last image in the gallery.\n"
" getPrevIndex: function(index) {\n"
" var prevIndex = index-1;\n"
" if (prevIndex < 0)\n"
" prevIndex = this.data.length-1;\n"
" return prevIndex;\n"
" },\n\n"
" // Pauses the slideshow\n"
" pause: function() {\n"
" this.isSlideshowRunning = false;\n"
" if (this.slideshowTimeout) {\n"
" clearTimeout(this.slideshowTimeout);\n"
" this.slideshowTimeout = undefined;\n"
" }\n\n"
" if (this.$controlsContainer) {\n"
" this.$controlsContainer\n"
" .find('div.ss-controls a').removeClass().addClass('play')\n"
" .attr('title', this.playLinkText)\n"
" .attr('href', '#play')\n"
" .html(this.playLinkText);\n"
" }\n"
" \n"
" return this;\n"
" },\n\n"
" // Plays the slideshow\n"
" play: function() {\n"
" this.isSlideshowRunning = true;\n\n"
" if (this.$controlsContainer) {\n"
" this.$controlsContainer\n"
" .find('.ss-controls a').removeClass().addClass('pause')\n"
" .attr('title', this.pauseLinkText)\n"
" .attr('href', '#pause')\n"
" .html(this.pauseLinkText);\n"
" }\n\n"
" if (!this.slideshowTimeout) {\n"
" var gallery = this;\n"
" this.slideshowTimeout = setTimeout(function() { gallery.ssAdvance(); }, this.delay);\n"
" }\n\n"
" return this;\n"
" },\n\n"
" // Toggles the state of the slideshow (playing/paused)\n"
" toggleSlideshow: function() {\n"
" if (this.isSlideshowRunning)\n"
" this.pause();\n"
" else\n"
" this.play();\n\n"
" return this;\n"
" },\n\n"
" // Advances the slideshow to the next image and delegates navigation to the\n"
" // history plugin when history is enabled\n"
" // enableHistory is true\n"
" ssAdvance: function() {\n"
" if (this.isSlideshowRunning)\n"
" this.next(true);\n\n"
" return this;\n"
" },\n\n"
" // Advances the gallery to the next image.\n"
" // @param {Boolean} dontPause Specifies whether to pause the slideshow.\n"
" // @param {Boolean} bypassHistory Specifies whether to delegate navigation to the history plugin when history is enabled. \n"
" next: function(dontPause, bypassHistory) {\n"
" this.gotoIndex(this.getNextIndex(this.currentImage.index), dontPause, bypassHistory);\n"
" return this;\n"
" },\n\n"
" // Navigates to the previous image in the gallery.\n"
" // @param {Boolean} dontPause Specifies whether to pause the slideshow.\n"
" // @param {Boolean} bypassHistory Specifies whether to delegate navigation to the history plugin when history is enabled.\n"
" previous: function(dontPause, bypassHistory) {\n"
" this.gotoIndex(this.getPrevIndex(this.currentImage.index), dontPause, bypassHistory);\n"
" return this;\n"
" },\n\n"
" // Navigates to the next page in the gallery.\n"
" // @param {Boolean} dontPause Specifies whether to pause the slideshow.\n"
" // @param {Boolean} bypassHistory Specifies whether to delegate navigation to the history plugin when history is enabled.\n"
" nextPage: function(dontPause, bypassHistory) {\n"
" var page = this.getCurrentPage();\n"
" var lastPage = this.getNumPages() - 1;\n"
" if (page < lastPage) {\n"
" var startIndex = page * this.numThumbs;\n"
" var nextPage = startIndex + this.numThumbs;\n"
" this.gotoIndex(nextPage, dontPause, bypassHistory);\n"
" }\n\n"
" return this;\n"
" },\n\n"
" // Navigates to the previous page in the gallery.\n"
" // @param {Boolean} dontPause Specifies whether to pause the slideshow.\n"
" // @param {Boolean} bypassHistory Specifies whether to delegate navigation to the history plugin when history is enabled.\n"
" previousPage: function(dontPause, bypassHistory) {\n"
" var page = this.getCurrentPage();\n"
" if (page > 0) {\n"
" var startIndex = page * this.numThumbs;\n"
" var prevPage = startIndex - this.numThumbs; \n"
" this.gotoIndex(prevPage, dontPause, bypassHistory);\n"
" }\n"
" \n"
" return this;\n"
" },\n\n"
" // Navigates to the image at the specified index in the gallery\n"
" // @param {Integer} index The index of the image in the gallery to display.\n"
" // @param {Boolean} dontPause Specifies whether to pause the slideshow.\n"
" // @param {Boolean} bypassHistory Specifies whether to delegate navigation to the history plugin when history is enabled.\n"
" gotoIndex: function(index, dontPause, bypassHistory) {\n"
" if (!dontPause)\n"
" this.pause();\n"
" \n"
" if (index < 0) index = 0;\n"
" else if (index >= this.data.length) index = this.data.length-1;\n"
" \n"
" var imageData = this.data[index];\n"
" \n"
" if (!bypassHistory && this.enableHistory)\n"
" $.historyLoad(String(imageData.hash)); // At the moment, historyLoad only accepts string arguments\n"
" else\n"
" this.gotoImage(imageData);\n\n"
" return this;\n"
" },\n\n"
" // This function is garaunteed to be called anytime a gallery slide changes.\n"
" // @param {Object} imageData An object holding the image metadata of the image to navigate to.\n"
" gotoImage: function(imageData) {\n"
" var index = imageData.index;\n\n"
" if (this.onSlideChange)\n"
" this.onSlideChange(this.currentImage.index, index);\n"
" \n"
" this.currentImage = imageData;\n"
" this.preloadRelocate(index);\n"
" \n"
" this.refresh();\n"
" \n"
" return this;\n"
" },\n\n"
" // Returns the default transition duration value. The value is halved when not\n"
" // performing a synchronized transition.\n"
" // @param {Boolean} isSync Specifies whether the transitions are synchronized.\n"
" getDefaultTransitionDuration: function(isSync) {\n"
" if (isSync)\n"
" return this.defaultTransitionDuration;\n"
" return this.defaultTransitionDuration / 2;\n"
" },\n\n"
" // Rebuilds the slideshow image and controls and performs transitions\n"
" refresh: function() {\n"
" var imageData = this.currentImage;\n"
" if (!imageData)\n"
" return this;\n\n"
" var index = imageData.index;\n\n"
" // Update Controls\n"
" if (this.$controlsContainer) {\n"
" this.$controlsContainer\n"
" .find('.nav-controls a.prev').attr('href', '#'+this.data[this.getPrevIndex(index)].hash).end()\n"
" .find('.nav-controls a.next').attr('href', '#'+this.data[this.getNextIndex(index)].hash);\n"
" }\n\n"
" var previousSlide = this.$imageContainer.find('span.current').addClass('previous').removeClass('current');\n"
" var previousCaption = 0;\n\n"
" if (this.$captionContainer) {\n"
" previousCaption = this.$captionContainer.find('span.current').addClass('previous').removeClass('current');\n"
" }\n\n"
" // Perform transitions simultaneously if syncTransitions is true and the next image is already preloaded\n"
" var isSync = this.syncTransitions && imageData.image;\n\n"
" // Flag we are transitioning\n"
" var isTransitioning = true;\n"
" var gallery = this;\n\n"
" var transitionOutCallback = function() {\n"
" // Flag that the transition has completed\n"
" isTransitioning = false;\n\n"
" // Remove the old slide\n"
" previousSlide.remove();\n\n"
" // Remove old caption\n"
" if (previousCaption)\n"
" previousCaption.remove();\n\n"
" if (!isSync) {\n"
" if (imageData.image && imageData.hash == gallery.data[gallery.currentImage.index].hash) {\n"
" gallery.buildImage(imageData, isSync);\n"
" } else {\n"
" // Show loading container\n"
" if (gallery.$loadingContainer) {\n"
" gallery.$loadingContainer.show();\n"
" }\n"
" }\n"
" }\n"
" };\n\n"
" if (previousSlide.length == 0) {\n"
" // For the first slide, the previous slide will be empty, so we will call the callback immediately\n"
" transitionOutCallback();\n"
" } else {\n"
" if (this.onTransitionOut) {\n"
" this.onTransitionOut(previousSlide, previousCaption, isSync, transitionOutCallback);\n"
" } else {\n"
" previousSlide.fadeTo(this.getDefaultTransitionDuration(isSync), 0.0, transitionOutCallback);\n"
" if (previousCaption)\n"
" previousCaption.fadeTo(this.getDefaultTransitionDuration(isSync), 0.0);\n"
" }\n"
" }\n\n"
" // Go ahead and begin transitioning in of next image\n"
" if (isSync)\n"
" this.buildImage(imageData, isSync);\n\n"
" if (!imageData.image) {\n"
" var image = new Image();\n"
" \n"
" // Wire up mainImage onload event\n"
" image.onload = function() {\n"
" imageData.image = this;\n\n"
" // Only build image if the out transition has completed and we are still on the same image hash\n"
" if (!isTransitioning && imageData.hash == gallery.data[gallery.currentImage.index].hash) {\n"
" gallery.buildImage(imageData, isSync);\n"
" }\n"
" };\n\n"
" // set alt and src\n"
" image.alt = imageData.title;\n"
" image.src = imageData.slideUrl;\n"
" }\n\n"
" // This causes the preloader (if still running) to relocate out from the currentIndex\n"
" this.relocatePreload = true;\n\n"
" return this.syncThumbs();\n"
" },\n\n"
" // Called by the refresh method after the previous image has been transitioned out or at the same time\n"
" // as the out transition when performing a synchronous transition.\n"
" // @param {Object} imageData An object holding the image metadata of the image to build.\n"
" // @param {Boolean} isSync Specifies whether the transitions are synchronized.\n"
" buildImage: function(imageData, isSync) {\n"
" var gallery = this;\n"
" var nextIndex = this.getNextIndex(imageData.index);\n\n"
" // Construct new hidden span for the image\n"
" var newSlide = this.$imageContainer\n"
" .append('<span class=\"image-wrapper current\"><a class=\"advance-link\" rel=\"history\" href=\"#'+this.data[nextIndex].hash+'\" title=\"'+imageData.title+'\"> </a></span>')\n"
" .find('span.current').css('opacity', '0');\n"
" \n"
" newSlide.find('a')\n"
" .append(imageData.image)\n"
" .click(function(e) {\n"
" gallery.clickHandler(e, this);\n"
" });\n"
" \n"
" var newCaption = 0;\n"
" if (this.$captionContainer) {\n"
" // Construct new hidden caption for the image\n"
" newCaption = this.$captionContainer\n"
" .append('<span class=\"image-caption current\"></span>')\n"
" .find('span.current').css('opacity', '0')\n"
" .append(imageData.caption);\n"
" }\n\n"
" // Hide the loading conatiner\n"
" if (this.$loadingContainer) {\n"
" this.$loadingContainer.hide();\n"
" }\n\n"
" // Transition in the new image\n"
" if (this.onTransitionIn) {\n"
" this.onTransitionIn(newSlide, newCaption, isSync);\n"
" } else {\n"
" newSlide.fadeTo(this.getDefaultTransitionDuration(isSync), 1.0);\n"
" if (newCaption)\n"
" newCaption.fadeTo(this.getDefaultTransitionDuration(isSync), 1.0);\n"
" }\n"
" \n"
" if (this.isSlideshowRunning) {\n"
" if (this.slideshowTimeout)\n"
" clearTimeout(this.slideshowTimeout);\n\n"
" this.slideshowTimeout = setTimeout(function() { gallery.ssAdvance(); }, this.delay);\n"
" }\n\n"
" return this;\n"
" },\n\n"
" // Returns the current page index that should be shown for the currentImage\n"
" getCurrentPage: function() {\n"
" return Math.floor(this.currentImage.index / this.numThumbs);\n"
" },\n\n"
" // Applies the selected class to the current image's corresponding thumbnail.\n"
" // Also checks if the current page has changed and updates the displayed page of thumbnails if necessary.\n"
" syncThumbs: function() {\n"
" var page = this.getCurrentPage();\n"
" if (page != this.displayedPage)\n"
" this.updateThumbs();\n\n"
" // Remove existing selected class and add selected class to new thumb\n"
" var $thumbs = this.find('ul.thumbs').children();\n"
" $thumbs.filter('.selected').removeClass('selected');\n"
" $thumbs.eq(this.currentImage.index).addClass('selected');\n\n"
" return this;\n"
" },\n\n"
" // Performs transitions on the thumbnails container and updates the set of\n"
" // thumbnails that are to be displayed and the navigation controls.\n"
" // @param {Delegate} postTransitionOutHandler An optional delegate that is called after\n"
" // the thumbnails container has transitioned out and before the thumbnails are rebuilt.\n"
" updateThumbs: function(postTransitionOutHandler) {\n"
" var gallery = this;\n"
" var transitionOutCallback = function() {\n"
" // Call the Post-transition Out Handler\n"
" if (postTransitionOutHandler)\n"
" postTransitionOutHandler();\n"
" \n"
" gallery.rebuildThumbs();\n\n"
" // Transition In the thumbsContainer\n"
" if (gallery.onPageTransitionIn)\n"
" gallery.onPageTransitionIn();\n"
" else\n"
" gallery.show();\n"
" };\n\n"
" // Transition Out the thumbsContainer\n"
" if (this.onPageTransitionOut) {\n"
" this.onPageTransitionOut(transitionOutCallback);\n"
" } else {\n"
" this.hide();\n"
" transitionOutCallback();\n"
" }\n\n"
" return this;\n"
" },\n\n"
" // Updates the set of thumbnails that are to be displayed and the navigation controls.\n"
" rebuildThumbs: function() {\n"
" var needsPagination = this.data.length > this.numThumbs;\n\n"
" // Rebuild top pager\n"
" if (this.enableTopPager) {\n"
" var $topPager = this.find('div.top');\n"
" if ($topPager.length == 0)\n"
" $topPager = this.prepend('<div class=\"top pagination\"></div>').find('div.top');\n"
" else\n"
" $topPager.empty();\n\n"
" if (needsPagination)\n"
" this.buildPager($topPager);\n"
" }\n\n"
" // Rebuild bottom pager\n"
" if (this.enableBottomPager) {\n"
" var $bottomPager = this.find('div.bottom');\n"
" if ($bottomPager.length == 0)\n"
" $bottomPager = this.append('<div class=\"bottom pagination\"></div>').find('div.bottom');\n"
" else\n"
" $bottomPager.empty();\n\n"
" if (needsPagination)\n"
" this.buildPager($bottomPager);\n"
" }\n\n"
" var page = this.getCurrentPage();\n"
" var startIndex = page*this.numThumbs;\n"
" var stopIndex = startIndex+this.numThumbs-1;\n"
" if (stopIndex >= this.data.length)\n"
" stopIndex = this.data.length-1;\n\n"
" // Show/Hide thumbs\n"
" var $thumbsUl = this.find('ul.thumbs');\n"
" $thumbsUl.find('li').each(function(i) {\n"
" var $li = $(this);\n"
" if (i >= startIndex && i <= stopIndex) {\n"
" $li.show();\n"
" } else {\n"
" $li.hide();\n"
" }\n"
" });\n\n"
" this.displayedPage = page;\n\n"
" // Remove the noscript class from the thumbs container ul\n"
" $thumbsUl.removeClass('noscript');\n"
" \n"
" return this;\n"
" },\n\n"
" // Returns the total number of pages required to display all the thumbnails.\n"
" getNumPages: function() {\n"
" return Math.ceil(this.data.length/this.numThumbs);\n"
" },\n\n"
" // Rebuilds the pager control in the specified matched element.\n"
" // @param {jQuery} pager A jQuery element set matching the particular pager to be rebuilt.\n"
" buildPager: function(pager) {\n"
" var gallery = this;\n"
" var numPages = this.getNumPages();\n"
" var page = this.getCurrentPage();\n"
" var startIndex = page * this.numThumbs;\n"
" var pagesRemaining = this.maxPagesToShow - 1;\n"
" \n"
" var pageNum = page - Math.floor((this.maxPagesToShow - 1) / 2) + 1;\n"
" if (pageNum > 0) {\n"
" var remainingPageCount = numPages - pageNum;\n"
" if (remainingPageCount < pagesRemaining) {\n"
" pageNum = pageNum - (pagesRemaining - remainingPageCount);\n"
" }\n"
" }\n\n"
" if (pageNum < 0) {\n"
" pageNum = 0;\n"
" }\n\n"
" // Prev Page Link\n"
" if (page > 0) {\n"
" var prevPage = startIndex - this.numThumbs;\n"
" pager.append('<a rel=\"history\" href=\"#'+this.data[prevPage].hash+'\" title=\"'+this.prevPageLinkText+'\">'+this.prevPageLinkText+'</a>');\n"
" }\n\n"
" // Create First Page link if needed\n"
" if (pageNum > 0) {\n"
" this.buildPageLink(pager, 0, numPages);\n"
" if (pageNum > 1)\n"
" pager.append('<span class=\"ellipsis\">…</span>');\n"
" \n"
" pagesRemaining--;\n"
" }\n\n"
" // Page Index Links\n"
" while (pagesRemaining > 0) {\n"
" this.buildPageLink(pager, pageNum, numPages);\n"
" pagesRemaining--;\n"
" pageNum++;\n"
" }\n\n"
" // Create Last Page link if needed\n"
" if (pageNum < numPages) {\n"
" var lastPageNum = numPages - 1;\n"
" if (pageNum < lastPageNum)\n"
" pager.append('<span class=\"ellipsis\">…</span>');\n\n"
" this.buildPageLink(pager, lastPageNum, numPages);\n"
" }\n\n"
" // Next Page Link\n"
" var nextPage = startIndex + this.numThumbs;\n"
" if (nextPage < this.data.length) {\n"
" pager.append('<a rel=\"history\" href=\"#'+this.data[nextPage].hash+'\" title=\"'+this.nextPageLinkText+'\">'+this.nextPageLinkText+'</a>');\n"
" }\n\n"
" pager.find('a').click(function(e) {\n"
" gallery.clickHandler(e, this);\n"
" });\n\n"
" return this;\n"
" },\n\n"
" // Builds a single page link within a pager. This function is called by buildPager\n"
" // @param {jQuery} pager A jQuery element set matching the particular pager to be rebuilt.\n"
" // @param {Integer} pageNum The page number of the page link to build.\n"
" // @param {Integer} numPages The total number of pages required to display all thumbnails.\n"
" buildPageLink: function(pager, pageNum, numPages) {\n"
" var pageLabel = pageNum + 1;\n"
" var currentPage = this.getCurrentPage();\n"
" if (pageNum == currentPage)\n"
" pager.append('<span class=\"current\">'+pageLabel+'</span>');\n"
" else if (pageNum < numPages) {\n"
" var imageIndex = pageNum*this.numThumbs;\n"
" pager.append('<a rel=\"history\" href=\"#'+this.data[imageIndex].hash+'\" title=\"'+pageLabel+'\">'+pageLabel+'</a>');\n"
" }\n"
" \n"
" return this;\n"
" }\n"
" });\n\n"
" // Now initialize the gallery\n"
" $.extend(this, defaults, settings);\n"
" \n"
" // Verify the history plugin is available\n"
" if (this.enableHistory && !$.historyInit)\n"
" this.enableHistory = false;\n"
" \n"
" // Select containers\n"
" if (this.imageContainerSel) this.$imageContainer = $(this.imageContainerSel);\n"
" if (this.captionContainerSel) this.$captionContainer = $(this.captionContainerSel);\n"
" if (this.loadingContainerSel) this.$loadingContainer = $(this.loadingContainerSel);\n\n"
" // Initialize the thumbails\n"
" this.initializeThumbs();\n"
" \n"
" if (this.maxPagesToShow < 3)\n"
" this.maxPagesToShow = 3;\n\n"
" this.displayedPage = -1;\n"
" this.currentImage = this.data[0];\n"
" var gallery = this;\n\n"
" // Hide the loadingContainer\n"
" if (this.$loadingContainer)\n"
" this.$loadingContainer.hide();\n\n"
" // Setup controls\n"
" if (this.controlsContainerSel) {\n"
" this.$controlsContainer = $(this.controlsContainerSel).empty();\n"
" \n"
" if (this.renderSSControls) {\n"
" if (this.autoStart) {\n"
" this.$controlsContainer\n"
" .append('<div class=\"ss-controls\"><a href=\"#pause\" class=\"pause\" title=\"'+this.pauseLinkText+'\">'+this.pauseLinkText+'</a></div>');\n"
" } else {\n"
" this.$controlsContainer\n"
" .append('<div class=\"ss-controls\"><a href=\"#play\" class=\"play\" title=\"'+this.playLinkText+'\">'+this.playLinkText+'</a></div>');\n"
" }\n\n"
" this.$controlsContainer.find('div.ss-controls a')\n"
" .click(function(e) {\n"
" gallery.toggleSlideshow();\n"
" e.preventDefault();\n"
" return false;\n"
" });\n"
" }\n"
" \n"
" if (this.renderNavControls) {\n"
" this.$controlsContainer\n"
" .append('<div class=\"nav-controls\"><a class=\"prev\" rel=\"history\" title=\"'+this.prevLinkText+'\">'+this.prevLinkText+'</a><a class=\"next\" rel=\"history\" title=\"'+this.nextLinkText+'\">'+this.nextLinkText+'</a></div>')\n"
" .find('div.nav-controls a')\n"
" .click(function(e) {\n"
" gallery.clickHandler(e, this);\n"
" });\n"
" }\n"
" }\n\n"
" var initFirstImage = !this.enableHistory || !location.hash;\n"
" if (this.enableHistory && location.hash) {\n"
" var hash = $.galleriffic.normalizeHash(location.hash);\n"
" var imageData = allImages[hash];\n"
" if (!imageData)\n"
" initFirstImage = true;\n"
" }\n\n"
" // Setup gallery to show the first image\n"
" if (initFirstImage)\n"
" this.gotoIndex(0, false, true);\n\n"
" // Setup Keyboard Navigation\n"
" if (this.enableKeyboardNavigation) {\n"
" $(document).keydown(function(e) {\n"
" var key = e.charCode ? e.charCode : e.keyCode ? e.keyCode : 0;\n"
" switch(key) {\n"
" case 32: // space\n"
" gallery.next();\n"
" e.preventDefault();\n"
" break;\n"
" case 33: // Page Up\n"
" gallery.previousPage();\n"
" e.preventDefault();\n"
" break;\n"
" case 34: // Page Down\n"
" gallery.nextPage();\n"
" e.preventDefault();\n"
" break;\n"
" case 35: // End\n"
" gallery.gotoIndex(gallery.data.length-1);\n"
" e.preventDefault();\n"
" break;\n"
" case 36: // Home\n"
" gallery.gotoIndex(0);\n"
" e.preventDefault();\n"
" break;\n"
" case 37: // left arrow\n"
" gallery.previous();\n"
" e.preventDefault();\n"
" break;\n"
" case 39: // right arrow\n"
" gallery.next();\n"
" e.preventDefault();\n"
" break;\n"
" }\n"
" });\n"
" }\n\n"
" // Auto start the slideshow\n"
" if (this.autoStart)\n"
" this.play();\n\n"
" // Kickoff Image Preloader after 1 second\n"
" setTimeout(function() { gallery.preloadInit(); }, 0);\n\n"
" return this;\n"
"};\n"
"})(jQuery);`")
match = regex.search(test_str)
if match:
print(f"Match was found at {match.start()}-{match.end()}: {match.group()}")
for group_num, group in enumerate(match.groups(), start=1):
print(f"Group {group_num} found at {match.start(group_num)}-{match.end(group_num)}: {group}")
Please keep in mind that these code samples are automatically generated and are not guaranteed to work. If you find any syntax errors, feel free to submit a bug report. For a full regex reference for Python, please visit: https://docs.python.org/3/library/re.html