I've created a modal window that will show thumbnails for each of my Facebook albums. But the problem I'm having is when I fetch my album pictures with the Fb Graph API, the images are different sizes! So, while my modal window might be forcing a certain width for each thumbnail, the heights can vary according to the size of the picture returned. I can't correct this by forcing a max-height or min-height because it stretches and shrinks the pictures and they end up looking funny! I've been looking at fetching the album images with some type of parameter to 'square' the images or 'crop' the images but haven't had any luck. Any help would be greatly appreciated.
Here is the call to the Fb Graph API that I'v ebeen playing with in the graph explorer to try and force a standard size for each image.
'me?fields=id,name,albums{cover_photo,photo_count,picture,name}'
here are some failed attempts to make each picture equal.
me?fields=id,name,albums{cover_photo,photo_count,picture?type=square,name}
me?fields=id,name,albums{cover_photo,photo_count,picture?type=thumbnail,name}
me?fields=id,name,albums{cover_photo,photo_count,picture.height(200).width(200),name}
Here is my js code to fetch the album images and then put them into a modal window.
scope.getFbAlbumData = function(accessToken) {
var albumIds = [];
var columnCount = 0;
FB.api('me?fields=id,name,albums{cover_photo,photo_count,picture,name}', 'get', {
access_token: accessToken
}, function(response) {
if (response.error) {
yb.base.displayNotification('Oh no! Something went wrong. Please contact YogaBandy Help Desk', 'danger');
return false;
}
$('#uploadModal').on('hidden.bs.modal', function() {
var data = response.albums.data;
var rowContents = "";
$('#modalContainer').load(scope.enumControllers.getAlbumModal, function(response, status, xhr) {
for (var i = 0, l = data.length; i < l; i++) {
if (columnCount < 3) {
rowContents += "<div class=\"col-sm-4\" style=\"padding-right: 5px; padding-left: 5px;\"><div class=\"thumbnail\" data-albumid=\"" + data[i].id + "\"><img style=\"\" src=\"" + data[i].picture.data.url + "\" alt=\"...\" class=\"d\"><div class=\"caption\"><p class=\"album-title-name\">" + data[i].name + "</p><p>" + data[i].photo_count + " photos</p></div></div></div>";
columnCount++;
}
if (i == l || columnCount == 3 || i + 1 == l) {
$("#albumPicturesModalBody").append("<div class=\"row\">" + rowContents + "<\div>");
rowContents = "";
columnCount = 0;
}
}
$("#albumPicturesModal").modal('show');
});
});
$('#uploadModal').modal('hide');
});
}
Here is what the modal window looks like, you can see the images are different sizes.
I want the images to be more uniform like this!
Related
I’m coding an experiment with a 12x12 grid of image buttons in jsPsych. Participants receive feedback based on which squares they choose to click. These squares are all initially white (white squares are saved as an img file called “old”) but after a square has been clicked I want it to turn grey (saved as an img file called “new”) for all subsequent trials (100 total). To do this, I’ve been trying to get the src of a button to update after it has been clicked. I'm relatively new to coding so any help would be greatly appreciated, thank you!
I was able to change the image on an individual trial. Usually the trial would end upon click, so I turned that off and was able to see the square change to grey after it was clicked. However, all the squares revert back to white for the next trial.
var squares = new Array(144);
for (var t = 0; t < squares.length; t++) {
squares[t] = t;
};
var responseGrid = {
type: jsPsychHtmlButtonResponseGridFeedback, // plugin arranges buttons in 12 x 12 grid
stimulus: [],
prompt: "<p>Please select a key</p>",
choices: squares,
button_html: '<button class= "jspsych-btn-grid" style = "display: grid" onclick = "color(this)"><img id="%choice%" src="imgRareEnvironment/%choice%-old.jpg"></button>',
image_size: 100,
response_ends_trial: false,
trial_duration: 6000,
on_finish: function(data){
i = i + 1;
data.i = i;
var lastSquare = data.response;
clickedSquares.push(data.response);
},
};
function color(el){
var img_id = el.querySelector('img').id;
var img_el = document.getElementById(img_id);
img_el.src = img_el.src.replace("old","new");
};
To try and fix this so the colour change carried over for all future trials, I made the button_html a function and included an on_finish function that goes through all the squares and updates them to grey if they are included in the "clicked squares" array. However, this gives an error "Uncaught TypeError: Cannot set properties of null (setting 'src')". I'm not sure why it can't find the images to update the src, or if there's a better way to go about this.
var responseGrid = {
type: jsPsychHtmlButtonResponseGridFeedback,
stimulus: [],
prompt: "<p>Please select a key</p>",
choices: squares,
button_html: function(choice) {
var button_html = '';
var choice = '%choice%';
var id = 'square-' + choice;
var src = "imgRareEnvironment/" + choice + "-old.jpg";
button_html += '<button class="jspsych-btn-grid" style="display: grid"><img id="' + id + '" src="' + src + '"></button>';
return button_html;
},
image_size: 100,
on_finish: function(data){
i = i + 1;
data.i = i;
var lastSquare = data.response;
clickedSquares.push(lastSquare);
for (let t = 0; t < 144; t++) {
let image = document.getElementById('square-' + t);
if (clickedSquares.includes(t)) {
image.src = "imgRareEnvironment/" + t + "-new.jpg";
}
}
},
};
I have a javascript object contains 3000 records of addresses. If i load all the addresses in tiles in a container, it's very slow. What i want is :
- I want to load 15-18 address tiles on load
- setting the container height dynamically calculating from tile height so that scroll will be created on container load based on number of records. ex: tile height: 80px, and total address records: 3000, so the container height = 3000*80
- i want to append few tiles from javascript object to html template on page scroll
But when scroll very first, my approach is not working. i can see blank space on bottom. So is there anything that i can follow to achieve to load more addresses on fast scroll also.
Take a look at this fiddle: jsFiddle
It uses standard JavaScript and jQuery to load an infinite amount of results based upon scroll position.
//View contains references and data concerning the screen, scroll, etc...
var view = {};
var tileSize = 72;
//List of results
var results = [];
//Function to get results.
//If you load all on load, ignor this
function getResults(min, max) {
//Here should be an AJAX call to the server
//I'll just generate some dummies since i don't have access to a server
var returner = []
for (var i = min; i < max; i++) {
returner.push({
"LastName" : ["Ken", "Zarah", "Thrawn", "Oakenshield"][Math.round(Math.random() * 3)],
"EmailAddress" : "alteam06#scanapp.local",
"Id" : Math.round(Math.random() * 1000),
"FirstName" : ["Sai", "Zayda", "Han", "Lurtz"][Math.round(Math.random() * 3)],
"Drawn" : false
})
}
//Return results
return returner
}
//Function to display new tiles
function drawTiles() {
//We want to manipulate the actual DOM tree as little as possible to save calculations.
//Therefore we will compile our results into one string and then append them all at once.
var html = '';
//Only get results that hasn't yet been drawn
results.filter(function (result) {
return result.Drawn == false;
}).forEach(function (el) {
html += '<div style="height:' + tileSize + 'px; float:left; margin: 2em;">';
html += '<p>' + el.FirstName + ' ' + el.LastName + '</p>';
html += '<p>' + el.EmailAddress + '</p>';
html += '<p>' + el.Id + '</p>';
html += '</div>';
el.drawn = true;
});
document.getElementById("results").innerHTML += html;
}
//Function handle to request and draw a bundle
function getBundle() {
//Change the min, max here if you plan to send these parameters with AJAX
results = results.concat(getResults(0, 10));
drawTiles();
}
jQuery(document).ready(function () {
//Listen for scroll events
jQuery(document).on("scroll", function (evt) {
//If we are low enough in the scroll
if (window.scrollY > jQuery("#results").height() - window.innerHeight - (2 * tileSize)) {
getBundle();
}
})
//Run initial build
getBundle();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<!-- The container for the tiles -->
<div id="results" style="float:left">
</div>
I am generating pdf from jsPDF api , I want to add footer to each page with page number .
How to achieve this . It is having option of adding footer from fromHTML plugin , but I am writing without HTML.
var doc = new jsPDF("portrait","px","a4");
You have to implement it yourself. You can do something like this:
var doc = new jsPDF();
doc.page=1; // use this as a counter.
function footer(){
doc.text(150,285, 'page ' + doc.page); //print number bottom right
doc.page ++;
};
// and call footer() after each doc.addPage()
Stephen Collins is the best answer! It works well with jspdf-autotable plugin.
With this is made after all is added to the doc, so we can use easy the total page number!
Add some style to the Stephen Collins answer: "page x of total"
const addFooters = doc => {
const pageCount = doc.internal.getNumberOfPages()
doc.setFont('helvetica', 'italic')
doc.setFontSize(8)
for (var i = 1; i <= pageCount; i++) {
doc.setPage(i)
doc.text('Page ' + String(i) + ' of ' + String(pageCount), doc.internal.pageSize.width / 2, 287, {
align: 'center'
})
}
}
let doc = new jsPDF()
doc.text(...)
doc.autoTable(...)
addFooters(doc)
doc.save()
I know this post is old but I'm going to offer another solution.
First define your total amount of pages. There's multiple ways to determine this so I won't go into that.
var doc = new jsPDF('p', 'pt', 'letter');
doc.page = 1; // use this as a counter.
var totalPages = 10; // define total amount of pages
// HEADER
doc.setFontSize(20);//optional
doc.setTextColor(40);//optional
doc.setFontStyle('normal');//optional
doc.text("Report", 50, 22);// set your margins
// FOOTER
var str = "Page " + doc.page + " of " + totalPages;
doc.setFontSize(10);// optional
doc.text(str, 50, doc.internal.pageSize.height - 10);//key is the interal pageSize function
// Add Page content
.....
//Add new page and increase page count
doc.addPage();
doc.page ++;
//Begin process all over again.
This works well in a loop as you can set your page count by taking your array.length + 1 (as it's zero based).
Run this function before you run doc.save()
function addFooters() {
const pageCount = doc.internal.getNumberOfPages();
for(var i = 1; i <= pageCount; i++) {
doc.text(String(i), 196, 285);
}
}
It's work for me:
I just put coordenates for A4 Paper;
Just add the for before doc.save() like this;
// Create a document
var doc = new jsPDF('p','mm','a4');
// Some stuff
doc.text("Some text", 74, 150);
doc.addPage();
doc.text("Some text", 74, 150);
doc.addPage();
doc.text("Some text", 74, 150);
doc.addPage();
doc.text("Some text", 74, 150);
doc.addPage();
doc.text("Last page", 74, 150);
// PAGE NUMBERING
// Add Page number at bottom-right
// Get the number of pages
const pageCount = doc.internal.getNumberOfPages();
// For each page, print the page number and the total pages
for(var i = 1; i <= pageCount; i++) {
// Go to page i
doc.setPage(i);
//Print Page 1 of 4 for example
doc.text('Page ' + String(i) + ' of ' + String(pageCount),210-20,297-30,null,null,"right");
}
// Save the doc
doc.save('test.pdf');
If you need something like "current page / totalPage" displaying for each page.
Using "Total page number" plugin available in jspdf v1.0+
How to use:
var doc = new jsPDF();
doc.page=1; // use this as a counter.
var totalPagesExp = "{total_pages_count_string}";
function footer(){
var str = "Page " + doc.page;
if (typeof doc.putTotalPages === 'function') {
str = str + "/" + totalPagesExp;
}
doc.text(150,285, str); //print number bottom right
}
// call footer() after each doc.addPage()
// and before doc.save() do not forget put
if (typeof doc.putTotalPages === 'function') {
doc.putTotalPages(totalPagesExp);
}
It should work. Hope this helps.
After digging into the code, I think the feature you ask is not implemented. But there is a function to generate a footer from html and you can use this code to fullfill your need. But beware some part of the code is marked as "bad hack".
From plugins/from_html.js
checkForFooter = function (elem, renderer, elementHandlers) {
//check if we can found a <footer> element
var footer = elem.getElementsByTagName("footer");
if (footer.length > 0) {
footer = footer[0];
//bad hack to get height of footer
//creat dummy out and check new y after fake rendering
var oldOut = renderer.pdf.internal.write;
var oldY = renderer.y;
renderer.pdf.internal.write = function () {};
DrillForContent(footer, renderer, elementHandlers);
var footerHeight = Math.ceil(renderer.y - oldY) + 5;
renderer.y = oldY;
renderer.pdf.internal.write = oldOut;
//add 20% to prevent overlapping
renderer.pdf.margins_doc.bottom += footerHeight;
//Create function render header on every page
var renderFooter = function (pageInfo) {
var pageNumber = pageInfo !== undefined ? pageInfo.pageNumber : 1;
//set current y position to old margin
var oldPosition = renderer.y;
//render all child nodes of the header element
renderer.y = renderer.pdf.internal.pageSize.height - renderer.pdf.margins_doc.bottom;
renderer.pdf.margins_doc.bottom -= footerHeight;
//check if we have to add page numbers
var spans = footer.getElementsByTagName('span');
for (var i = 0; i < spans.length; ++i) {
//if we find some span element with class pageCounter, set the page
if ((" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" pageCounter ") > -1) {
spans[i].innerHTML = pageNumber;
}
//if we find some span element with class totalPages, set a variable which is replaced after rendering of all pages
if ((" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" totalPages ") > -1) {
spans[i].innerHTML = '###jsPDFVarTotalPages###';
}
}
//render footer content
DrillForContent(footer, renderer, elementHandlers);
//set bottom margin to previous height including the footer height
renderer.pdf.margins_doc.bottom += footerHeight;
//important for other plugins (e.g. table) to start rendering at correct position after header
renderer.y = oldPosition;
};
//check if footer contains totalPages which shoudl be replace at the disoposal of the document
var spans = footer.getElementsByTagName('span');
for (var i = 0; i < spans.length; ++i) {
if ((" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" totalPages ") > -1) {
renderer.pdf.internal.events.subscribe('htmlRenderingFinished', renderer.pdf.putTotalPages.bind(renderer.pdf, '###jsPDFVarTotalPages###'), true);
}
}
//register event to render footer on every new page
renderer.pdf.internal.events.subscribe('addPage', renderFooter, false);
//render footer on first page
renderFooter();
//prevent footer rendering
SkipNode['FOOTER'] = 1;
}
};
I want to paginate a text in some div so it will fit the allowed area
Logic is pretty simple:
1. split text into words
2. add word by word into and calculate element height
3. if we exceed the height - create next page
It works quite good
here is JS function i've used:
function paginate() {
var newPage = $('<pre class="text-page" />');
contentBox.empty().append(newPage);
var betterPageText='';
var pageNum = 0;
var isNewPage = false;
var lineHeight = parseInt(contentBox.css('line-height'), 10);
var wantedHeight = contentBox.height() - lineHeight;
for (var i = 0; i < words.length; i++) {
if (isNewPage) {
isNewPage = false;
} else {
betterPageText = betterPageText + ' ' + words[i];
}
newPage.text(betterPageText + ' ...');
if (newPage.height() >= wantedHeight) {
pageNum++;
if (pageNum > 0) {
betterPageText = betterPageText + ' ...';
}
newPage.text(betterPageText);
newPage.clone().insertBefore(newPage)
betterPageText = '...';
isNewPage = true;
} else {
newPage.text(betterPageText);
}
}
contentBox.craftyslide({ height: wantedHeight });
}
But when i add an image it break everything. In this case text overflows 'green' area.
Working fiddle: http://jsfiddle.net/74W4N/7/
Is there a better way to paginate the text and calculate element height?
Except the fact that there are many more variables to calculate,not just only the word width & height, but also new lines,margins paddings and how each browser outputs everything.
Then by adding an image (almost impossible if the image is higher or larger as the max width or height) if it's smaller it also has margins/paddings. and it could start at the end of a line and so break up everything again.basically only on the first page you could add an image simply by calculating it's width+margin and height+margin/lineheight. but that needs alot math to get the wanted result.
Said that i tried some time ago to write a similar script but stopped cause of to many problems and different browser results.
Now reading your question i came across something that i read some time ago:
-webkit-column-count
so i made a different approach of your function that leaves out all this calculations.
don't judge the code as i wrote it just now.(i tested on chrome, other browsers need different prefixes.)
var div=document.getElementsByTagName('div')[0].firstChild,
maxWidth=300,
maxHeigth=200,
div.style.width=maxWidth+'px';
currentHeight=div.offsetHeight;
columns=Math.ceil(currentHeight/maxHeigth);
div.style['-webkit-column-count']=columns;
div.style.width=(maxWidth*columns)+'px';
div.style['-webkit-transition']='all 700ms ease';
div.style['-webkit-column-gap']='0px';
//if you change the column-gap you need to
//add padding before calculating the normal div.
//also the line height should be an integer that
// is divisible of the max height
here is an Example
http://jsfiddle.net/HNF3d/10/
adding an image smaller than the max height & width in the first page would not mess up everything.
and it looks like it's supported by all modern browsers now.(with the correct prefixes)
In my experience, trying to calculate and reposition text in HTML is almost an exercise in futility. There are too many variations among browsers, operating systems, and font issues.
My suggestion would be to take advantage of the overflow CSS property. This, combined with using em sizing for heights, should allow you to define a div block that only shows a defined number of lines (regardless of the size and type of the font). Combine this with a bit of javascript to scroll the containing div element, and you have pagination.
I've hacked together a quick proof of concept in JSFiddle, which you can see here: http://jsfiddle.net/8CMzY/1/
It's missing a previous button and a way of showing the number of pages, but these should be very simple additions.
EDIT: I originally linked to the wrong version for the JSFiddle concept
Solved by using jQuery.clone() method and performing all calculations on hidden copy of original HTML element
function paginate() {
var section = $('.section');
var cloneSection = section.clone().insertAfter(section).css({ position: 'absolute', left: -9999, width: section.width(), zIndex: -999 });
cloneSection.css({ width: section.width() });
var descBox = cloneSection.find('.holder-description').css({ height: 'auto' });
var newPage = $('<pre class="text-page" />');
contentBox.empty();
descBox.empty();
var betterPageText = '';
var pageNum = 0;
var isNewPage = false;
var lineHeight = parseInt(contentBox.css('line-height'), 10);
var wantedHeight = contentBox.height() - lineHeight;
var oldText = '';
for (var i = 0; i < words.length; i++) {
if (isNewPage) {
isNewPage = false;
descBox.empty();
}
betterPageText = betterPageText + ' ' + words[i];
oldText = betterPageText;
descBox.text(betterPageText + ' ...');
if (descBox.height() >= wantedHeight) {
if (i != words.length - 1) {
pageNum++;
if (pageNum > 0) {
betterPageText = betterPageText + ' ...';
}
oldText += ' ... ';
}
newPage.text(oldText);
newPage.clone().appendTo(contentBox);
betterPageText = '... ';
isNewPage = true;
} else {
descBox.text(betterPageText);
if (i == words.length - 1) {
newPage.text(betterPageText).appendTo(contentBox);
}
}
}
if (pageNum > 0) {
contentBox.craftyslide({ height: wantedHeight });
}
cloneSection.remove();
}
live demo: http://jsfiddle.net/74W4N/19/
I actually came to an easier solution based on what #cocco has done, which also works in IE9.
For me it was important to keep the backward compatibility and the animation and so on was irrelevant so I stripped them down. You can see it here: http://jsfiddle.net/HNF3d/63/
heart of it is the fact that I dont limit height and present horizontal pagination as vertical.
var parentDiv = div = document.getElementsByTagName('div')[0];
var div = parentDiv.firstChild,
maxWidth = 300,
maxHeigth = 200,
t = function (e) {
div.style.webkitTransform = 'translate(0,-' + ((e.target.textContent * 1 - 1) * maxHeigth) + 'px)';
div.style["-ms-transform"] = 'translate(0,-' + ((e.target.textContent * 1 - 1) * maxHeigth) + 'px)';
};
div.style.width = maxWidth + 'px';
currentHeight = div.offsetHeight;
columns = Math.ceil(currentHeight / maxHeigth);
links = [];
while (columns--) {
links[columns] = '<span>' + (columns + 1) + '</span>';
}
var l = document.createElement('div');
l.innerHTML = links.join('');
l.onclick = t;
document.body.appendChild(l)
I wrote a nice heatmap in javascript, and that worked pretty nice so far. The heatmap is basically a table with a coloring variation, based on the threshold of the value displayed in the table. I used JavaScript to create the table, and to set up the colors. However, I wanted to show a nice pop up window, so when the user hover over the table's cell, some additional information is displayed. I found this library qTip2
$(document).ready(function(){
$('#mytable td').qtip({
overwrite : false, // make sure it can't be overwritten
content : {
text : function(api){
return "Time spent: " + $(this).html();
}
},
position : {
my : 'top left',
target : 'mouse',
viewport : $(window), //keep it on-screen at all time if possible
adjust : {
x : 10, y : 10
}
},
hide : {
fixed : true // Helps to prevent the tooltip from hiding occassionaly when tracking!
},
style : 'ui-tooltip-tipsy ui-tooltip-shadow'
});
});
This function creates the heatmap:
function makeTable(data)
{
var row = new Array();
var cell = new Array();
var row_num = 26;
var cell_num = 44;
var tab = document.createElement('table');
tab.setAttribute('id', 'mytable');
tab.border = '1px';
var tbo = document.createElement('tbody');
for(var i = 0; i < row_num; i++){
row[i] = document.createElement('tr');
var upper = (i+1)*44;
var lower = i*44;
for(var j = lower; j < upper; j++){
cell[j] = document.createElement('td');
//cell[j].setAttribute('class', 'selector');
if(data[j] != undefined){
var count = document.createTextNode(data[j].diff);
cell[j].appendChild(count);
var index = parseInt(data[j].diff);
/* specify which color better suits the heatmap */
if(index >= 0 && index <= 100){
cell[j].style.backgroundColor = '#00BFFF';
}
else if(index > 100 && index <= 1000){
cell[j].style.backgroundColor = "#6495ED";
}
else if(index > 1000 && index <= 4000){
cell[j].style.backgroundColor = "#4682B4";
}
else if(index > 4000 && index <= 6000){
cell[j].style.backgroundColor = "#0000FF";
}
else{
cell[j].style.backgroundColor = "#00008B";
}
row[i].appendChild(cell[j]);
}
}
tbo.appendChild(row[i]);
}
tab.appendChild(tbo);
document.getElementById('mytable').appendChild(tab);
}
Inside of my <body> tag I have:
<div id="body">
<div id="mytable"></div>
</div>
However, when I load the page, I expect to see the pop up box when I hover the mouse over the table's cell, however something happens. Also, when I execute that $(document).ready part from firebug's terminal, then the program starts to execute as suppose to. I also made sure the library is being loaded into my page before I used it. I also don't see any errors in the firebug's terminal.
<script src="http://localhost/heatmap/javascript/jquery.qtip.js">
Could someone please give me a clue why is this happening?
The main function of my javascript is
function OnLoad() {
$.post('index.php/heatmap/getDatalines',
function(answer){
var data = eval('(' + answer + ')');
var list = [];
makeTable(data);
});
}
Thanks
whis is called on load: google.setOnLoadCallback(OnLoad);
You need to create the qtip after you have loaded the table like this:
function OnLoad() {
$.post('index.php/heatmap/getDatalines',
function(answer){
var data = eval('(' + answer + ')');
var list = [];
makeTable(data);
$('#mytable td').qtip({
overwrite : false, // make sure it can't be overwritten
content : {
text : function(api){
return "Time spent: " + $(this).html();
}
},
position : {
my : 'top left',
target : 'mouse',
viewport : $(window), // keep it on-screen at all time if possible
adjust : {
x : 10, y : 10
}
},
hide : {
fixed : true // Helps to prevent the tooltip from hiding occassionaly when tracking!
},
style : 'ui-tooltip-tipsy ui-tooltip-shadow'
});
});
}