First, I'm sorry I don't know how to title the question better.
I'm making a Google Chrome extension whose purpose is to read table columns from a table on a page A. This happens in my inject.js:
var $tableRows = $("table tbody tr");
var offers = [];
var links = [];
$tableRows.each(function (index) {
offers.push({
code: $.trim($(this).find('td:nth-child(2)').text()),
city: $.trim($(this).find('td:nth-child(6)').text()),
state: $.trim($(this).find('td:nth-child(7)').text())
});
links.push($(this).find('td:first').find('a').attr('href'));
if (index == $campaignTableRows.length - 1) {
downloadPdfForOffers(offers, links);
}
});
The code above creates an array that holds information that's in the table for those offers and saves the links for every offer profile.
Then the function downloadPdfForOffers does the following:
var downloadPdfForOffers = function (offers, links) {
chrome.storage.local.set({"offers": offers}, function () {
chrome.runtime.sendMessage({
downloadUrlsReady: true,
data: {links: links}
});
});
};
It sets in the local storage the current information I have for those offers and send the links to background.js.
Then from background.js, I open those links in new tabs.
if (request.downloadUrlsReady) {
for (var i = 0; i < request.data.links.length; i++) {
chrome.tabs.create({url: request.data.links[i], active: true});
}
}
In my inject.js I'm waiting for the page to load the offer profile and with jQuery get the additional information I need and I send it to background.js again:
chrome.storage.local.get(["job_offers"], function (items) {
if (items.job_offers && items.job_offers.length > 0) {
var requirements = $("#j_id0\\:SiteTemplate\\:j_id747\\:j_id748").find('.margin-b3:first div.tcell:nth-child(2) div');
var data = {
file: {
link: $('.btn.btn-link.btn-lg.vs2.margin-r2').attr('href'),
name: $.trim($('#j_id0\\:SiteTemplate\\:j_id747\\:j_id767').text())
},
job_offer: {
position: requirements.text()
}
};
chrome.runtime.sendMessage({
pdfUrlReady: true,
data: data
});
}
});
Then in my background.js I update the already set offers and download the pdf:
if (request.pdfUrlReady) {
console.log(request.data);
chrome.storage.local.get(["job_offers"], function (items) {
items.job_offers.forEach(function (job_offer, index) {
if (job_offer.code == request.data.file.name) {
items.job_offers[index] = $.extend(job_offer, request.data.job_offer);
chrome.storage.local.set(items, function () {
chrome.downloads.download({
url: request.data.file.link,
saveAs: false,
filename: request.data.file.name + '.pdf'
}, function () {
chrome.tabs.remove(sender.tab.id);
});
});
}
});
});
}
The console.log there always has the full information I passed from the single page, however when I'm updating chrome.storage.local sometimes I get not complete information about the offer, probably because it's asynchronous.
How can I handle such situation?
Related
Im using django-el-pagination to do lazy loading of entries.
When I click on an entry and then use the browser back button, all of the lazy loading is gone, I tried to add window.history.pushState() but then I only get the current page i.e.?page=4 when I use the browser back button, and all of the entries on top is not loaded.
Is there any way to implement a correct history so that the user is back at the same place when they use the browser back button?
$.endlessPaginate({
paginateOnScroll: true,
paginateOnScrollMargin: 400,
paginateOnScrollChunkSize: 2,
onCompleted: function(context, fragment) {
window.history.pushState(null, null, context.url);
}
});
Edit 1
Here is the JavaScript for the .endlessPaginate function:
'use strict';
(function ($) {
// Fix JS String.trim() function is unavailable in IE<9 #45
if (typeof(String.prototype.trim) === "undefined") {
String.prototype.trim = function() {
return String(this).replace(/^\s+|\s+$/g, '');
};
}
$.fn.endlessPaginate = function(options) {
var defaults = {
// Twitter-style pagination container selector.
containerSelector: '.endless_container',
// Twitter-style pagination loading selector.
loadingSelector: '.endless_loading',
// Twitter-style pagination link selector.
moreSelector: 'a.endless_more',
// Digg-style pagination page template selector.
pageSelector: '.endless_page_template',
// Digg-style pagination link selector.
pagesSelector: 'a.endless_page_link',
// Callback called when the user clicks to get another page.
onClick: function() {},
// Callback called when the new page is correctly displayed.
onCompleted: function() {},
// Set this to true to use the paginate-on-scroll feature.
paginateOnScroll: false,
// If paginate-on-scroll is on, this margin will be used.
paginateOnScrollMargin : 1,
// If paginate-on-scroll is on, it is possible to define chunks.
paginateOnScrollChunkSize: 0
},
settings = $.extend(defaults, options);
var getContext = function(link) {
return {
key: link.attr('rel').split(' ')[0],
url: link.attr('href')
};
};
return this.each(function() {
var element = $(this),
loadedPages = 1;
// Twitter-style pagination.
element.on('click', settings.moreSelector, function() {
var link = $(this),
html_link = link.get(0),
container = link.closest(settings.containerSelector),
loading = container.find(settings.loadingSelector);
// Avoid multiple Ajax calls.
if (loading.is(':visible')) {
return false;
}
link.hide();
loading.show();
var context = getContext(link);
// Fire onClick callback.
if (settings.onClick.apply(html_link, [context]) !== false) {
var data = 'querystring_key=' + context.key;
// Send the Ajax request.
$.get(context.url, data, function(fragment) {
container.before(fragment);
container.remove();
// Increase the number of loaded pages.
loadedPages += 1;
// Fire onCompleted callback.
settings.onCompleted.apply(
html_link, [context, fragment.trim()]);
});
}
return false;
});
// On scroll pagination.
if (settings.paginateOnScroll) {
var win = $(window),
doc = $(document);
doc.scroll(function(){
if (doc.height() - win.height() -
win.scrollTop() <= settings.paginateOnScrollMargin) {
// Do not paginate on scroll if chunks are used and
// the current chunk is complete.
var chunckSize = settings.paginateOnScrollChunkSize;
if (!chunckSize || loadedPages % chunckSize) {
element.find(settings.moreSelector).click();
} else {
element.find(settings.moreSelector).addClass('endless_chunk_complete');
}
}
});
}
// Digg-style pagination.
element.on('click', settings.pagesSelector, function() {
var link = $(this),
html_link = link.get(0),
context = getContext(link);
// Fire onClick callback.
if (settings.onClick.apply(html_link, [context]) !== false) {
var page_template = link.closest(settings.pageSelector),
data = 'querystring_key=' + context.key;
// Send the Ajax request.
page_template.load(context.url, data, function(fragment) {
// Fire onCompleted callback.
settings.onCompleted.apply(
html_link, [context, fragment.trim()]);
});
}
return false;
});
});
};
$.endlessPaginate = function(options) {
return $('body').endlessPaginate(options);
};
})(jQuery);
short answer: no. The whole point of 'endless pagination' is to not reload a (new) page, therefore there is no history.
On my PHP page I use AJAX to fetch items for an auction, everything is working properly when something is chosen from the dropdown lists (as can be seen from picture 1 ). My problem is that when the page loads for the first time (see second picture ) than nothing happens while I just want the default values of the dropdown list to be loaded in Ajax just like the rest, how do I go around this issue? Note that in the first picture I first selected something else and then selected the default values again, the 2nd picture is the page when i open up my browser and do nothing else.
my code:
$(function() {
$("#filtercatselect").on("change", function() {
var categoryid = document.getElementById("filtercatselect").value;
var orderbyname = document.getElementById("filterorderbyselect").value;
if(categoryid == "")
{
categoryid = 0;
}
$.post('homefiltering.php', { catid: categoryid, sortname: orderbyname }, function(result) {
$('#item-container').html(result);
}
);
});
$("#filterorderbyselect").on("change", function() {
var categoryid = document.getElementById("filtercatselect").value;
var orderbyname = document.getElementById("filterorderbyselect").value;
if(categoryid == "")
{
categoryid = 0;
}
$.post('homefiltering.php', { catid: categoryid, sortname: orderbyname }, function(result) {
$('#item-container').html(result);
}
);
});
});
just place under your code this, to trigger the change event on first script execution
[--- your code above ---]
$("#filterorderbyselect").trigger('change');
// OR - based on what code you prefer to execute on first page execution
// place both you make two equal ajax calls- thanks to Don't Panic
$("#filtercatselect").trigger('change');
How about this? On load you call the functions that I have given names and then assign them for change-event to the dropdowns.
$(document).ready(function(){
// call these methods on load
filtercatselectChangeHandler();
filterorderbyselectChangeHandler();
// set on-change handlers
$("#filtercatselect").change(filtercatselectChangeHandler);
$("#filterorderbyselect").change(filterorderbyselectChangeHandler);
});
function filtercatselectChangeHandler(){
var categoryid = document.getElementById("filtercatselect").value;
var orderbyname = document.getElementById("filterorderbyselect").value;
if(categoryid == "")
{
categoryid = 0;
}
$.post('homefiltering.php', { catid: categoryid, sortname: orderbyname }, function(result) {
$('#item-container').html(result);
}
);
}
function filterorderbyselectChangeHandler(){
var categoryid = document.getElementById("filtercatselect").value;
var orderbyname = document.getElementById("filterorderbyselect").value;
if(categoryid == "")
{
categoryid = 0;
}
$.post('homefiltering.php', { catid: categoryid, sortname: orderbyname }, function(result) {
$('#item-container').html(result);
}
);
}
I use pjax to ajaxify my menu links. This works fine until I use the browser back button. In my javascript file I use Common Script files (to load all the necessary js files when the user hits the url) and Script files with respect to each menu links (when navigated through pjax)
function myFunction(){
/*All the script files */
}
$(document).ready(function(){
myFunction();
/*pjax menu loading block*/
$(document).on('click', 'a[data-pjax]', function(event) {
$.pjax.click(event, '#pjax-container');
$(document).on('pjax:end', function() {
myFunction();
});
});
});
Now when I navigate to a menu item and try to come back by clicking the browser back button, the script files are getting duplicated (eg: slider images getting duplicated and table sorting not working).How to overcome this issue?
You can implement the url specific loading this way, create a queue of functions which you want to load and unload on pjax complete
The solution is based on js prototyping
// create queue for load and unload
var onLoad = new PjaxExecQueue();
var onUnload = new PjaxExecQueue();
// way to add functions to queue to run on pjax load
onLoad.queue(function() {
someFunction();
});
// way to add functions to queue to unload on pjax load
onUnload.queue(function() {
someOtherFunction();
});
// load function if url contain particular path name
onLoad.queue_for_url(function_name, 'url_section');
// check for url specific function
var URLPjaxQueueElement = function(exec_function, url) {
this.method = exec_function;
if(url) {
this.url = new RegExp(url);
} else {
this.url = /.*/;
}
};
// create a queue object
var PjaxExecQueue = function () {
this.url_exec_queue = [];
this.id_exec_queue = [];
this.fired = false;
this.indicating_loading = false;
this.content = $('#content');
};
PjaxExecQueue.prototype = {
queue: function (exec_function) {
this.url_exec_queue.unshift(new URLPjaxQueueElement(exec_function));
},
queue_for_url: function (exec_function, url_pattern) {
this.url_exec_queue.unshift(new URLPjaxQueueElement(exec_function, url_pattern));
},
queue_if_id_present: function(exec_function, id) {
this.id_exec_queue.unshift(new IDPjaxQueueElement(exec_function, id));
},
fire: function () {
if(this.indicating_loading) {
this.content.removeClass("indicate-loading");
this.indicating_loading = false;
}
if(!this.fired) {
var match_loc = window.location.pathname;
var i = this.url_exec_queue.length;
while(i--) {
this.url_exec_queue[i].fire(match_loc);
}
i = this.id_exec_queue.length;
while(i--) {
this.id_exec_queue[i].fire(match_loc);
}
}
this.fired = true;
},
reset: function() {
this.fired = false;
},
loading: function () {
this.content.addClass("indicate-loading");
this.indicating_loading = true;
this.reset();
},
count: function () {
return exec_queue.length;
},
show: function (for_url) {
for (var i=0; i < exec_queue.length; i++) {
if(for_url) {
if(exec_queue[i].url.test(for_url)) {
console.log("" + exec_queue[i].method);
}
} else{
console.log(exec_queue[i].url + " : " + exec_queue[i].method);
}
}
}
};
// before send
$(document).on('pjax:beforeSend', function() {
onLoad.loading();
onUnload.fire();
});
// after pjax complete
$(document).on('pjax:complete', function() {
onLoad.fire();
onUnload.reset();
});
With alot of help from #kalley we have found out that If I comment the following two lines out the LAG is gone!
var $tableContents = $table.find('tbody')
var $html = $('<tbody/>').html(data);
But how do I keep the above but cancel out the LAG ?
MORE INFO:
The code below works but the problem is that the $.GET is causing the browser to hang until the ajax request completes. I need (flow control?) or something that will solve this problem without locking/hanging up the browser until ajax completes the GET request.
The biggest LAG/Lockup/Hang is at $.get("updatetable.php", since the others only return 7 or less (number) values and this one ('updatetable.php') returns alot more (200-300kb). I would like to implement some sort of flow control here or make the script wait like 5 secs before firing the update command for tablesort and before showing the toast message so that ajax has time to GET the $.get("updatetable.php"data I just don't understand why does it lockup the browser as it is getting the data? is it trying to fire the other commands and that's whats causing the LAG?
Here are the STEPS
1.
$.get("getlastupdate.php" Will fire every 10 secs or so to check if the date and time are the same the return data looks like this: 20130812092636 the format is: YYYmmddHHmmss.
2.
if the date and time are not the same as the last GET then $.get("getlastupdate2.php" will trigger and this data will be send back and placed into a toast message and dispalyed to the user $().toastmessage('showNoticeToast', Vinfoo);
3.
before or after the above ($.get("getlastupdate2.php") another GET will fire: $.get('updatetable.php' this will GET the updated table info. and replace the old one with the new info. and then update/resort the table
4.
at the end of it all I want to $.get("ajaxcontrol.php" and this will return a 1 or 2 if the user is logged in then it will be a 2 else it's a 1 and it will destroy the session and log the user out.
<script type="text/javascript" src="tablesorter/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="tablesorter/final/jquery.tablesorter.js"></script>
<script type="text/javascript" src="tablesorter/final/jquery.tablesorter.widgets.js"></script>
<script type="text/javascript" src="tablesorter/final/toastmessage/jquery.toastmessage-min.js"></script>
<script type="text/javascript" src="tablesorter/qtip/jquery.qtip.min.js"></script>
<script type="text/javascript">
var comper;
function checkSession() {
return $.get("ajaxcontrol.php", function (DblIn) {
console.log('checking for session');
if (DblIn == 1) {
window.location = 'loggedout.php';
}
}).then(updateTable);
}
function checkComper() {
var SvInfo;
var onResponse = function (comperNow) {
if (comper === undefined) {
comper = comperNow;
} else if (comper !== comperNow) {
var Vinfoo;
comper = comperNow;
// returning this $.get will make delay done until this is done.
return $.get("getlastupdate2.php", function (primaryAddType) {
Vinfoo = primaryAddType;
$().toastmessage('showNoticeToast', Vinfoo);
}).then(checkSession);
}
};
$.get('getlastupdate.php').then(onResponse).done(function () {
tid = setTimeout(checkComper, 2000);
});
}
function updateTable() {
return $.get('updatetable.php', function (data) {
console.log('update table');
var $table = $("table.tablesorter");
var $tableContents = $table.find('tbody')
var $html = $('<tbody/>').html(data);
$tableContents.replaceWith('<tbody>' + data + '</tbody>')
//$tableContents.replaceWith($html)
$table.trigger("update", [true]);
var currentUrl = document.getElementById("frmcontent").contentWindow.location.href;
var urls = ['indexTOM.php', 'index1.php'],
frame = document.getElementById('frmcontent').contentDocument;
for (var i = 0; i < urls.length; i++) {
var url = urls[i];
if (frame.location.href.indexOf(url) !== -1) {
frame.location.reload()
}
}
$('[title!=""]').qtip({});
});
};
$(function () {
var tid = setTimeout(checkComper, 2000);
$("#append").click(function (e) {
// We will assume this is a user action
e.preventDefault();
updateTable();
});
// call the tablesorter plugin
$("table.tablesorter").tablesorter({
theme: 'blue',
// hidden filter input/selects will resize the columns, so try to minimize the change
widthFixed: true,
// initialize zebra striping and filter widgets
widgets: ["saveSort", "zebra", "filter"],
headers: {
8: {
sorter: false,
filter: false
}
},
widgetOptions: {
filter_childRows: false,
filter_columnFilters: true,
filter_cssFilter: 'tablesorter-filter',
filter_filteredRow: 'filtered',
filter_formatter: null,
filter_functions: null,
filter_hideFilters: false, // true, (see note in the options section above)
filter_ignoreCase: true,
filter_liveSearch: true,
filter_reset: 'button.reset',
filter_searchDelay: 300,
filter_serversideFiltering: false,
filter_startsWith: false,
filter_useParsedData: false
}
});
// External search
$('button.search').click(function () {
var filters = [],
col = $(this).data('filter-column'), // zero-based index
txt = $(this).data('filter-text'); // text to add to filter
filters[col] = txt;
$.tablesorter.setFilters($('table.hasFilters'), filters, true); // new v2.9
return false;
});
});
</script>
Maybe instead of using setInterval, you should consider switching to setTimeout. It will give you more control over when the time repeats:
function checkComper() {
var SvInfo;
var onResponse = function (comperNow) {
if (comper === undefined) {
comper = comperNow;
} else if (comper !== comperNow) {
var Vinfoo;
comper = comperNow;
// returning this $.get will make delay done until this is done.
return $.get("getlastupdate2.php", function (primaryAddType) {
Vinfoo = primaryAddType;
$().toastmessage('showNoticeToast', Vinfoo);
}).then(checkSession);
}
};
$.get('getlastupdate.php').then(onResponse).done(function () {
tid = setTimeout(checkComper, 10000);
});
}
var tid = setTimeout(checkComper, 10000);
Then you can keep it async: true
Here's a fiddle showing it working using echo.jsontest.com and some fudging numbers.
Since the click event callback seems to be where the issue is, try doing this and see if it removes the lag (I removed other comments to make it more brief):
function checkSession() {
return $.get("ajaxcontrol.php", function (DblIn) {
console.log('checking for session');
if (DblIn == 1) {
window.location = 'loggedout.php';
}
}).then(updateTable);
}
function updateTable() {
return $.get('updatetable.php', function (data) {
console.log('update table');
var $tableContents = $table.find('tbody')
//var $html = $('<tbody/>').html(data);
//$tableContents.replaceWith($html);
// replaceWith text seems to be much faster:
// http://jsperf.com/jquery-html-vs-replacewith/4
$tableContents.replaceWith('<tbody'> + data + '</tbody>');
//$table.trigger("update", [true]);
var currentUrl = document.getElementById("frmcontent").contentWindow.location.href;
var urls = ['indexTOM.php', 'index1.php'],
frame = document.getElementById('frmcontent').contentDocument;
for (var i = 0; i < urls.length; i++) {
var url = urls[i];
if (frame.location.href.indexOf(url) !== -1) {
frame.location.reload()
}
}
$('[title!=""]').qtip({});
});
};
$("#append").click(function (e) {
// We will assume this is a user action
e.preventDefault();
updateTable();
});
I commented out $table.trigger("update", [true]) since if you sort the table on the server before you return it, you shouldn't need to run that, which I'm almost certain is where the bottleneck is.
It is really hard untangle the mess you have but if what you want is ajax requests every 10 seconds it make sense to separate this logic from business logic over data from server.
Your code would also really benefit from using promises. Consider this example
$(document).ready(function() {
var myData = { }
, ajaxPromise = null
setInterval(callServer, 1000)
function callServer() {
ajaxPromise = updateCall()
.then(controlCall)
.done(handler)
.error(errorHandler)
}
function updateCall() {
return $.get('updateTable.php', function(data) {
myData.update = data
})
}
function controlCall( ) {
return $.get('ajaxControl.php', function(data) {
myData.control = data
})
}
function handler() {
console.dir(myData)
}
function errorHandler(err) {
console.log(err)
console.dir(myData)
}
})
I have three URLs that return different JSON responses (say user mobiles, addresses and emails) being populated from different beans.
url='/mobile.do?username=x&password=y'
url='/email.do?username=x&password=y'
url='/address.do?username=x&password=y'
For the following autocomplete plugin (fcbkcomplete):
<script type="text/javascript">
$(document).ready(function(){
$("#mySelect").fcbkcomplete({
json_url: "?!!",
});
});
</script>
Now I want to use these URLs to populate and add data to a single field rather than three different fields. Hence, somehow I need to mix these URL or something like this.
I was wondering what is the best way for this? Can we set more than one URLs or something?
You could modify the plugin, by changing the function load_feed. This isn't tested, so might need some tweeking.
function load_feed(etext) {
counter = 0;
if (options.json_url_list && maxItems()) {
if (options.cache && json_cache_object.get(etext)) {
addMembers(etext);
bindEvents();
} else {
getBoxTimeout++;
var getBoxTimeoutValue = getBoxTimeout;
setTimeout(function () {
if (getBoxTimeoutValue != getBoxTimeout) return;
var count = 0;
var all_data = [];
var finished = function () {
if (!isactive) return; // prevents opening the selection again after the focus is already off
json_cache_object.set(etext, 1);
bindEvents();
};
for (var i = 0; i < options.json_url_list.length; i++) {
$.getJSON(options.json_url_list[i], {
"tag": xssDisplay(etext)
}, function (data) {
addMembers(etext, data);
count += 1;
if (count === options.json_url_list.length) finished();
});
}
}, options.delay);
}
} else {
addMembers(etext);
bindEvents();
}
}