Show limited no. of page links in pagination - html

I want to display a limited no. of page links, say 5 out of 10 links, and was wondering is there any known or tried and tested method to achieve this.
so lets say user can right now see following links
previous, 1(selected), 2, 3, 4, 5... next
user clicks on, say 4, now he sees
previous... 3, 4(selected), 5, 6, 7...next
now he clicks on 7
previous... 6, 7(selected), 8, 9, 10...next
Now I believe this to be something very common in pagination programming. So is there any known algo to do this. I am feeling too lazy to cookup my own!
Edit:- This needs to be achieved on the server side. I am working on C#, however you can pitch in algo in any language.

The problem with some of the answers, especially with Tyrone's is that it only updates the navigation when the remainder is 0, if you want it to update on every click then the following is much better:
var start,
end,
pagesCutOff = 5,
ceiling = Math.ceil(pagesCutOff / 2),
floor = Math.floor(pagesCutOff / 2);
if(numPages < pagesCutOff) {
start = 0;
end = numPages;
} else if(currentPage >= 1 && currentPage <= ceiling) {
start = 0;
end = pagesCutOff;
} else if((currentPage + floor) >= numPages) {
start = (numPages - pagesCutOff);
end = numPages;
} else {
start = (currentPage - ceiling);
end = (currentPage + floor);
}
Obviously you send through the currentPage and numPages yourself to the function, this will keep the current page centered in the pagination list, the number of buttons shown should be an odd number so that the selected page can be "in the middle" of the list.
You can then do the following loop:
for (var i = start; i < end; i++) {
//Your code here
}
If you want to add a next and previous button to this then simply do something like:
if(currentPage !== 1) {
$('< Previous').appendTo(navElement);
}
Where navElement is a jQuery object of where you want to append the list to like: $('#pagination-nav');
Hope this helps someone!
Cheers

Tested this and it works even though it could be a little more graceful. I am using C# here, but the logic for any language should be the same. If you come up with a better solution please post. If you have any questions please post to. I have commented the code to help better explain what is going on.
//Get the current page we are on
int start = currentPage;
int end = currentPage;
//If the page cannot be devised by 5 enter the loop
if ((start % 5 != 0) && (end % 5 != 0))
{
//Get the next nearest page that is divisible by 5
while ((end % 5) != 0)
{
end++;
}
//Get the previous nearest page that is divisible by 5
while ((start % 5) != 0)
{
start--;
}
}
//The page is divisible by 5 so get the next 5 pages in the pagination
else
{
end += 5;
}
//We are on the first page
if (start == 0)
{
start++;
end++;
}
//We are on the last page
if (end == lastpage)
{
end = lastpage;
}
//Post your pagination links below
for (int i = start; i < end; i++)
{
//Your code here
}

The problem with some of the answers, especially with Tyrone's is that it only updates the navigation when the remainder is 0, if you want it to update on every click then the following is much better:
//Get the current page we are on
int start = currentPage;
int end = currentPage;
//If the page cannot be devised by 5 enter the loop
if ((start % 5 != 0) && (end % 5 != 0))
{
//Get the next nearest page that is divisible by 5
while ((end % 5) != 0)
{
end++;
}
//Get the previous nearest page that is divisible by 5
while ((start % 5) != 0)
{
start--;
}
}
//The page is divisible by 5 so get the next 5 pages in the pagination
else
{
end += 5;
}
//We are on the first page
if (start == 0)
{
start++;
end++;
}
//We are on the last page
if (end == lastpage)
{
end = lastpage;
}
//Post your pagination links below
for (int i = start; i < end; i++)
{
//Your code here
}

You can't do this with just HTML. You'll have to use either PHP or javascript (or some other scripting language) to generate the links.

I had to limit no of page links on the pager to 3 on small (mobile) devices. I used following code to achieve it.
//If it is full page, pager
//Assume all page links are visible
//Get the actual pages links count
var noOfPages = $('ul.pagination li.pager__item').not('.pager__item--first, .pager__item--previous,.pager__item--last, .pager__item--next').length;
var pages = [];
if (noOfPages > 3) {
var activePage = 0;
var index = 0;
$('ul.pagination li.pager__item').not('.pager__item--first, .pager__item--previous,.pager__item--last, .pager__item--next').each(function() {
//Get the active page no.
if ($(this).hasClass('current')) {
activePage = index;
}
//Hide page link
$(this).css('display', 'none');
pages.push($(this));
index++;
});
var start;
var end;
//We are at page 1 (i.e page=0)
if(activePage == 0) {
start = 0;
end = 2;
}
// Wer are at last page
else if(activePage == noOfPages - 1) {
start = activePage - 2;
end = activePage;
}
else {
start = activePage - 1;
end = activePage + 1;
}
for (var i = start; i <= end; i++) {
pages[i].css('display', 'inline-block');
}
}

Related

More than 100 widgets in single page [duplicate]

Is there any max number for sections while adding to card in googlescript?
I have a card with 13 sections in it, but it displays only 12 sections, Is there any specific limit on adding sections to the card
I have multiple sections which are going to add dynamically to the card,Is this possible to add n number of sections to the card?.
Yes, as of now the limit of sections a Card can have is 100.
However, 13 items are not a problem. The problem you are facing lies somewhere else in your code. Without that information we are not able to help.
As proof, here is some code that successfully shows how to adds 15 widgets:
function buildAddOn(e) {
var tempsections = [];
for (var i =0 ; i<15; i++) {
var tempsection = CardService.newCardSection().setHeader('Texts '+ i + 'aaaa.');
tempsection.addWidget(CardService.newTextButton().setText("Sections num"+i).setOnClickAction(CardService.newAction().setFunctionName("test")));
tempsections.push(tempsection);
}
// Build the main card after adding the section.
var card = CardService.newCardBuilder()
.setHeader(CardService.newCardHeader()
.setTitle('Quick Label')
.setImageUrl('https://www.gstatic.com/images/icons/material/system/1x/label_googblue_48dp.png'))
.addSection(tempsections[0])
.addSection(tempsections[1])
.addSection(tempsections[2])
.addSection(tempsections[3])
.addSection(tempsections[4])
.addSection(tempsections[5])
.addSection(tempsections[6])
.addSection(tempsections[7])
.addSection(tempsections[8])
.addSection(tempsections[9])
.addSection(tempsections[10])
.addSection(tempsections[11])
.addSection(tempsections[12])
.addSection(tempsections[13])
.addSection(tempsections[14])
.build();
return [card];
}
function test() {
return 0;
}
Also, for proof, here is the change to add 101 sections:
function buildAddOn(e) {
var tempsections = [];
for (var i =0 ; i<101; i++) {
var tempsection = CardService.newCardSection().setHeader('Texts '+ i + 'aaaa.');
tempsection.addWidget(CardService.newTextButton().setText("Sections num"+i).setOnClickAction(CardService.newAction().setFunctionName("test")));
tempsections.push(tempsection);
}
// Build the main card after adding the section.
var card = CardService.newCardBuilder()
.setHeader(CardService.newCardHeader()
.setTitle('Quick Label')
.setImageUrl('https://www.gstatic.com/images/icons/material/system/1x/label_googblue_48dp.png'));
for (var i =0 ; i<101; i++) {
card = card.addSections(tempsections[i]);
}
card = card.build();
return [card];
}

Programming Greatest Common Divisor for HTML Webpage

EDIT: So the question is, why will my script not execute properly.
Edit 2: So this is the part that works as advertised and all items are predefined as number inputs prior too.
<script>
function Script5(){
var numeratorIn = document.getElementById("Numerator").value;
var denominatorIn = document.getElementById("Denominator").value;
var FACTOR = document.getElementById("FACTOR").value;
var Snum = document.getElementById("Snum").value;
var Sden = document.getElementById("Sden").value;
var x = document.getElementById("FinalAnswer");
x.style.display = 'none';
var x = document.getElementById("FinalDisplay");
x.style.display = 'block';
}
</script>
So I am working on a series of codes for a website I will be developing and this is a small subroutine for finding greatest common divisors. I am using Notepad++ for mobility purposes and trying to run my code in Google Chrome to start. I want to make a GCD function for variables (numeratorIn,denominatorIn). Again in HTML
This is the part I want to add into the same script
var a = Math.floor(Math.sqrt(numeratorIn));
var b = Math.floor(Math.sqrt(denominatorIn));
document.getElementById("midpage15").innerHTML = (+a);
var k = 1
if (a<b) {
while (k<a) {
if ((Snum/k == Math.floor(Snum/k)) && (Sden/k == Math.floor(Sden/k)); {
var h = k;
}
k = k++;
}
}
else if (a>b) {
while (k<b) {
if ((Snum/k == Math.floor(Snum/k)) && (Sden/k == Math.floor(Sden/k)) {
var h = k;
}
k = k++;
}
}
else (a == b) {
document.getElementById("midpage15").innerHTML = ("Final Answer 1");
}
But it breaks my button that I use to activate the script every time :(
I usually program in Python but I want to make this in HTML. So I spent about 5 hours working on it and it is just driving me nuts. I am 100% confident it has something to do with the Ifs and the while statements at the bottom.
Here Snum and Sden are string.So you need to convert it into integer first.For that you can use parseInt() function.

Can Google apps script be used to randomize page order on Google forms?

Update #2: Okay, I'm pretty sure my error in update #1 was because of indexing out of bounds over the array (I'm still not used to JS indexing at 0). But here is the new problem... if I write out the different combinations of the loop manually, setting the page index to 1 in moveItem() like so:
newForm.moveItem(itemsArray[0][0], 1);
newForm.moveItem(itemsArray[0][1], 1);
newForm.moveItem(itemsArray[0][2], 1);
newForm.moveItem(itemsArray[1][0], 1);
newForm.moveItem(itemsArray[1][1], 1);
newForm.moveItem(itemsArray[1][2], 1);
newForm.moveItem(itemsArray[2][0], 1);
...
...I don't get any errors but the items end up on different pages! What is going on?
Update #1:: Using Sandy Good's answer as well as a script I found at this WordPress blog, I have managed to get closer to what I needed. I believe Sandy Good misinterpreted what I wanted to do because I wasn't specific enough in my question.
I would like to:
Get all items from a page (section header, images, question etc)
Put them into an array
Do this for all pages, adding these arrays to an array (i.e: [[all items from page 1][all items from page 2][all items from page 3]...])
Shuffle the elements of this array
Repopulate a new form with each element of this array. In this way, page order will be randomized.
My JavaScript skills are poor (this is the first time I've used it). There is a step that produces null entries and I don't know why... I had to remove them manually. I am not able to complete step 5 as I get the following error:
Cannot convert Item,Item,Item to (class).
"Item,Item,Item" is the array element containing all the items from a particular page. So it seems that I can't add three items to a page at a time? Or is something else going on here?
Here is my code:
function shuffleForms() {
var itemsArray,shuffleQuestionsInNewForm,fncGetQuestionID,
newFormFile,newForm,newID,shuffle, sections;
// Copy template form by ID, set a new name
newFormFile = DriveApp.getFileById('1prfcl-RhaD4gn0b2oP4sbcKaRcZT5XoCAQCbLm1PR7I')
.makeCopy();
newFormFile.setName('AAAAA_Shuffled_Form');
// Get ID of new form and open it
newID = newFormFile.getId();
newForm = FormApp.openById(newID);
// Initialize array to put IDs in
itemsArray = [];
function getPageItems(thisPageNum) {
Logger.log("Getting items for page number: " + thisPageNum );
var thisPageItems = []; // Used for result
var thisPageBreakIndex = getPageItem(thisPageNum).getIndex();
Logger.log( "This is index num : " + thisPageBreakIndex );
// Get all items from page
var allItems = newForm.getItems();
thisPageItems.push(allItems[thisPageBreakIndex]);
Logger.log( "Added pagebreak item: " + allItems[thisPageBreakIndex].getIndex() );
for( var i = thisPageBreakIndex+1; ( i < allItems.length ) && ( allItems[i].getType() != FormApp.ItemType.PAGE_BREAK ); ++i ) {
thisPageItems.push(allItems[i]);
Logger.log( "Added non-pagebreak item: " + allItems[i].getIndex() );
}
return thisPageItems;
}
function shuffle(array) {
var currentIndex = array.length, temporaryValue, randomIndex;
Logger.log('shuffle ran')
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
function shuffleAndMove() {
// Get page items for all pages into an array
for(i = 2; i <= 5; i++) {
itemsArray[i] = getPageItems(i);
}
// Removes null values from array
itemsArray = itemsArray.filter(function(x){return x});
// Shuffle page items
itemsArray = shuffle(itemsArray);
// Move page items to the new form
for(i = 2; i <= 5; ++i) {
newForm.moveItem(itemsArray[i], i);
}
}
shuffleAndMove();
}
Original post: I have used Google forms to create a questionnaire. For my purposes, each question needs to be on a separate page but I need the pages to be randomized. A quick Google search shows this feature has not been added yet.
I see that the Form class in the Google apps script has a number of methods that alter/give access to various properties of Google Forms. Since I do not know Javascript and am not too familiar with Google apps/API I would like to know if what I am trying to do is even possible before diving in and figuring it all out.
If it is possible, I would appreciate any insight on what methods would be relevant for this task just to give me some direction to get started.
Based on comments from Sandy Good and two SE questions found here and here, this is the code I have so far:
// Script to shuffle question in a Google Form when the questions are in separate sections
function shuffleFormSections() {
getQuestionID();
createNewShuffledForm();
}
// Get question IDs
function getQuestionID() {
var form = FormApp.getActiveForm();
var items = form.getItems();
arrayID = [];
for (var i in items) {
arrayID[i] = items[i].getId();
}
// Logger.log(arrayID);
return(arrayID);
}
// Shuffle function
function shuffle(a) {
var j, x, i;
for (i = a.length; i; i--) {
j = Math.floor(Math.random() * i);
x = a[i - 1];
a[i - 1] = a[j];
a[j] = x;
}
}
// Shuffle IDs and create new form with new question order
function createNewShuffledForm() {
shuffle(arrayID);
// Logger.log(arrayID);
var newForm = FormApp.create('Shuffled Form');
for (var i in arrayID) {
arrayID[i].getItemsbyId();
}
}
Try this. There's a few "constants" to be set at the top of the function, check the comments. Form file copying and opening borrowed from Sandy Good's answer, thanks!
// This is the function to run, all the others here are helper functions
// You'll need to set your source file id and your destination file name in the
// constants at the top of this function here.
// It appears that the "Title" page does not count as a page, so you don't need
// to include it in the PAGES_AT_BEGINNING_TO_NOT_SHUFFLE count.
function shuffleFormPages() {
// UPDATE THESE CONSTANTS AS NEEDED
var PAGES_AT_BEGINNING_TO_NOT_SHUFFLE = 2; // preserve X intro pages; shuffle everything after page X
var SOURCE_FILE_ID = 'YOUR_SOURCE_FILE_ID_HERE';
var DESTINATION_FILE_NAME = 'YOUR_DESTINATION_FILE_NAME_HERE';
// Copy template form by ID, set a new name
var newFormFile = DriveApp.getFileById(SOURCE_FILE_ID).makeCopy();
newFormFile.setName(DESTINATION_FILE_NAME);
// Open the duplicated form file as a form
var newForm = FormApp.openById(newFormFile.getId());
var pages = extractPages(newForm);
shuffleEndOfPages(pages, PAGES_AT_BEGINNING_TO_NOT_SHUFFLE);
var shuffledFormItems = flatten(pages);
setFormItems(newForm, shuffledFormItems);
}
// Builds an array of "page" arrays. Each page array starts with a page break
// and continues until the next page break.
function extractPages(form) {
var formItems = form.getItems();
var currentPage = [];
var allPages = [];
formItems.forEach(function(item) {
if (item.getType() == FormApp.ItemType.PAGE_BREAK && currentPage.length > 0) {
// found a page break (and it isn't the first one)
allPages.push(currentPage); // push what we've built for this page onto the output array
currentPage = [item]; // reset the current page to just this most recent item
} else {
currentPage.push(item);
}
});
// We've got the last page dangling, so add it
allPages.push(currentPage);
return allPages;
};
// startIndex is the array index to start shuffling from. E.g. to start
// shuffling on page 5, startIndex should be 4. startIndex could also be thought
// of as the number of pages to keep unshuffled.
// This function has no return value, it just mutates pages
function shuffleEndOfPages(pages, startIndex) {
var currentIndex = pages.length;
// While there remain elements to shuffle...
while (currentIndex > startIndex) {
// Pick an element between startIndex and currentIndex (inclusive)
var randomIndex = Math.floor(Math.random() * (currentIndex - startIndex)) + startIndex;
currentIndex -= 1;
// And swap it with the current element.
var temporaryValue = pages[currentIndex];
pages[currentIndex] = pages[randomIndex];
pages[randomIndex] = temporaryValue;
}
};
// Sourced from elsewhere on SO:
// https://stackoverflow.com/a/15030117/4280232
function flatten(array) {
return array.reduce(
function (flattenedArray, toFlatten) {
return flattenedArray.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten);
},
[]
);
};
// No safety checks around items being the same as the form length or whatever.
// This mutates form.
function setFormItems(form, items) {
items.forEach(function(item, index) {
form.moveItem(item, index);
});
};
I tested this code. It created a new Form, and then shuffled the questions in the new Form. It excludes page breaks, images and section headers. You need to provide a source file ID for the original template Form. This function has 3 inner sub-functions. The inner functions are at the top, and they are called at the bottom of the outer function. The arrayOfIDs variable does not need to be returned or passed to another function because it is available in the outer scope.
function shuffleFormSections() {
var arrayOfIDs,shuffleQuestionsInNewForm,fncGetQuestionID,
newFormFile,newForm,newID,items,shuffle;
newFormFile = DriveApp.getFileById('Put the source file ID here')
.makeCopy();
newFormFile.setName('AAAAA_Shuffled_Form');
newID = newFormFile.getId();
newForm = FormApp.openById(newID);
arrayOfIDs = [];
fncGetQuestionID = function() {
var i,L,thisID,thisItem,thisType;
items = newForm.getItems();
L = items.length;
for (i=0;i<L;i++) {
thisItem = items[i];
thisType = thisItem.getType();
if (thisType === FormApp.ItemType.PAGE_BREAK ||
thisType === FormApp.ItemType.SECTION_HEADER ||
thisType === FormApp.ItemType.IMAGE) {
continue;
}
thisID = thisItem.getId();
arrayOfIDs.push(thisID);
}
Logger.log('arrayOfIDs: ' + arrayOfIDs);
//the array arrayOfIDs does not need to be returned since it is available
//in the outermost scope
}// End of fncGetQuestionID function
shuffle = function() {// Shuffle function
var j, x, i;
Logger.log('shuffle ran')
for (i = arrayOfIDs.length; i; i--) {
j = Math.floor(Math.random() * i);
Logger.log('j: ' + j)
x = arrayOfIDs[i - 1];
Logger.log('x: ' + x)
arrayOfIDs[i - 1] = arrayOfIDs[j];
arrayOfIDs[j] = x;
}
Logger.log('arrayOfIDs: ' + arrayOfIDs)
}
shuffleQuestionsInNewForm = function() {
var i,L,thisID,thisItem,thisQuestion,questionType;
L = arrayOfIDs.length;
for (i=0;i<L;i++) {
thisID = arrayOfIDs[i];
Logger.log('thisID: ' + thisID)
thisItem = newForm.getItemById(thisID);
newForm.moveItem(thisItem, i)
}
}
fncGetQuestionID();//Get all the question ID's and put them into an array
shuffle();
shuffleQuestionsInNewForm();
}

On page reload display diffrent SWF file

I have a html page "first" playing 1.swf and on the next page
"reload" it plays 2.swf. How do I do that?
Code i now use for the first one:
<center><object width="500" height="500" data="1.swf"></object></center>
One really simple solution would be to create a cookie incrementer:
var number = parseInt(getCookie("number"));
if (number == "") {
document.cookie="number=1";
} else {
document.cookie="number=" + (number + 1);
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for(var i=0; i<ca.length; i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1);
if (c.indexOf(name) == 0) return c.substring(name.length,c.length);
}
return "";
}
Now, every time the page reloads, number gets incremented by 1.
You can now remove the previous swf and create a new swf with the new source.
JSFIDDLE

A* algorithm works OK, but not perfectly. What's wrong?

This is my grid of nodes:
I'm moving an object around on it using the A* pathfinding algorithm. It generally works OK, but it sometimes acts wrongly:
When moving from 3 to 1, it correctly goes via 2. When going from 1 to 3 however, it goes via 4.
When moving between 3 and 5, it goes via 4 in either direction instead of the shorter way via 6
What can be wrong? Here's my code (AS3):
public static function getPath(from:Point, to:Point, grid:NodeGrid):PointLine {
// get target node
var target:NodeGridNode = grid.getClosestNodeObj(to.x, to.y);
var backtrace:Map = new Map();
var openList:LinkedSet = new LinkedSet();
var closedList:LinkedSet = new LinkedSet();
// begin with first node
openList.add(grid.getClosestNodeObj(from.x, from.y));
// start A*
var curNode:NodeGridNode;
while (openList.size != 0) {
// pick a new current node
if (openList.size == 1) {
curNode = NodeGridNode(openList.first);
}
else {
// find cheapest node in open list
var minScore:Number = Number.MAX_VALUE;
var minNext:NodeGridNode;
openList.iterate(function(next:NodeGridNode, i:int):int {
var score:Number = curNode.distanceTo(next) + next.distanceTo(target);
if (score < minScore) {
minScore = score;
minNext = next;
return LinkedSet.BREAK;
}
return 0;
});
curNode = minNext;
}
// have not reached
if (curNode == target) break;
else {
// move to closed
openList.remove(curNode);
closedList.add(curNode);
// put connected nodes on open list
for each (var adjNode:NodeGridNode in curNode.connects) {
if (!openList.contains(adjNode) && !closedList.contains(adjNode)) {
openList.add(adjNode);
backtrace.put(adjNode, curNode);
}
}
}
}
// make path
var pathPoints:Vector.<Point> = new Vector.<Point>();
pathPoints.push(to);
while(curNode != null) {
pathPoints.unshift(curNode.location);
curNode = backtrace.read(curNode);
}
pathPoints.unshift(from);
return new PointLine(pathPoints);
}
NodeGridNode::distanceTo()
public function distanceTo(o:NodeGridNode):Number {
var dx:Number = location.x - o.location.x;
var dy:Number = location.y - o.location.y;
return Math.sqrt(dx*dx + dy*dy);
}
The problem I see here is the line
if (!openList.contains(adjNode) && !closedList.contains(adjNode))
It may be the case that an adjNode may be easier(shorter) to reach through the current node although it was reached from another node previously which means it is in the openList.
Found the bug:
openList.iterate(function(next:NodeGridNode, i:int):int {
var score:Number = curNode.distanceTo(next) + next.distanceTo(target);
if (score < minScore) {
minScore = score;
minNext = next;
return LinkedSet.BREAK;
}
return 0;
});
The return LinkedSet.BREAK (which acts like a break statement in a regular loop) should not be there. It causes the first node in the open list to be selected always, instead of the cheapest one.