Create jquery plugin : run only once per selector - javascript

I'm trying to create a plugin suited for my needs which adds some functions to tables. There are of course thousands of plugins like dataTables.js etc. but a lightweight solution is more suitable for what I'm trying to do.
Problem(s): If there are 2 tables or more, the code runs multiple times on each table and of course if you filter one table, paginate one table and change the page of each... it applies to all.
Any help would be appreciated and if you have improvement ideas they're welcome ! Once I'm done I will publish this on github if anyone is interested in a simple version of datatables... !
https://jsfiddle.net/ygery/gm02mvpk/
function tableOptions(selector, paginate, npages, filter, responsive) {
var $table = $(selector);
var currentPage = 0;
function tablePaginate() {
$table.bind('paginate', {
pages: npages
}, function(event) {
$('.pagination').remove();
// if(npages != )
//alert(event.data.pages);
var $row = $table.find('tbody tr:not(.hidden)').addClass('row-pagination');
$row.hide()
$row.slice(currentPage * npages, (currentPage + 1) * npages).show();
var numRows = $table.find('.row-pagination').length;
var numPages = Math.ceil(numRows / npages);
var $pager = $('<ul class="pagination"></ul>');
$('<li class="table-prev"></li>').html('<').bind('click', {
prevPage: currentPage - 1
}, function(event) {
if (currentPage != 0) {
currentPage = event.data['prevPage'];
$table.trigger('paginate');
$(this).addClass('active').siblings().removeClass('active');
$(this).hide();
}
}).appendTo($pager).addClass('clickable');
for (var page = 0; page < numPages; page++) {
$('<li class="table-page" data-page=' + parseInt(page + 1) + '></li>').html('' + parseInt(page + 1) + '').bind('click', {
newPage: page
}, function(event) {
currentPage = event.data['newPage'];
//$table.trigger('paginate');
$(this).addClass('active').siblings().removeClass('active');
}).appendTo($pager).addClass('clickable');
}
$('<li class="table-next"></li>').html('>').bind('click', {
nextPage: currentPage + 1
}, function(event) {
if (currentPage != numPages - 1) {
currentPage = event.data['nextPage'];
//$table.trigger('paginate');
$(this).addClass('active').siblings().removeClass('active');
}
}).appendTo($pager).addClass('clickable');
$pager.insertAfter($table);
if (currentPage == 0) {
$('.table-prev').addClass('disabled').attr('onclick', 'event.preventDefault();');
}
if (currentPage == numPages - 1) {
$('.table-next').addClass('disabled').attr('onclick', 'event.preventDefault();');
}
$('li[data-page="' + parseInt(currentPage + 1) + '"]').addClass('active');
if (numPages > 5) {
var $this = parseInt(currentPage + 1);
$('.table-page').hide();
if (currentPage == 0) {
currentPage = currentPage + 1;
}
for (var page = currentPage; page < currentPage + 2; page++) {
$('.table-page[data-page="' + parseInt(page + 1) + '"]').show();
if (page == currentPage + 1) {
var dots = '<li class="disabled" onclick="event.preventDefault();">...</li>';
$('.table-page[data-page="' + parseInt(page + 1) + '"]').after(dots);
}
}
for (var page = currentPage; page > currentPage - 1; page--) {
$('.table-page[data-page="' + parseInt(page) + '"]').show();
//if(page == currentPage+1) {
// var dots = '<li class="disabled" onclick="event.preventDefault();">...</li>';
// $('.table-page[data-page="'+parseInt(page - 1)+'"]').after(dots);
//}
}
}
});
$table.trigger('paginate');
}
function tableFilter() {
$(".table-search").keyup(function() {
$('li[data-page="1"]').click();
var input = $(this);
var $row = $table.find('tbody tr');
var $cell = $row.find('td');
if (input.val() != '') {
$row.addClass('hidden').removeClass('row-pagination');
$($cell).each(function(i, obj) {
if ($(obj).find('input').length) {
var $content = $(obj).find('input').val().toLowerCase().indexOf(input.val().toLowerCase());
} else {
if ($(obj).find('select').length) {
var $content = $(obj).find('select option:selected').text().toLowerCase().indexOf(input.val().toLowerCase());
} else {
if ($(obj).text().length) {
var $content = $(obj).text().toLowerCase().indexOf(input.val().toLowerCase());
} else {
var $content = $(obj).html().toLowerCase().indexOf(input.val().toLowerCase());
}
}
}
if ($content > -1 && input.val()) {
$(obj).parent().removeClass('hidden');
$(obj).parent().addClass('row-pagination');
}
});
$table.trigger('paginate');
} else {
$table.find('tr').removeClass('hidden').addClass('row-pagination');
$table.trigger('paginate');
}
});
}
function tableResponsive() {
$(window).resize(function() {
if ($(window).width() < 767) {
if (!$('.responsive-toggle').length) {
$table.find('thead tr th:first-child').before('<td class="responsive-toggle"><i class="glyphicon glyphicon-phone"></i></td>');
$table.find('tbody tr td:first-child').before('<td class="responsive-toggle"><i class="glyphicon glyphicon-menu-down"></i></td>');
}
var max = $table.width();
//alert(max);
} else {
$('.responsive-toggle').remove();
}
}).resize();
}
$table.each(function() {
if (paginate || filter) {
var $header = '<div class="col-sm-2 table-pagination-select"></div><div class="col-sm-6 table-options"></div><div class="col-sm-4 table-search-input"></div><hr />';
$table.before($header);
$table.wrap('<div class="col-md-12"></div>');
}
if (paginate) {
var $target = $('.table-pagination-select');
var $select = '<select name="" id="" class="form-control table-select"><option value="10">10</option><option value="25">25</option><option value="50">50</option><option value="100">100</option><option value="all">Tous</option></select>';
$($select).appendTo($target);
//$('.table-select').select2();
tablePaginate();
}
if (filter) {
var $target = $('.table-search-input');
var $searcher = '<input type="text" class="form-control table-search" placeholder="Rechercher..." />';
$($searcher).appendTo($target);
tableFilter();
}
if (responsive) {
tableResponsive();
}
})
}
tableOptions('.table', true, 10, true, true);

You can set data on jQuery object:
function tableOptions(selector, paginate, npages, filter, responsive) {
var $table = $(selector);
if ($table.data('table')) {
return;
} else {
$table.data('table', true);
}

Related

Paginate google book search results

I ve a problem with the pagination of the search results in Javascript, my code show me in the dom the all results for a determinate word but i need to paginate these in different pages in 5 elements by page. when i try to call the element totalItems it gives me the error the element is not defined. can someone help me please?
function getBooks(search) {
bookSearch(search, start);
}
function bookSearch(search, start){
var search = document.getElementById('search').value
document.getElementById('results').innerHTML = ""
$.ajax({
url:"https://www.googleapis.com/books/v1/volumes?q=" + search + "&maxResults=40&start=" + start,
dataType: "json",
success: function(data){
totalItems = data.items;
if(data.items){
for(i = 0; i < data.items.length; i++){
if(data.items[i].volumeInfo){
var title = data.items[i].volumeInfo.title
var subtitle = data.items[i].volumeInfo.subtitle
var img = data.items[i].volumeInfo.imageLinks.thumbnail
var date = data.items[i].volumeInfo.publishedDate
var info = data.items[i].volumeInfo.infoLink
results.innerHTML +=
'<div class="card my-3" style="max-width: 540px;">'+
'<div class="row no-gutters">'+
'<div class="col-md-4">'+
'<img src="'+img+'" class="card-img-top img-card" alt="...">'+
'</div>'+
'<div class="col-md-8">'+
'<div class="card-body">'+
'<h5 class="card-title">'+title+'</h5>'+
'<p class="card-text subtitle">'+ subtitle +'</p>'+
'<p class="card-text"><small class="text-muted">'+ date +'</small></p>'+
'Book Info'+
'</div>'+
'</div>'+
'</div>'+
'</div>'
}
numberOfPages = getNumberOfPages();
}
if(totalItems > 40){
start+=40;
bookSearch("", start);
}
}
},
type: 'GET'
});
}
document.getElementById('button').addEventListener('click', bookSearch, false)
function load() {
bookSearch();
loadTotalItems();
}
var list = totalItems;
var numberPerPage = 5;
var pageList = new Array();
var currentPage = 1;
var numberOfPages = 0;
function getNumberOfPages(){
return Math.ceil(list.length / numberPerPage)
}
function nextPage(){
currentPage +=1;
loadTotalItems();
}
function previousPage() {
currentPage -= 1;
loadTotalItems();
}
function firstPage() {
currentPage = 1;
loadTotalItems();
}
function lastPage() {
currentPage = numberOfPages;
loadTotalItems();
}
function loadTotalItems() {
var begin = ((currentPage - 1) * numberPerPage);
var end = begin + numberPerPage;
pageList = list.slice(begin, end);
bookSearch();
check();
}
function check() {
document.getElementById("next").disabled = currentPage == numberOfPages ? true : false;
document.getElementById("previous").disabled = currentPage == 1 ? true : false;
document.getElementById("first").disabled = currentPage == 1 ? true : false;
document.getElementById("last").disabled = currentPage == numberOfPages ? true : false;
}
You need to declare the variable outside of the function to make it have a global scope:
var totalItems;
function bookSearch(search, start){
$.ajax({
success: function(data) {
totalItems = data.items;
}
})
}

'DOMException: Failed to execute 'querySelectorAll' on 'Element' when using an 'option:selected' selector

I'm running a page which throws an error at the following line:
var label = $select.find('option:selected').html() || $select.find('option:first').html() || "";
For the sake of completeness, here is the full jQuery function (country-select.js):
(function($) {
$.fn.countrySelect = function (callback) {
$(this).each(function(){
var $select = $(this);
var lastID = $select.data('select-id'); // Tear down structure if Select needs to be rebuilt
if (lastID) {
$select.parent().find('span.caret').remove();
$select.parent().find('input').remove();
$select.unwrap();
$('ul#select-options-'+lastID).remove();
}
// If destroying the select, remove the selelct-id and reset it to it's uninitialized state.
if(callback === 'destroy') {
$select.data('select-id', null).removeClass('initialized');
return;
}
var uniqueID = Materialize.guid();
$select.data('select-id', uniqueID);
var wrapper = $('<div class="select-wrapper"></div>');
wrapper.addClass($select.attr('class'));
var options = $('<ul id="select-options-' + uniqueID +'" class="dropdown-content select-dropdown country-select"></ul>'),
selectChildren = $select.children('option, optgroup'),
valuesSelected = [],
optionsHover = false;
var label = $select.find('option:selected').html() || $select.find('option:first').html() || "";
// Function that renders and appends the option taking into
// account type and possible image icon.
var appendOptionWithIcon = function(select, option, type) {
// Add disabled attr if disabled
var disabledClass = (option.is(':disabled')) ? 'disabled ' : '';
var optgroupClass = (type === 'optgroup-option') ? 'optgroup-option ' : '';
var classes = option.attr('class');
var data = option.data('phone-code');
var opt = '<li class="' + disabledClass + optgroupClass + '"><span>';
if (option.val() !== '') {
opt += '<b class="flag flag-' + option.val().toLowerCase() + '"></b>';
}
opt += '<span class="option-label">' + option.html() + '</span>';
if (data && data !== '') {
opt += '<small>' + data + '</small>';
}
opt += '</span></li>';
options.append($(opt));
};
/* Create dropdown structure. */
if (selectChildren.length) {
selectChildren.each(function() {
if ($(this).is('option')) {
appendOptionWithIcon($select, $(this));
} else if ($(this).is('optgroup')) {
// Optgroup.
var selectOptions = $(this).children('option');
options.append($('<li class="optgroup"><span>' + $(this).attr('label') + '</span></li>'));
selectOptions.each(function() {
appendOptionWithIcon($select, $(this), 'optgroup-option');
});
}
});
}
options.find('li:not(.optgroup)').each(function (i) {
$(this).click(function (e) {
// Check if option element is disabled
if (!$(this).hasClass('disabled') && !$(this).hasClass('optgroup')) {
var selected = true;
options.find('li').removeClass('active');
$(this).toggleClass('active');
$newSelect.val($(this).find('.option-label').text());
activateOption(options, $(this));
$select.find('option').eq(i).prop('selected', selected);
// Trigger onchange() event
$select.trigger('change');
if (typeof callback !== 'undefined') callback();
}
e.stopPropagation();
});
});
// Wrap Elements
$select.wrap(wrapper);
// Add Select Display Element
var dropdownIcon = $('<span class="caret">▼</span>');
if ($select.is(':disabled'))
dropdownIcon.addClass('disabled');
// escape double quotes
var sanitizedLabelHtml = label.replace(/"/g, '"');
var $newSelect = $('<input type="text" class="select-dropdown" readonly="true" ' + (($select.is(':disabled')) ? 'disabled' : '') + ' data-activates="select-options-' + uniqueID +'" value="'+ sanitizedLabelHtml +'"/>');
$select.before($newSelect);
$newSelect.before(dropdownIcon);
$newSelect.after(options);
// Check if section element is disabled
if (!$select.is(':disabled')) {
$newSelect.data('constrainwidth', false)
$newSelect.dropdown({'hover': false, 'closeOnClick': false});
}
// Copy tabindex
if ($select.attr('tabindex')) {
$($newSelect[0]).attr('tabindex', $select.attr('tabindex'));
}
$select.addClass('initialized');
$newSelect.on({
'focus': function (){
if ($('ul.select-dropdown').not(options[0]).is(':visible')) {
$('input.select-dropdown').trigger('close');
}
if (!options.is(':visible')) {
$(this).trigger('open', ['focus']);
var label = $(this).val();
var selectedOption = options.find('li').filter(function() {
return $(this).find('.option-label').text().toLowerCase() === label.toLowerCase();
})[0];
activateOption(options, selectedOption);
}
},
'click': function (e){
e.stopPropagation();
}
});
$newSelect.on('blur', function() {
$(this).trigger('close');
options.find('li.selected').removeClass('selected');
});
options.hover(function() {
optionsHover = true;
}, function () {
optionsHover = false;
});
// Make option as selected and scroll to selected position
var activateOption = function(collection, newOption) {
if (newOption) {
collection.find('li.selected').removeClass('selected');
var option = $(newOption);
option.addClass('selected');
options.scrollTo(option);
}
};
// Allow user to search by typing
// this array is cleared after 1 second
var filterQuery = [],
onKeyDown = function(e){
// TAB - switch to another input
if(e.which == 9){
$newSelect.trigger('close');
return;
}
// ARROW DOWN WHEN SELECT IS CLOSED - open select options
if(e.which == 40 && !options.is(':visible')){
$newSelect.trigger('open');
return;
}
// ENTER WHEN SELECT IS CLOSED - submit form
if(e.which == 13 && !options.is(':visible')){
return;
}
e.preventDefault();
// CASE WHEN USER TYPE LETTERS
var letter = String.fromCharCode(e.which).toLowerCase(),
nonLetters = [9,13,27,38,40];
if (letter && (nonLetters.indexOf(e.which) === -1)) {
filterQuery.push(letter);
var string = filterQuery.join(''),
newOption = options.find('li').filter(function() {
return $(this).text().toLowerCase().indexOf(string) === 0;
})[0];
if (newOption) {
activateOption(options, newOption);
}
}
// ENTER - select option and close when select options are opened
if (e.which == 13) {
var activeOption = options.find('li.selected:not(.disabled)')[0];
if(activeOption){
$(activeOption).trigger('click');
$newSelect.trigger('close');
}
}
// ARROW DOWN - move to next not disabled option
if (e.which == 40) {
if (options.find('li.selected').length) {
newOption = options.find('li.selected').next('li:not(.disabled)')[0];
} else {
newOption = options.find('li:not(.disabled)')[0];
}
activateOption(options, newOption);
}
// ESC - close options
if (e.which == 27) {
$newSelect.trigger('close');
}
// ARROW UP - move to previous not disabled option
if (e.which == 38) {
newOption = options.find('li.selected').prev('li:not(.disabled)')[0];
if(newOption)
activateOption(options, newOption);
}
// Automaticaly clean filter query so user can search again by starting letters
setTimeout(function(){ filterQuery = []; }, 1000);
};
$newSelect.on('keydown', onKeyDown);
});
function toggleEntryFromArray(entriesArray, entryIndex, select) {
var index = entriesArray.indexOf(entryIndex),
notAdded = index === -1;
if (notAdded) {
entriesArray.push(entryIndex);
} else {
entriesArray.splice(index, 1);
}
select.siblings('ul.dropdown-content').find('li').eq(entryIndex).toggleClass('active');
// use notAdded instead of true (to detect if the option is selected or not)
select.find('option').eq(entryIndex).prop('selected', notAdded);
setValueToInput(entriesArray, select);
return notAdded;
}
function setValueToInput(entriesArray, select) {
var value = '';
for (var i = 0, count = entriesArray.length; i < count; i++) {
var text = select.find('option').eq(entriesArray[i]).text();
i === 0 ? value += text : value += ', ' + text;
}
if (value === '') {
value = select.find('option:disabled').eq(0).text();
}
select.siblings('input.select-dropdown').val(value);
}
};
$(function() {
$('.js-country-select').countrySelect();
});
$(document).on('change', '[data-country-select]', function() {
var target = 'select' + $(this).data('country-select');
var val = $(this).val();
var label = 'State';
var options = [
'<option value="" selected="" disabled="">Select State</option>'
];
if (val !== '') {
var country = window.__COUNTRIES[val];
var subdivisions = country.subdivisions;
var keys = Object.keys(subdivisions);
label = country.subdivisionName;
options = [
'<option value="" selected="" disabled="">Select ' + label + '</option>'
];
keys = keys.sort(function(a, b) {
var valA = subdivisions[a].toLowerCase();
var valB = subdivisions[b].toLowerCase();
if (valA < valB) return -1;
if (valA > valB) return 1;
return 0;
});
keys.forEach(function(key) {
options.push('<option value="' + key + '">' + subdivisions[key] + '</option>');
});
$(target).removeAttr('disabled');
} else {
$(target).attr('disabled', 'disabled');
}
$(target).parents('.input-field').find('label').html(label);
$(target).val('').html(options);
$(target).select2();
});
})(jQuery);
Here is the exception that I see in debug mode:
From what I understand from Failed to execute 'querySelectorAll' on 'Element' in ExtJS 5, :selected is not part of the CSS specification.
How should I fix this? Should I use:
var label = $select.find('option').filter(':selected').html() || $select.find('option').filter(':first').html() || "";
?
Converting Phil's comment to an answer, my debugger was set to pause on all exceptions (including caught ones). I had to de-activate the 'stop sign' button shown below to make the debugger work normally again.

CasperJS - Memory Exhausted

When I run this through command line, it goes for an hour or two, and then command line spits out "Memory Exhausted". I can't figure out what's going on.
Also, some general advice about how to make this more readable or modifiable, since I'll be passing the project off in a month.
var fs = require('fs');
var currentPhysician = [];
var physicianData = [];
var permitMax = 99999;
var alreadyParsed = [];
var targetFile = "CMQphysicians.csv";
var startTime = new Date().getTime();
var permitNumber = -1;
var firstLicense = 0;
var utils = require('utils');
String.prototype.contains = function (s) {
return (this.indexOf(s) != -1);
}
var casper = require('casper').create({
verbose : true,
logLevel : "info",
pageSettings : {
loadImages : false, // do not load images
loadPlugins : false // do not load NPAPI plugins (Flash, Silverlight, ...)
}
});
function getPermitNumberString() {
var pn = permitNumber.toString();
var l = pn.length;
var i;
var leadingZeros = '';
for (i = 0; i < (5 - pn.length); i++) {
leadingZeros = leadingZeros + '0';
}
return leadingZeros + pn;
}
function getDetailsData() {
var details = document.querySelectorAll('#content-html > table.griddetails > tbody > tr > td');
return Array.prototype.map.call(details, function (e) {
return e.innerText;
});
}
function getPhysicianCount() {
return document.querySelectorAll("#GViewList > tbody > tr:nth-child(2) > td:nth-child(1) > a").length;
}
casper.on("resource.error", function (resourceError) {
if (!resourceError.url.contains('google')) {
this.echo("Resource error: " + "Error code: " + resourceError.errorCode + " ErrorString: " + resourceError.errorString + " url: " + resourceError.url + " id: " + resourceError.id, "ERROR");
}
while (resourceError.errorString.contains('undefined')) {}
});
casper.on('load.started', function () {
//casper.echo('load started');
});
casper.on('navigation.requested', function (url, navigationType, navigationLocked, isMainFrame) {
//casper.echo('navigation requested');
//casper.echo(navigationType);
});
casper.on('remote.message', function (msg) {
this.echo('from within remote page DOM' + msg);
});
casper.start('https://www.google.ca/?gws_rd=ssl', function () { // Loads the initial page.
casper.echo('Starting!');
});
casper.on('load.finished', function (status) {
//casper.echo('load finished');
var date = new Date();
var hours = date.getHours();
var minutes = date.getMinutes();
//casper.echo(hours.toString() + ':' + minutes.toString() + ' ' + this.getCurrentUrl().toUpperCase());
var urlPrefix = this.getCurrentUrl().substring(0, this.getCurrentUrl().indexOf('.aspx'));
if (urlPrefix.length == 0) {
casper.echo('undefined');
urlPrefix = 'https://www.google.ca/?gws_rd=ssl'.toUpperCase();
}
switch (urlPrefix.toUpperCase()) {
case 'https://www.google.ca/?gws_rd=ssl'.toUpperCase():
casper.echo('on google');
if (fs.exists('CMQphysicians.csv')) {
stream = fs.open('CMQphysicians.csv', 'r');
line = stream.readLine();
var i = 0;
while (line) {
if (i > 0) {
alreadyParsed.push(Number(line.substring(0, line.indexOf(','))));
}
line = stream.readLine();
i++;
}
stream.close();
permitNumber = Math.max.apply(null, alreadyParsed) + 1;
firstLicense = permitNumber;
casper.echo(permitNumber);
} else {
fs.write(targetFile, "\uFEFF" + 'Permit Number,Last Name,First Name,Gender,Permit,Status,Specialty,Activity,Authorization,Address,Phone\n', 'a');
}
casper.thenOpen('http://www.cmq.org/bottin/index.aspx?lang=en&a=1');
break;
case 'http://www.cmq.org/bottin/index'.toUpperCase():
casper.waitForSelector('#___gcse_0 > div > form > table.gsc-search-box > tbody > tr > td.gsc-search-button > input', function() {
var finishedSoFar = permitNumber - firstLicense;
var timeSoFar = new Date().getTime() - startTime;
var licensesToDo = permitMax - permitNumber;
var msPerLicense = timeSoFar / finishedSoFar;
var minutesToGo = (licensesToDo * msPerLicense) / 1000 / 60;
//casper.echo(licensesToDo + ' licenses to go. ' + msPerLicense.toString() + 'ms per license. ' + minutesToGo.toString() + ' minutes remaining.');
casper.echo('index stage');
permitNumber++;
if (permitNumber > permitMax) {
casper.echo('Permit number maxed out');
} else {
var permitNumberString = getPermitNumberString();
casper.echo('going to list');
casper.sendKeys('#txbNoPermis', permitNumberString);
//casper.wait(100);
casper.echo('sent keys, now clicking');
casper.thenClick('#btSubmit');
casper.echo('after the click');
}
});
break;
case 'http://www.cmq.org/bottin/list'.toUpperCase():
casper.waitForSelector('#___gcse_0 > div > form > table.gsc-search-box > tbody > tr > td.gsc-search-button > input', function() {
casper.echo('list stage');
// Three cases:
// No results, one result, many results
// No results: go back (00000)
// One result: go forward (82365)
// Many results: crash (?????)
a = casper.evaluate(getPhysicianCount);
if (a == 0) {
casper.echo('No physicians for license ' + getPermitNumberString());
casper.echo('going to index');
casper.thenClick('#btSubmit');
//casper.wait(1000);
} else if (a == 1) {
casper.echo('Physician exists for license ' + getPermitNumberString());
casper.echo('going to details');
casper.thenClick('#GViewList > tbody > tr:nth-child(2) > td:nth-child(1) > a');
//casper.wait(1000);
} else if (a > 1) {
casper.echo('a > 1 at ') + getPermitNumberString();
while(true){}
} else {
casper.echo('negative a at ') + getPermitNumberString();
while(true){}
}
// No results
});
break;
case 'http://www.cmq.org/bottin/details'.toUpperCase():
casper.waitForSelector('#___gcse_0 > div > form > table.gsc-search-box > tbody > tr > td.gsc-search-button > input', function() {
casper.echo('details stage');
var name = casper.getHTML('#content-html > table.griddetails > tbody > tr:nth-child(1) > th').substring(0, casper.getHTML('#content-html > table.griddetails > tbody > tr:nth-child(1) > th').indexOf('(')).trim().split(',');
tableData = (casper.evaluate(getDetailsData));
currentPhysician.push(tableData[4]);
currentPhysician.push(name[0].trim());
currentPhysician.push(name[1].trim());
for (i = 2; i < tableData.length; i++) {
if (i % 2 == 0 && i != 4) {
currentPhysician.push(tableData[i]);
}
}
for (i = 0; i < currentPhysician.length; i++) {
currentPhysician[i] = currentPhysician[i].replace(/,/g, ';').replace(/\n/g, ';');
}
var physicianString = currentPhysician.join(',') + '\n';
casper.echo('writing to file!');
fs.write(targetFile, physicianString, 'a');
currentPhysician = [];
casper.echo(casper.exists('#btNewsearch'));
casper.echo('going to index');
casper.thenClick('#btNewsearch');
//casper.wait(1000);
});
break;
default:
casper.echo("Wrong URL!");
casper.back();
break;
}});
casper.run(function () {
casper.echo('ending!');
casper.echo(physicianData.length);
});
Due to a bug:
https://bugs.webkit.org/show_bug.cgi?id=154452
Resolved by turning off image loading.
EDIT: seems to still be a problem. My guess is that it's because casperjs is outdated, so I'm abandoning it and using python.

Auto refresh a div containing Twitter posts

I am working with Twitter APIv1.1 and currently I am trying to implement a box which will display my latest tweets. This can be seen here:
http://www.jdiadt.com/twitterv1_1feed/testindex.html
However I would like to make this so that when I tweet, the box is automatically updated. I am quite new to JQuery and Javascript so I would appreciate any advice on how I can do this. I've hear AJAX can be used for something like this. Currently I have to refresh the entire page to display any new tweets. I'd like to only refresh the box.
Here is my script: twitterfeed.js
$(document).ready(function () {
var displaylimit = 10;
var twitterprofile = "jackcoldrick";
var screenname = "Jack Coldrick";
var showdirecttweets = false;
var showretweets = true;
var showtweetlinks = true;
var showprofilepic = true;
var headerHTML = '';
var loadingHTML = '';
headerHTML += '<a href="https://twitter.com/" ><img src="http://www.jdiadt.com/twitterv1_1feed/twitteroauth/images/birdlight.png" width="34" style="float:left;padding:3px 12px 0px 6px" alt="twitter bird" /></a>';
headerHTML += '<h1>'+screenname+' <span style="font-size:13px"><a href="https://twitter.com/'+twitterprofile+'" >#'+twitterprofile+'</a></span></h1>';
loadingHTML += '<div id="loading-container"><img src="http://www.jdiadt.com/twitterv1_1feed/twitteroauth/images/ajax-loader.gif" width="32" height="32" alt="tweet loader" /></div>';
$('#twitter-feed').html(headerHTML + loadingHTML);
$.getJSON('http://www.jdiadt.com/twitterv1_1feed/get_tweets.php',
function(feeds) {
//alert(feeds);
var feedHTML = '';
var displayCounter = 1;
for (var i=0; i<feeds.length; i++) {
var tweetscreenname = feeds[i].user.name;
var tweetusername = feeds[i].user.screen_name;
var profileimage = feeds[i].user.profile_image_url_https;
var status = feeds[i].text;
var isaretweet = false;
var isdirect = false;
var tweetid = feeds[i].id_str;
//If the tweet has been retweeted, get the profile pic of the tweeter
if(typeof feeds[i].retweeted_status != 'undefined'){
profileimage = feeds[i].retweeted_status.user.profile_image_url_https;
tweetscreenname = feeds[i].retweeted_status.user.name;
tweetusername = feeds[i].retweeted_status.user.screen_name;
tweetid = feeds[i].retweeted_status.id_str
isaretweet = true;
};
//Check to see if the tweet is a direct message
if (feeds[i].text.substr(0,1) == "#") {
isdirect = true;
}
//console.log(feeds[i]);
if (((showretweets == true) || ((isaretweet == false) && (showretweets == false))) && ((showdirecttweets == true) || ((showdirecttweets == false) && (isdirect == false)))) {
if ((feeds[i].text.length > 1) && (displayCounter <= displaylimit)) {
if (showtweetlinks == true) {
status = addlinks(status);
}
if (displayCounter == 1) {
feedHTML += headerHTML;
}
feedHTML += '<div class="twitter-article">';
feedHTML += '<div class="twitter-pic"><a href="https://twitter.com/'+tweetusername+'" ><img src="'+profileimage+'"images/twitter-feed-icon.png" width="42" height="42" alt="twitter icon" /></a></div>';
feedHTML += '<div class="twitter-text"><p><span class="tweetprofilelink"><strong><a href="https://twitter.com/'+tweetusername+'/status/'+tweetid+'">'+relative_time(feeds[i].created_at)+'</span><br/>'+status+'</p></div>';
feedHTML += '</div>';
displayCounter++;
}
}
}
$('#twitter-feed').html(feedHTML);
});
//Function modified from Stack Overflow
function addlinks(data) {
//Add link to all http:// links within tweets
data = data.replace(/((https?|s?ftp|ssh)\:\/\/[^"\s\<\>]*[^.,;'">\:\s\<\>\)\]\!])/g, function(url) {
return '<a href="'+url+'" >'+url+'</a>';
});
//Add link to #usernames used within tweets
data = data.replace(/\B#([_a-z0-9]+)/ig, function(reply) {
return '<a href="http://twitter.com/'+reply.substring(1)+'" style="font-weight:lighter;" >'+reply.charAt(0)+reply.substring(1)+'</a>';
});
return data;
}
function relative_time(time_value) {
var values = time_value.split(" ");
time_value = values[1] + " " + values[2] + ", " + values[5] + " " + values[3];
var parsed_date = Date.parse(time_value);
var relative_to = (arguments.length > 1) ? arguments[1] : new Date();
var delta = parseInt((relative_to.getTime() - parsed_date) / 1000);
var shortdate = time_value.substr(4,2) + " " + time_value.substr(0,3);
delta = delta + (relative_to.getTimezoneOffset() * 60);
if (delta < 60) {
return '1m';
} else if(delta < 120) {
return '1m';
} else if(delta < (60*60)) {
return (parseInt(delta / 60)).toString() + 'm';
} else if(delta < (120*60)) {
return '1h';
} else if(delta < (24*60*60)) {
return (parseInt(delta / 3600)).toString() + 'h';
} else if(delta < (48*60*60)) {
//return '1 day';
return shortdate;
} else {
return shortdate;
}
}
});
This is the get_tweets.php script where I encode the results in a JSON format.
<?php
session_start();
require_once('twitteroauth/twitteroauth/twitteroauth.php');
$twitteruser = "jackcoldrick";
$notweets = 30;
$consumerkey="xxxxxxxxxxxxxx";
$consumersecret="xxxxxxxxxxxxxxx";
$accesstoken="xxxxxxxxx";
$accesstokensecret="xxxxxxxxxxxxxx";
function getConnectionWithAccessToken($cons_key, $cons_secret, $oauth_token, $oauth_token_secret){
$connection = new TwitterOAuth($cons_key, $cons_secret, $oauth_token, $oauth_token_secret);
return $connection;
}
$connection = getConnectionWithAccessToken($consumerkey, $consumersecret, $accesstoken, $accesstokensecret);
$tweets = $connection->get("https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=".$twitteruser."&count=".$notweets);
echo json_encode($tweets);
?>
This seems doable with your current code. Things to consider:
I'm not sure, but Twitter might have a limit on requests (I imagine it's not a huge one)
Just encapsulate the reusable parts of your code in a function called updateTweets, and call that with a setInterval. There isn't anyway to really "push" tweet updates to your JavaScript, that I know of.
I would put your update code into a function that has a SetTimeout() that does a recursive call to the new function every x seconds. An example below.
$(document).ready(function () {
// Call to your update twitter function
updateTwitter(data);
});
function updateTwitter(data) {
// do your original update twitter GET
$.getJSON('http://www.jdiadt.com/twitterv1_1feed/get_tweets.php', function () {
//... all that code
});
// Sets a timer that calls the updateTwitter function 1x a minute
setTimeout(function () { updateTwitter(data); }, 60000);
}

Having issues with live calculations, calculating each key stroke

I have a table that calculates a total depending on the input the user types. My problem is that the jquery code is calculating each key stroke and not "grabbing" the entire number once you stop typing. Code is below, any help woud be greatly appreciated.
$(document).ready(function() {
$('input.refreshButton').bind('click', EstimateTotal);
$('input.seatNumber').bind('keypress', EstimateTotal);
$('input.seatNumber').bind('change', EstimateTotal);
});
//$('input[type=submit]').live('click', function() {
function EstimateTotal(event) {
var tierSelected = $(this).attr('data-year');
var numberSeats = Math.floor($('#numberSeats_' + tierSelected).val());
$('.alertbox_error_' + tierSelected).hide();
if (isNaN(numberSeats) || numberSeats == 0) {
$('.alertbox_error_' + tierSelected).show();
} else {
$('.alertbox_error_' + tierSelected).hide();
var seatHigh = 0;
var seatLow = 0;
var seatBase = 0;
var yearTotal = 0;
var totalsArray = [];
var currentYear = 0;
$('.tier_' + tierSelected).each(function() {
seatLow = $(this).attr('data-seat_low');
firstSeatLow = $(this).attr('data-first_seat_low');
seatHigh = $(this).attr('data-seat_high');
seatBase = $(this).attr('data-base_cost');
costPerSeat = $(this).attr('data-cost_per_seat');
years = $(this).attr('data-year');
seats = 0;
if (years != currentYear) {
if (currentYear > 0) {
totalsArray[currentYear] = yearTotal;
}
currentYear = years;
yearTotal = 0;
}
if (numberSeats >= seatHigh) {
seats = Math.floor(seatHigh - seatLow + 1);
} else if (numberSeats >= seatLow) {
seats = Math.floor(numberSeats - seatLow + 1);
}
if (seats < 0) {
seats = 0;
}
yearTotal += Math.floor(costPerSeat) * Math.floor(seats) * Math.floor(years) + Math.floor(seatBase);
});
totalsArray[currentYear] = yearTotal;
totalsArray.forEach(function(item, key) {
if (item > 1000000) {
$('.totalCost_' + tierSelected + '[data-year="' + key + '"]').append('Contact Us');
} else {
$('.totalCost_' + tierSelected + '[data-year="' + key + '"]').append('$' + item);
}
});
}
}
You'll need a setTimeout, and a way to kill/reset it on the keypress.
I'd personally do something like this:
var calc_delay;
$(document).ready(function() {
$('input.refreshButton').bind('click', runEstimateTotal);
$('input.seatNumber').bind('keypress', runEstimateTotal);
$('input.seatNumber').bind('change', runEstimateTotal);
});
function runEstimateTotal(){
clearTimeout(calc_delay);
calc_delay = setTimeout(function(){ EstimateTotal(); }, 100);
}
function EstimateTotal() {
....
What this does is prompt the system to calculate 100ms after every keypress - unless another event is detected (i.e. runEstimateTotal is called), in which case the delay countdown resets.

Categories