I am developing an extension for Google Chrome that was working perfectly, but now stopped working with version 2 of the manifest.
It is giving me the following error:
Uncaught SyntaxError: Unexpected end of input
popup.js:
chrome.tabs.getSelected(null, function (aba) {
link = aba.url;
titulo = aba.title;
document.getElementById('mensagem').value = link;
});
function GrabIFrameURL(feedDescription) {
var regex = new RegExp("iframe(?:.*?)src='(.*?)'", 'g');
var matches = regex.exec(feedDescription);
var url = '';
if (matches.length == 2) {
url = matches[1];
}
var quebra = url.split("/");
return quebra[4];
}
$(document).ready(function () {
if (localStorage.nome == '' || localStorage.nome == null || localStorage.email == '' || localStorage.email == null) {
jQuery('#formulario').hide();
jQuery('#erros').html('Configure seus dados aqui');
}
jQuery("#resposta").ajaxStart(function () {
jQuery(this).html("<img src='img/loading.gif'> <b>Sugestão sendo enviada, aguarde...</b>");
});
jQuery('#submit').click(function () {
var nome = localStorage.nome;
var email = localStorage.email;
var mensagem = titulo + "\n\n<br><br>" + link;
jQuery('#formulario').hide();
jQuery.post('http://blabloo.com.br/naosalvo/mail.php', {
nome: nome,
email: email,
mensagem: mensagem
},
function (data, ajaxStart) {
jQuery('#resposta').html(data);
console.log(data);
});
return false;
});
//Listagem de posts
var bkg = chrome.extension.getBackgroundPage();
jQuery('#close').click(function () {
window.close();
});
jQuery('#markeall').click(function () {
bkg.lidoAll();
$('.naolido').attr('class', 'lido');
});
jQuery.each(bkg.getFeed(), function (id, item) {
if (item.naolido == '1')
lidoClass = 'naolido';
else
lidoClass = 'lido';
var thumb = GrabIFrameURL(item.description);
$('#feed').append('<li><a id="' + item.id + '" href="' + item.link + '" class="' + lidoClass + '"><img src="' + $.jYoutube(thumb, 'small') + '" class="' + lidoClass + '"/>' + item.title + '</a></li>');
});
$('.naolido').click(function (e) {
e.preventDefault();
klicked = $(this).attr('id');
console.log(klicked);
bkg.setLido(klicked);
$(this).attr('class', 'lido');
openLink($(this).attr('href'));
});
$('.lido').click(function (e) {
openLink($(this).attr('href'));
});
var openLink = function (link) {
chrome.tabs.create({
'url': link,
'selected': true
});
window.close();
}
});
I think the problem is in this part of popup.js:
jQuery.each(bkg.getFeed(), function (id, item) {
if (item.naolido == '1')
lidoClass = 'naolido';
else
lidoClass = 'lido';
var thumb = GrabIFrameURL(item.description);
$('#feed').append('<li><a id="' + item.id + '" href="' + item.link + '" class="' + lidoClass + '"><img src="' + $.jYoutube(thumb, 'small') + '" class="' + lidoClass + '"/>' + item.title + '</a></li>');
});
You problem is actually in background.js on lines 12 and 20, you set localStorage.items to '' initially but then try to run a JSON.parse() on it later, but it's not JSON data so JSON.parse returns Unexpected end of input. And, JSON.parse isn't a function you define but a native browser function so, it shows the error as being on "line 1".
Try defining it as an empty array initially and then "pushing" to it as you are now.
Related
Through the attached code I do a search on youtube based on the username and the results are shown. If I search twice, the results add up. I would like previous results to be deleted. I try with htmlString = card; but it show only one result.Thanks to everyone who wants to help me solve this problem.
var musicCards = [];
jQuery(document).ready(function() {
jQuery("#searchButton").on("click", function() {
var query = jQuery("#queryInput").val();
if (query != "") {
loadYoutubeService(query);
console.log(query + "");
}
});
});
function loadYoutubeService(query) {
gapi.client.load('youtube', 'v3', function() {
gapi.client.setApiKey('ADADADADADA');
search(query);
});
}
function search(query) {
var request = gapi.client.youtube.search.list({
part: 'snippet',
q: query,
type: 'channel',
maxResults: 15
});
request.execute(function(response) {
jQuery.each(response.items, function(i, item) {
if (!item['']) {
var musicCard = {};
musicCard._id = item['snippet']['customUrl'];
musicCard.title = item['snippet']['title'];
musicCard.linkprofilo = item['snippet']['channelId'];
musicCard.url = "https://www.youtube.com/channel/";
musicCard.description = item['snippet']['description'];
musicCard.immagine = item['snippet']['thumbnails']['high']['url'];
musicCards.push(musicCard);
}
});
renderView();
});
}
function renderView() {
var htmlString = "";
musicCards.forEach(function(musicCard, i) {
var card = createCard(musicCard._id, musicCard.title, musicCard.description, musicCard.url,musicCard.immagine, musicCard.linkprofilo);
htmlString += card;
});
jQuery('#youtube-utente').html(htmlString);
}
function createCard(_id, title, description, url, immagine, linkprofilo) {
var card =
'<div class="card">' +
'<div class="info">' +
'<img src="' + immagine + '" alt="' + description + '">' +
'</div>' +
'<div class="content">Clicca per selezionare:' +
'<h3>' + title + '</h3>' +
'<a class="seleziona" href="' + url +linkprofilo+'">'+ url +linkprofilo+'</a>' +
'<p>' + description + '</p>' +
'</div>' +
'</div>';
return card;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
Solved using :
request.execute(function(response) {
musicCards.length = 0; // clear array
I have a jQuery loop that iterates over specifics elements of an HTML page. For every element, I do a switch over a variable and append HTML code in specific places.
The problem is that, one of those appends is an import to another Javascript file. This file uses a variable from the first one but, for some reason, that variable doesn't always have the correct value, depending on the order of the HTML elements in the page.
UPDATE
As requested, I created a Plunker so it's easy to see code:
http://plnkr.co/edit/mrEhgbZhhvu0Z4iniXGl?p=preview
Note: For this to work, you need to have correct pageId and appId for Instagram.
I'll put the code to be more clear:
index.html
<!DOCTYPE html>
<html>
<head>
<title>Demo</title>
<link rel="stylesheet" href="estilo.css">
</head>
<body>
<section id="rrss">
<!-- If I put this article block as the last one, it works -->
<article id="instagram">
<div data-rrss="instagram"></div>
</article>
<br/>
<article id="facebook">
<div data-rrss="facebook"></div>
</article>
<br/>
<article id="twitter">
<div data-rrss="twitter"></div>
</article>
</section>
<!-- SCRIPTS -->
<script src='scripts/data.js'></script>
<script src='scripts/jquery.js'></script>
<script>var customJquery = $.noConflict(true);</script>
<script src='../magic.js'></script>
</body>
</html>
data.js
var data = {
"facebook": {
"id": "facebook",
"width": 0,
"height": 0,
"custom_style": "border:none;overflow:hidden",
"hide_cover": false,
"show_facepile": true,
"small_header": false,
"adapt_container_width": true
},
"twitter": {
"id": "twitter",
"width": 0,
"height": 0,
"chrome": "nofooter noscrollbar noheader", // noborders transparent
"tweet_limit": 0,
"lang": "es",
"theme": "dark",
"link_color": "#0084b4"
},
"instagram": {
"id": "123456798123467/9876543219876543",
"hidecaption": false,
"custom_style": "overflow:auto;",
"max_width": 0,
"max_height": 500
},
"defaults": {
"width": 380,
"height": 500
}
}
magic.js
var rrss = customJquery('div[data-rrss]');
var conf = undefined;
var defaults = undefined;
var node = document.querySelectorAll('[data-rrss="instagram"]')[0];
customJquery.each(rrss, function(ix, it) {
var html = '';
var network = customJquery(it).data('rrss');
if (network === undefined || network == null || network.length <= 0)
return;
conf = data[network];
if (conf === undefined ||conf === null || conf.length <= 0)
return;
defaults = data['defaults'];
//Comprobamos si existe el key y si el value tiene texto
if(conf.id === undefined || conf.id === null || conf.id.length === 0)
return;
switch(network) {
case 'facebook':
html = '<iframe id="iFB" src="https://www.facebook.com/plugins/page.php?href=https%3A%2F%2Fwww.facebook.com%2F' + conf.id +
'&tabs=timeline' +
'&width=' + (conf.width <= 0 ? defaults.width : conf.width) +
'&height=' + (conf.height <= 0 ? defaults.height : conf.height) +
'&small_header=' + conf.small_header +
'&adapt_container_width=' + conf.adapt_container_width +
'&hide_cover=' + conf.hide_cover +
'&show_facepile=' + conf.show_facepile + '"' +
'width="' + (conf.width <= 0 ? defaults.width : conf.width) + '" ' +
'height="' + (conf.height <= 0 ? defaults.height : conf.height) + '" ' +
'style="' + conf.custom_style + '" ' +
'scrolling="no" frameborder="0" allowTransparency="true" allow="encrypted-media"></iframe>\n' +
'<script type="text/javascript">\n' +
' setInterval(() => {\n' +
' customJquery("#iFB")[0].src = customJquery("#iFB")[0].src\n' +
' }, 5 * 60 * 1000);\n'
'</script>';
break;
case 'twitter':
html = '<a class="twitter-timeline" '+
'href="https://twitter.com/' + conf.id + '" ' +
'data-width="' + (conf.width <= 0 ? defaults.width : conf.width) + '" ' +
'data-height="' + (conf.height <= 0 ? defaults.height : conf.height) + '" ';
if (conf.chrome !== undefined && conf.chrome !== '') {
html += 'data-chrome="' + conf.chrome + '" ';
}
if (conf.tweet_limit > 0) {
html += 'data-tweet-limit="' + conf.tweet_limit + '" ';
}
html += 'data-lang="' + conf.lang + '" ' +
'data-theme="' + conf.theme + '" ' +
'data-link-color="' + conf.link_color + '"' +
'>Tweets by ' + conf.id + '</a>\n' +
'<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>';
break;
case 'instagram':
node = node.parentElement;
html = '<script async src="https://connect.facebook.net/es_ES/sdk.js"></script>\n' +
'<script src="../insta.js"></script>\n' +
'<script async defer src="https://www.instagram.com/embed.js"></script>\n' +
'<script>\n'+
' setInterval(() => {\n' +
' if (document.readyState === "complete") {\n' +
' window.instgrm.Embeds.process();\n' +
' }\n' +
' }, 100);\n' +
' setInterval(() => {\n' +
' fbAsyncInit();\n' +
' }, 5 * 60 * 1000);\n'
'</script>';
break;
default:
html = '';
}
if (html != '') {
customJquery(it).replaceWith(html);
}
});
insta.js
window.fbAsyncInit = function () {
var html = '';
var style = '';
// When the Instagram's article bloke in HTML isn't the last one, this shows data from Twitter bloke
console.log(conf);
if (node !== undefined) {
if (document.getElementById('instagram') !== null) {
document.getElementById('instagram').innerHTML = '';
}
if (conf !== undefined && conf !== '') {
if (conf.max_width !== undefined && conf.max_width > 0) {
style += 'max-width: ' + conf.max_width + 'px;';
} else {
style += 'max-width: ' + defaults.width + 'px;';
}
if (conf.max_height !== undefined && conf.max_height > 0) {
style += 'max-height: ' + conf.max_height + 'px;';
} else {
style += 'max-height: ' + defaults.height + 'px;';
}
style += conf.custom_style;
}
var div = document.createElement('div');
div.id = 'instagram';
if (style !== '') {
div.style = style;
}
node.appendChild(div);
}
var pageId = conf.id.substring(0, conf.id.indexOf('/'));
var appId = conf.id.substring(conf.id.indexOf('/') + 1);
FB.init({
appId: appId,
autoLogAppEvents: true,
xfbml: true,
version: "v3.1"
});
FB.getLoginStatus(function (response) {
if (response.status === "connected") {
FB.api(
"/" + pageId + "/",
"GET", { "fields": "instagram_business_account" },
function (response) {
if (response.error && response.error !== '') {
console.log("Error recovering 'instagram_business_account': " + response.error.message);
} else {
FB.api(
"/" + response.instagram_business_account.id + "/media",
"GET", { "fields": "shortcode" },
function (response) {
for (var i = 0; i < response.data.length; i++) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
html = JSON.parse(this.response).html;
document.getElementById("instagram").innerHTML += html;
}
};
xhttp.open("GET", "https://api.instagram.com/oembed/?url=http://instagr.am/p/" + response.data[i].shortcode + "&omitscript=true&hidecaption=" + conf.hidecaption, true);
xhttp.send();
}
}
);
}
}
);
} else {
console.log("Error recovering access token: Not connected.")
console.log(response)
}
});
};
Well, I've solved with some ugly lonely line:
case 'instagram':
node = node.parentElement;
instaconf = conf; // ***** Saved the conf here *****
html = '<script async src="https://connect.facebook.net/es_ES/sdk.js"></script>\n' +
'<script src="../tecInsta.js"></script>\n' +
'<script async defer src="https://www.instagram.com/embed.js"></script>\n' +
'<script>\n'+
' setInterval(() => {\n' +
' if (document.readyState === "complete") {\n' +
' window.instgrm.Embeds.process();\n' +
' }\n' +
' }, 100);\n' +
' setInterval(() => {\n' +
' fbAsyncInit();\n' +
' }, 5 * 60 * 1000);\n'
'</script>';
break;
Then changed the conf references to instaconf in insta.js file.
Since this file was loaded after the jQuery loops ended, the configuration was the last one in that loop (the last article in index.html file).
Here the problem is you are appending HTML elements dynamically. Here jquery will append in DOM then browser will some time to parse. Until that controller wont wait for the operation so loop continue execution. so here config is hold last element of twitter configuration.
And we can't pass reference to html string as per my knowledge. This we can achieve by passing config as string into html string from there we can pass to fbAsyncInit() method.
'<script>\n'+
' setInterval(() => {\n' +
' if (document.readyState === "complete") {\n' +
' window.instgrm.Embeds.process();\n' +
' }\n' +
' }, 100);\n' +
' setInterval(() => {\n' +
' let configuration = '+ JSON.stringify(conf) +';'+
' fbAsyncInit(configuration);\n' +
' }, 5 * 60 * 1000);\n'
'</script>';
and we can receive as
window.fbAsyncInit = function (con) {
or we can pass call back function to html method and we do operation which is done in magic.js
Reference: http://api.jquery.com/html/#html-function
then we can return html accordingly.
I hope this will help you.
I have javascript code to view a news from RSS as a vertical list.
(function ($) {
$.fn.FeedEk = function (opt) {
var def = $.extend({
MaxCount: 5,
ShowDesc: true,
ShowPubDate: true,
DescCharacterLimit: 0,
TitleLinkTarget: "_blank",
DateFormat: "",
DateFormatLang:"en"
}, opt);
var id = $(this).attr("id"), i, s = "", dt;
$("#" + id).empty();
if (def.FeedUrl == undefined) return;
$("#" + id).append('<img src="loader.gif" />');
var YQLstr = 'SELECT channel.item FROM feednormalizer WHERE output="rss_2.0" AND url ="' + def.FeedUrl + '" LIMIT ' + def.MaxCount;
$.ajax({
url: "https://query.yahooapis.com/v1/public/yql?q=" + encodeURIComponent(YQLstr) + "&format=json&diagnostics=false&callback=?",
dataType: "json",
success: function (data) {
$("#" + id).empty();
if (!(data.query.results.rss instanceof Array)) {
data.query.results.rss = [data.query.results.rss];
}
$.each(data.query.results.rss, function (e, itm) {
s += '<li><div class="itemTitle"><a href="' + itm.channel.item.link + '" target="' + def.TitleLinkTarget + '" >' + itm.channel.item.title + '</a></div>';
if (def.ShowPubDate){
dt = new Date(itm.channel.item.pubDate);
s += '<div class="itemDate">';
if ($.trim(def.DateFormat).length > 0) {
try {
moment.lang(def.DateFormatLang);
s += moment(dt).format(def.DateFormat);
}
catch (e){s += dt.toLocaleDateString();}
}
else {
s += dt.toLocaleDateString();
}
s += '</div>';
}
if (def.ShowDesc) {
s += '<div class="itemContent">';
if (def.DescCharacterLimit > 0 && itm.channel.item.description.length > def.DescCharacterLimit) {
s += itm.channel.item.description.substring(0, def.DescCharacterLimit) + '...';
}
else {
s += itm.channel.item.description;
}
s += '</div>';
}
});
$("#" + id).append('<ul class="feedEkList">' + s + '</ul>');
}
});
};
})(jQuery);
I need help to move the list of topics as horizontal one by one, in one line. by used javascript code. this code display just 5 topics, which I need it, but I have problem to how can I movement it as horizontal.
I have a javascript file here.What it does is,when a user selects seats accordings to his preference in a theater layout,the selected seats are stored in an array named "seat".This code works fine until function where the selected seats are shown in a window alert.But from there onwards,the code doesn't seem to do anything.
After the above window alert, I've tried to serialize the above array and send it to the "confirm.php" file.But it does not show anything when echoed the seats.
Here is the js code.
<script type="text/javascript">
$(function () {
var settings = {
rows: 6,
cols: 15,
rowCssPrefix: 'row-',
colCssPrefix: 'col-',
seatWidth: 80,
seatHeight: 80,
seatCss: 'seat',
selectedSeatCss: 'selectedSeat',
selectingSeatCss: 'selectingSeat'
};
var init = function (reservedSeat) {
var seat = [], seatNo, className;
for (i = 0; i < settings.rows; i++) {
for (j = 0; j < settings.cols; j++) {
seatNo = (i + j * settings.rows + 1);
className = settings.seatCss + ' ' + settings.rowCssPrefix + i.toString() + ' ' + settings.colCssPrefix + j.toString();
if ($.isArray(reservedSeat) && $.inArray(seatNo, reservedSeat) != -1) {
className += ' ' + settings.selectedSeatCss;
}
seat.push('<li class="' + className + '"' +
'style="top:' + (i * settings.seatHeight).toString() + 'px;left:' + (j * settings.seatWidth).toString() + 'px">' +
'<a title="' + seatNo + '">' + seatNo + '</a>' +
'</li>');
}
}
$('#place').html(seat.join(''));
};
var jArray = <?= json_encode($seats) ?>;
init(jArray);
$('.' + settings.seatCss).click(function () {
if ($(this).hasClass(settings.selectedSeatCss)) {
alert('This seat is already reserved!');
} else {
$(this).toggleClass(settings.selectingSeatCss);
}
});
$('#btnShowNew').click(function () {
var seat = [], item;
$.each($('#place li.' + settings.selectingSeatCss + ' a'), function (index, value) {
item = $(this).attr('title');
seat.push(item);
});
window.alert(seat);
});
$('#btnsubmit').click(function () {
var seat = [], item;
$.each($('#place li.' + settings.selectingSeatCss + ' a'), function (index, value) {
item = $(this).attr('title');
seat.push(item);
var seatar = JSON.stringify(seat);
$.ajax({
method: "POST",
url: "confirm.php",
data: {data: seatar}
});
});
});
});
</script>
Can somebody help me figure it out what's wrong in here?
Please Add content type as json.
$.ajax({
method: "POST",
url: "confirm.php",
contentType: "application/json"
data: {data: seatar}
});
For testing you can print file_get_contents('php://input') as this works regardless of content type.
I have a small problem with a function that calls itself from within itself.
The following function pretty much works, as can be seen here ...
http://jsfiddle.net/justinayles/frPZ8/2/
but if you click a square and after that click the back link and go back and forth a few times, eventually it will slow down and ultimately crash with a 'too much recursion error'. I cant really see any other way to achieve what I am trying to do here ? I kind of understand what the problem is, but was wondering if there was another way to do this, maybe rename the function? im trying to avoid duplication too much code. any help appreciated, thanks.
var questionArray = [];
var cardArray = [];
// extend array for IE !!
if (!Array.prototype.filter) {
Array.prototype.filter = function(fun /*, thisp*/) {
var len = this.length >>> 0;
if (typeof fun != "function")
throw new TypeError();
var res = [];
var thisp = arguments[1];
for (var i = 0; i < len; i++) {
if (i in this) {
var val = this[i]; // in case fun mutates this
if (fun.call(thisp, val, i, this))
res.push(val);
}
}
return res;
};
}
function setupPage(whichpage, questionArray) {
var html = "",
backlink = "",
activestep = "",
undertext = "",
qArray = questionArray;
switch(whichpage) {
case '1':
var pg1 = questionArray.filter(function (el) {
return el.page == "step1";
});
$.each(pg1, function(key,val) {
html += '<a class="quest ' + val.star + '" href="' + val.path + '" target="' + val.target + '">' + val.title + '</a>';
});
backlink = "0";
activestep = "1";
undertext = "";
break;
case '2a':
var pg2a = questionArray.filter(function (el) {
return el.page == "step2a";
});
$.each(pg2a, function(key,val) {
html += '<a class="quest ' + val.star + '" href="' + val.path + '" target="' + val.target + '">' + val.title + '</a>';
});
backlink = "1";
activestep = "2";
undertext = "";
break;
case '2b':
var pg2b = questionArray.filter(function (el) {
return el.page == "step2b";
});
$.each(pg2b, function(key,val) {
html += '<a class="quest ' + val.star + '" href="' + val.path + '" target="' + val.target + '">' + val.title + '</a>';
});
backlink = "1";
activestep = "2";
undertext = "";
break;
case '3a':
var pg3a = cardArray.filter(function (el) {
return el.page == "3a";
});
$.each(pg3a, function(key,val) {
html += '<div class="cardblock"><a class="wrapcard" href="' + val.path + '" target="' + val.target + '"><img border="0" alt="' + val.title + '" src="http://dummyimage.com/178x112/000/00ffd5.png" alt="placeholder image" /></a>';
html += '<a class="cardtitle" href="' + val.path + '" target="' + val.target + '">' + val.title + '</a></div>';
});
backlink = "2a";
activestep = "3";
undertext = "Choose a programme";
break;
case '3b':
var pg3b = cardArray.filter(function (el) {
return el.page == "3b";
});
$.each(pg3b, function(key,val) {
html += '<div class="cardblock"><a class="wrapcard" href="' + val.path + '" target="' + val.target + '"><img border="0" alt="' + val.title + '" src="http://dummyimage.com/178x112/000/00ffd5.png" /></a>';
html += '<a class="cardtitle" href="' + val.path + '" target="' + val.target + '">' + val.title + '</a></div>';
});
backlink = "2b";
activestep = "3";
undertext = "Choose a programme";
break;
}
// make the dom changes..
if ( backlink !== "0" ) {
html += '<a id="backlink" href="' + backlink + '"><< back a step</a>';
}
$('.wrapdots span').removeClass('active');
$('.wrapdots span.step'+activestep).addClass('active');
$('p.underdots').html(undertext);
$('#wrapper').fadeOut('fast', function() {
$(this).html(html).fadeIn('fast');
});
$('#wrapper').on('click', '#backlink', function(e) {
e = e || window.event;
e.target = e.target || e.srcElement;
var goto = e.target.href;
goto = goto.split('/');
goto = goto.pop();
switch(goto) {
case '1':
e.preventDefault();
setupPage('1', qArray);
break;
case '2a':
e.preventDefault();
setupPage('2a', qArray);
break;
case '2b':
e.preventDefault();
setupPage('2b', qArray);
break;
case '3a':
e.preventDefault();
setupPage('3a', qArray);
break;
case '3b':
e.preventDefault();
setupPage('3b', qArray);
break;
}
});
$('#wrapper').on('click', '.quest', function(e) {
e = e || window.event;
e.target = e.target || e.srcElement;
var goto = e.target.href;
goto = goto.split('/');
goto = goto.pop();
switch(goto) {
case '1':
e.preventDefault();
setupPage('1', qArray);
break;
case '2a':
e.preventDefault();
setupPage('2a', qArray);
break;
case '2b':
e.preventDefault();
setupPage('2b', qArray);
break;
case '3a':
e.preventDefault();
setupPage('3a', qArray);
break;
case '3b':
e.preventDefault();
setupPage('3b', qArray);
break;
default:
e.preventDefault();
break;
}
});
}
// doc ready...
$(function() {
// do questions
$('question').each(function() {
var qobj = {
title : $(this).attr('qTitle'),
star : $(this).attr('class'),
path : $(this).attr('path'),
page : $(this).attr('page'),
target : $(this).attr('target')
}
questionArray.push(qobj);
});
// got the questions, lets now setup page 1 !!
setupPage('1', questionArray);
// do cards
$('card').each(function() {
var cobj = {
title : $(this).attr('cTitle'),
path : $(this).attr('path'),
img : $(this).attr('img'),
page : $(this).attr('page'),
target : $(this).attr('target')
}
cardArray.push(cobj);
});
});
This isn't a case you'd want to use recursion. Every click binds more events to the DOM but never releases them. I'd recommend pulling your $('#wrapper').on('click'... code out of your setupPage function that way your click events are only bound once.
Recursive functions are best used for situations like scanning list trees where you don't know how deep they go. FYI, Code Academy has section on recursion that I found helpful.