This post discusses how to inform the user via a progress bar by calculating the percentage of the file being downloaded by a www::mechanize Perl script.
I need to inform my user of a www::mechanize script's progress but not necessarily in percentage remaining. The script is migrating through a user's account and picking up data, which can vary significantly in size so the percentage factor is unknown.
If I can show "progress" via some js DOM div writing (in the dialog that shows a "loader" image while the perl script is running) then that would suffice.
Can I inject js script like:
<script>
$('#formboxtext').html('Logging into account ...');
</script>
into the Perl script to show progression to my user? Or does the Perl script have to return before the DOM will get updated? (The answer appears to be "no".)
Are JavaScript::SpiderMonkey and WWW::Scripter modules I need to make this happen, or is the solution more simple?
EDIT:
I am expanding a PHP-based CMS. I have written a screen-scraping script using Perl and the module www::Mechanize which traverses several pages of a user's account on another site for retrieval into mySQL databases. I will display the collected content in a php form for the user to save once the Perl script is completed. The collection process will range from 10 seconds to a minute. I would like to display progression as the script navigates the user's account pages collecting information.
To begin, I supply the user a jQuery modal dialog (calling a php file to fill it's contents with a form of username and password inputs) used to login to their personal account. I show a loader image in this dialog and a sentence asking for patience, but I would like to use jQuery to rewrite this sentence (div) as the Perl script navigates through pages, sending back progress to display; such as, "I'm here now. Now I'm over here." If an error occurs (i.e. bad login) then I can rewrite the modal dialog and offer solutions per usual. If success, then I would close the dialog and display the collected information in form inputs ready for saving into my database - on the php page that spawned the modal dialog.
If all of this entails multiple DOMs, forking processes and returning control from one script execution to another... then I am definitely over my head. BUT, I would like to learn it. :) I would appreciate an overview on what to read and learn. If it's actually much simpler than I realize then I would appreciate that answer too.
Thanks to daxim and reinierpost for their patience and advice. Thanks for any help.
The Answer:
Recap: For me, I decided to fake the progress bar showing progression by estimating the time it would take. That has worked nicely. This post shows how you can dup output from a perl script back to the calling php script, but then feeding that information back to the original DOM was becoming too complex for it's worth. It was made more complex by various parameters passed to the perl script which changed the progression and output. "Faking it" proved a nice solution. Hey, now I see why my girlfriend does it! :)
PS. I gave the green checkmark to daxim because he has answered other questions of mine and been a big help to me, even though he was sort of shooting in the dark on this one.
Instead of printing progress to STDOUT as in https://stackoverflow.com/a/1938448, expose the number as a Web service. This should suffice:
sub {
return [200, [Content_Type => 'text/plain'], [$PROGRESS]]
}
On the client side, use jQuery get to poll the Web service every half second or so and jQuery Progressbar to display it.
Edit: code example
use 5.010;
use strictures;
use DBI qw();
use Plack::Request qw();
use POSIX qw(floor);
use Forks::Super qw(fork);
use WWW::Mechanize qw();
sub db_connect {
return DBI->connect('dbi:SQLite:dbname=/var/tmp/progress.db');
}
sub {
my ($env) = #_;
my $req = Plack::Request->new($env);
if ('/progress' eq $req->path_info) {
return [200,
[Content_Type => 'text/html'],
[db_connect->selectrow_array('select progress from progress')]
]
} else {
fork(sub => sub {
state $total = 0;
my $dbh = db_connect;
$dbh->do('delete from progress');
$dbh->do('insert into progress (progress) values (0)');
WWW::Mechanize->new->get(
'http://localhost:5000/VBoxGuestAdditions_4.0.4.iso', # large-ish file
':read_size_hint' => 1024**2,
':content_cb' => sub {
my ($data, $response, $proto) = #_;
$total += length($data);
my $size = $response->header('Content-Length');
$dbh->do(
'update progress set progress = ?', {}, floor(($total/$size)*100)
);
sleep 1;
},
);
}) unless defined db_connect->selectrow_array('select progress from progress');
return [200,
[Content_Type => 'text/html'],
[q~<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.8.18/themes/base/jquery-ui.css" />
<style>
#progress { width: 80em; height: 5em; border: 1px solid black; }
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js"></script>
<script>
jQuery(document).ready(function() {
// TODO: stop the timer if progress == 100 or no response
var timer = setInterval(function() {
jQuery.ajax({ async: false, cache: false, dataType: 'html', url: 'http://localhost:5001/progress' }).done(function(progress) {
jQuery('#progress').progressbar({ value: parseInt(progress) });
});
}, 500);
});
</script>
</head>
<body>
<h1>downloading your stuff</h1>
<div id="progress"><div>
</body>
</html>~]
]
}
};
Related
I am new here and a massive noob when it comes to coding. I have been teaching myself some basics, but I cannot seem to figure this one out.
I'm trying to integrate this piece of code in my website. It is an automaticcaly genenerated code from Eventbrite. Somehow it the whole thing is showing op twice in the front end of my website. I am using Divi builder with the code module. I hope someone can help. I have been googling around for over an hour now but I can't seem to find a fix.
<div id="eventbrite-widget-container-107705263342"></div>
<script src="https://www.eventbrite.nl/static/widgets/eb_widgets.js"></script>
<script type="text/javascript">
var exampleCallback = function() {
console.log('Order complete!');
};
window.EBWidgets.createWidget({
// Required
widgetType: 'checkout',
eventId: '107705263342',
iframeContainerId: 'eventbrite-widget-container-107705263342',
// Optional
iframeContainerHeight: 425, // Widget height in pixels. Defaults to a minimum of 425px if not provided
onOrderComplete: exampleCallback // Method called when an order has successfully completed
});
</script>
I have a 3D model being rendered on my site through an image rotator .xml config file. This feature works but I am attempting to render a completely different .xml in place of the previous file through a JS on change event.
I have done a fair bit of reading in order to solve this issue, although I have not found an answer. I have already tried to make the JQuery script into a function as seen below:
function updateModel(xml_file_path) {
console.log('updating room model...');
console.log('xml_file_path: ' + xml_file_path);
// clear past model
$("#wr360PlayerId").empty();
jQuery('#wr360PlayerId').rotator({
licenseFileURL: 'license.lic',
configFileURL: '/static/360_assets/' + xml_file_path,
graphicsPath: '/static/img/basic',
zIndexLayersOn: false,
responsiveBaseWidth: 600,
responsiveMinHeight: 0,
googleEventTracking: false,
});
console.log('rendering: ' + xml_file_path);
}
// clears the old model then updates the configFileURL to the new model
This was successful in clearing the previous model although when I inspect the new model the images used by the image rotator are not being loaded and nothing is displayed.
wr360 documentation
I've also read through the documentation for wr360 above and found a few different ways of loading the image rotator on my site. I've gone through each and attempted to make it update using similar methods as JQuery but each had their own oddities that were difficult to overcome.
There's not much to code to this as for most of it is created dynamically on page load, but I'll try to provide all code necessary below:
js
function updateModel(xml_file_path) {
console.log('updating room model...');
console.log('xml_file_path: ' + xml_file_path);
// clear past model
$("#wr360PlayerId").empty();
jQuery('#wr360PlayerId').rotator({
licenseFileURL: 'license.lic',
configFileURL: '/static/360_assets/' + xml_file_path,
graphicsPath: '/static/img/basic',
zIndexLayersOn: false,
responsiveBaseWidth: 600,
responsiveMinHeight: 0,
googleEventTracking: false,
});
console.log('rendering: ' + xml_file_path);
}
$(document).ready(function(){
$('#rooms').on('change', function() {
updateModel(room.xml_path);
console.log('model updated');
});
});
// truncated for simplicity
html
<div id="wr360PlayerId" class="wr360_player" style="background-color:#FFFFFF;">
</div>
The xml file path is getting passed correctly (checked by the console.log('xml_file_path: ' + xml_file_path);) it just doesn't render the second rotator.
$('#rooms') is a select field, and room.xml_path is the selected rooms .xml file path. With this being said, ideally, the on change event would show the selected model and if the selection changes again it should render the new model (instead of nothing like it currently does).
Either I am missing something or it is impossible to update a model without refreshing the page, either way, any help is appreciated!
You can actually use,
apiObj.reload(xml_path);
to simply reload the image rotator with a new xml file path.
I built a custom login page for users here.
If a user tries to close the popup window before he/she registered, I want the div with ID "uzenet" to have a shake effect, like in a Wordpress login-form.
The good news is, the plugin has a good api function, which is documented: here.
The function I need to use is: on_close_popup_ui, but it isn't working for me, and I don't know why.
If I understand the documentation, the correct code should look like this:
<!-- The plugin will be embedded into this div //-->
<div style="margin-top: 23px;" id="oa_social_login_container"></div>
<div style="margin-top: 23px;" id="oa_social_login_container"></div>
<script type="text/javascript">
var _oneall = _oneall || [];
_oneall.push(['social_login', 'do_popup_ui']);
_oneall.push(['social_login', 'set_callback_uri', 'http://neocsatblog.mblx.hu/']);
_oneall.push(['social_login', 'set_providers', ['facebook', 'google', 'steam', 'twitter', 'windowslive', 'wordpress']]);
_oneall.push(['social_login', 'do_render_ui', 'oa_social_login_container']);
/*
Signature
<scope> : string (social_login)
<function> : JavaScript function to be executed
*/
_oneall.push(['social_login', 'set_event', 'on_close_popup_ui', 'my_on_close_popup_ui']);
/* Example */
var my_on_close_popup_ui = function() {
alert("You have closed the popup user interface");
}
_oneall.push(['social_login', 'set_event', 'on_close_popup_ui', 'my_on_close_popup_ui']);
</script>
But unfortunately, this doesn't work, and I don't know why.
I don't get any syntax errors in chrome console, and I also don't get the alert popup, which means to me the function is stuck, before receiving themy_on_close_popup_ui named.
_oneall.push(['social_login', 'do_render_ui', 'oa_social_login_container']);
This line must always come last. Any _oneall.push(...) commands made after it are not take into consideration.
I jsut started learning angular.js. Can you guys show me the right way to make a page that initially presents an ajax loader element saying 'Loading data' or something like that. Then after data's been fetched it would update the view and hide the element. I can put stuff in page load event using jquery, but how do you do that using pure angular? So far I figured out how to put that in click event:
<div ng-app="VideoStatus" ng-controller="VideoStatusCtrl">
<button ng-click="getVideos()">get videos</button>
</div>
<script type="text/javascript">
angular.module('VideoStatus', ['ngResource']).run(function(){
// I guess somehow I can start fetching data from the server here,
// but I don't know how to call Controller methods passing the right scope
});
function VideoStatusCtrl($scope, $resource) {
$scope.videoStatus = $resource('/Videos/GetStatuses', { callback: 'JSON_CALLBACK' });
$scope.getVideos = function () {
$scope.videoResult = $scope.videoStatus.get();
console.log('videos fetched');
};
};
</script>
Kudos to Adam Webber & Peter Bacon Darwin
Here is the working plunker
Here is my version plunker that make loading as a directive with modal popup feature
Here is the tutorial to use my version
you only need loading.js and modal.js and reference jQuery and twitterbootstrap css.
in your code,
Only 2 steps you need to do with your code.
Add the following code to HTML
< div data-loading> < /div>
Add LoadingModule module to your application module.
angular.module('YourApp', ['LoadingModule'])
Fist off, been lurking for years, hopefully this post will be helpful to more than just me.
So I have a jstree that is generated for my site using very a small amount of JavaScript. Ideally, I would like for the page to load and show the 2 top folders only.
However, currently the page displays all of the names of the folders, subfolders, files, etc. for about 0.5 seconds before switching to the proper view. There are probably about 200 items in the tree structure
I added a manual tree.bind which does a "close_all", and I also tried hiding the DIV that it eventually appears in. Even though I put the code to show the DIV after I create the tree, it still shows everything before hiding itself.
I am using jsTree 1.0-rc3
Anyone have any thoughts?
<script type="text/javascript">
(document).ready(function () {
var tree = $("#sharepointHierarchy");
tree.bind("loaded.jstree", function (event, data) {
tree.jstree("close_all");
});
$("#sharepointHierarchy").jstree({
'plugins' : [ "themes", "html_data", "types", "ui" ],
'core' : {/* core options go here */},
});
document.getElementById("sharepointHierarchy").style.display="block";
});
</script>
I was able to mask this issue by adding the following code to the end of the function(). It is fairly straight-forward and simply gives the table time to load before showing it.
setTimeout(function() {
$("#sharepointDiv").show();
},1200);
I hope this helps someone else also.