JS issue with printing - javascript

I have a function which takes on some parameters and forwards it to print method.
getPrintAction: function () {
return new Ext.Action({
scope : this,
handler : function () {
var printRenderer = new BaseRenderer;
var data = [...];
var printData = [...];
printRenderer.print(printData);
}
});
}
While this works perfectly, when I add an Ajax request, the whole thing breaks.
getPrintAction: function () {
return new Ext.Action({
scope : this,
handler : function () {
var printRenderer = new BaseRenderer;
var data = [...];
Ext.Ajax.request({
scope : this,
params : {...},
success: function (result) {
var printData = [...];
printRenderer.print(printData);
},
failure: callback()
});
}
});
}
I tried to put breakpoints as I thought new printData may be causing errors. But that is not the case, printData parses result from callback and the result is fine. I am getting the right array to forward to print() method.
Ultimately it fails here:
print: function (component) {
var win = window.open('','print');
win.document.write(component); <--- HERE
win.document.close();
}
Where win variable is set to null if I am going there from request.
I suppose this may be a scope issue but I have no idea how to debug it...

Browsers usually blocks asynchronous calls to window.open. You will see at the right of the URL bar a notification about this block.
That is to prevent spam. You now hate it but trust me that you have been saved of spam because of this a lot of times, so don't hate it.
Such methods needs to be called at the very moment of the click event. If you defer a call to window.open in the future, although the callback is created at the moment of the click, it will break.
What you can do is to add another popup that the user will need to click just after the Ajax request. Something like showing a "loading" popup that will change to a "Click here to print" when the load ends.

Related

Javascript Button OnClick not working, method runs normally

I am trying to create a button in Javascript, that when clicked will send an AJAX Request to some PHP Code.
I have already setup 3 buttons that do the same thing and they are all working fine.
The most bizarre thing is that if I call the method directly it runs fine.
The code for the button:
<button id="toggle-button">Toggle</button>
The Javascript:
var toggleButton = document.getElementById('toggle-button');
...
function init()
{
...
toggleButton.onClick = handleToggleClick;
...
}
function handleToggleClick(event)
{
alert("Sending Request");
var admin_url = "http://localhost/wp-admin/admin-ajax.php";
var data = {
action : 'toggle',
}
$.post(admin_url, data, function(resp) {
alert(resp);
});
}
I have called the following in the Chrome Developer Tools Console:
handleToggleClick(null); // The request is sent
autoScheduleButton.onClick(); // The request is sent
autoScheduleButton.onClick; //It prints out the function
autoScheduleButton; //It displays the HTML of the button.
As I mentioned before there are 3 other buttons which do practically the same thing and work fine, so I really can't see why this isn't working.
Hopefully this is just something really obvious i missed.
EDIT:
Originally I made an error while anonymising the code, the code above is now correct in the init method.
Previous code:
function init()
{
...
toggleButton.onClick() = handleToggleClick;
...
}
In init function you have code like toggleButton.onClick() = handleToggleClick;. This means that you assign to the result of the onClick() function to the handleToggleClick.
You need to assign to onClick and not call it.
Try this instead toggleButton.onClick = handleToggleClick;
function init()
{
...
toggleButton.onClick = handleToggleClick;
...
}
The code toggleButton.onClick() = handleToggleClick; in the init function is not a right assignment. It actually means You are calling onClick function on toggleButton and then assigning to the result handleToggleClick.
change it ti toggleButton.onClick= handleToggleClick meaning you are listening to a click event on toggleButton and then calling function handleToggleClick

Cannot load Script using jquery Ajax request

I need to execute a function after a certain script was loaded so I load the script using an ajax call and on the success I invoke the function that uses an object that was suppose to be loaded already from that script and I get an error that this object is undefined.
Here is my code:
function LoadInlineManualScript() {
return $.ajax({
url: "../Scripts/inline-manual-player.js",
dataType: "script",
beforeSend: function () {
loader.hide();
loader.unbind();
},
success: SetInlineManualCallbacks
});
}
function SetInlineManualCallbacks() {
debugger;
//Here I get the error!
window.inline_manual_player.setCallbacks({
onTopicDone: function (player, topic_id, step_id) {
console.log('Step was displayed.');
}
});
}
And I get Uncaught TypeError: Cannot read property 'setCallbacks' of undefined.
I tried to change the call to use async:false but it didn't help.
Two things:
Function SetInlineManualCallbacks doesn't have a return statement. So despite issue 2, SetInlineManualCallbacks(inlineUid) should return undefined. However, success needs a function:
success: SetInlineManualCallbacks.bind(null, inlineUid)
Or old-school without bind:
success: function() {
SetInlineManualCallbacks(inlineUid)
}
But, looks like SetInlineManualCallbacks doesn't need any arguments, so this would be enough in your case:
success: SetInlineManualCallbacks
It says window.inline_manual_player is undefined. Judging from the variable name, I think it's because you just loaded the JS file content, as plain text, but you didn't execute the content as JavaScript.
You could use eval to execute it in success callback, but that's really a bad idea. I would probably insert a <script> tag into DOM, instead of using AJAX.
function LoadInlineManualScript() {
var s = document.createElement('script')
s.src = "../Scripts/inline-manual-player.js"
// your callback will fire when the script is loaded and executed
s.onload = SetInlineManualCallbacks
// it will start to load right after appended, and execute of course
document.body.appendChild(s)
}
Why you are loading by ajax? Let the browser do it.
The function loadScript insert the script on DOM and attach a callback to the event onload of script element.
function loadScript(href_script, callback) {
var script = document.createElement('script');
script.src = href_script;
script.onload = callback;
// The same code used by Google Analytics
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(script, x);
};
function SetInlineManualCallbacks() {
debugger;
//Here I get the error!
console.log("Code Running");
}
Here an example:
loadScript("//www.google-analytics.com/analytics.js", SetInlineManualCallbacks);
When you do this
success: SetInlineManualCallbacks(inlineUid)
You are executing the method and setting whatever it returns to the success callback. You are not saying call this when success is triggered.
It should just be
success: SetInlineManualCallbacks
But I think there is an issue with the Ajax call itself, you are loading a script and expecting it to do something, I do not think the Ajax call is what you want. If the script is just returning inlineUid why are you using a script and not a JSON or text call. If inlineUid is global, than just read it in the callback method. Without knowing what is happening in the script file, it is hard to give a full answer.
As someone suggested, Inline Manual is initialising itself in that script. You have your Site in Test mode, therefore there is an additional ajax request within the script itself, to check if the user can access it. So the script might load, but the inline_manual_player object won't be available yet when it is loaded because there are other things happening before it creates the object.
If you want to set the callbacks before the object is available, you can use queuing actions before calling the script to load.
var inline_manual_player = inline_manual_player || [];
inline_manual_player.push(['setCallbacks', my_callbacks]);
http://help.inlinemanual.com/docs/queuing-actions
This answer is a combination of some answers I got here:
The function I use to load the script instead of using Jquery's $.getScript is:
//Must load the script after inlineManualTracking object is ready.
function LoadInlineManualScript() {
loader.hide();
loader.unbind();
var script = document.createElement('script');
script.src = "../Scripts/inline-manual-player.js";
// your callback will fire when the script is loaded and executed
script.onload = WaitForInlineManualScriptToLoad;
// it will start to load right after appended, and execute of course
document.body.appendChild(script);
}
The script I load has use strict and it cannot be loaded with jquery because of the following reason: "use strict"; + jQuery.getScript() = script can't export to global namespace like epascarello mentioned earlier.
Even after using Leo's answer I still had a problem with that object and that's why I use the following function after the onload event of the script.
function WaitForInlineManualScriptToLoad() {
if (typeof window.inline_manual_player == 'undefined') {
setTimeout(WaitForInlineManualScriptToLoad, 100);
return;
}
InitInlineManualPlayerCallBacks();
}
function InitInlineManualPlayerCallBacks() {
window.inline_manual_player.setCallbacks({
onTopicDone: function (player, topic_id, step_id) {
console.log('Step was displayed.');
}
});
}
This works! it's not the prettiest, but it works. The developer of inline-manual tool had responded here(Marek) and mentioned the fact that my site in under "Test" mode. There was no way we could know this can affect.
According to your question you want to execute a skript, after another script was loaded correctly.
In your own answer using the jQuery "getScript"-function won´t work for you, so why not trying the old-fashioned way without using jQuery after all?
Create a new script-element, add it to your document head and set a callback-function on readystate.
function loadScript(url, callback) {
var newScript = document.createElement( "script" )
newScript.type = "text/javascript";
if(newScript.readyState) { //IE
newScript.onreadystatechange = function() {
if (newScript.readyState === "loaded" || newScript.readyState === "complete") {
newScript.onreadystatechange = null;
callback();
}
};
} else { //Others
newScript.onload = function() {
callback();
};
}
newScript.src = url;
document.getElementsByTagName("head")[0].appendChild( newScript );
}
Now you can call the defined method and offer a callback-function, in your case this would be:
loadScript("../Scripts/inline-manual-player.js", function() {
debugger;
// should work now
window.inline_manual_player.setCallbacks({
onTopicDone: function (player, topic_id, step_id) {
console.log('Step was displayed.');
}
});
});
It seems the script wasn't executed at that time. Please note the callback is fired once the script has been loaded but not necessarily executed, based on JQuery documentation.
If you want to make sure the script was executed, you can load the script synchronically on the HTML before you use it. If you do this, you won't need ajax, and you will keep your code simple. KISS principle
If you need to do it the way you have it here, then you can apply the observer pattern creating a custom event.
Here is an example.
At the end of the file "inline-manual-player.js". You can do something like this.
$.event.trigger({
type: "fileExecuted");
Then instead of the success callback, you would subscribe to the "fileExecuted" event.
$(document).on("fileExecuted", SetInlineManualCallbacks);
Hope it helps.
success takes a function and not a value. Unless SetInlineManualCallbacks returns another function, use bind and partially apply arguments.
function LoadInlineManualScript() {
return $.ajax({
url: "../Scripts/inline-manual-player.js",
dataType: "script",
beforeSend: function () {
loader.hide();
loader.unbind();
},
success: SetInlineManualCallbacks.bind(null, inlineUid)
});
}
It would help if you explained where inlineUid comes from.
It would appear that inline-manual-player.js loads window.inline_manual_player asynchronously. This means the thread is allowed to move on after that file's onload event fires but before window.inline_manual_player is loaded. Thus, you're finding that window.inline_manual_player is undefined for a period. This is probably due to inline-manual-player.js making ajax calls of its own (or setTimeouts, etc. - JavaScript is an asynchronous beast).
Rather than setting an ugly (no offense) setTimeout, there are two potentially better possibilities.
1
If you're willing and able to update inline-manual-player.js, you could put something like this at the top of that file:
window.inline-manual-player-callback-queue = [];
... and later in that file, after window.inline_manual_player is asynchronously defined, add:
// I've made this an array, in case you'd like to add multiple functions to be called upon window.inline-manual-player being ready.
var queue = window.inline-manual-player-callback-queue;
var length = queue.length;
var i = 0;
for(i; i<length; i++) {
queue[i]();
}
Finally, in your initial file, input the following:
function addCallback() {
window.inline_manual_player.setCallbacks({
onTopicDone: function (player, topic_id, step_id) {
console.log('Step was displayed.');
}
});
}
$.ajax({
url: "../Scripts/inline-manual-player.js",
dataType: "script",
beforeSend: function () {
loader.hide();
loader.unbind();
},
success: function() {
// Because you've defined window.inline-manual-player-callback-queue at the start of inline-manual-player.js, the following will be available upon onload (aka success) of that file:
window.inline-manual-player-callback-queue.push(addCallback);
}
});
2
If editing inline-manual-player.js isn't an option, the initial author of that file may have already included something akin to window.inline-manual-player-callback-queue. Check the documentation of the file, and see if there's not a global method or array you can add your callback function to, to have inline-manual-player.js call it automatically, once all the async magic has taken place.
Best of luck to you!

js jquery forward saved event

I´m holding back some of my native click events on hyperlinks, to check if the result page holds a content.
Im saving the jquery event object and after some checkings, i want to let the event go its natural way.
Capture Event
Check for Contents
If contents available
forward event as it was fired
else
do nothin
At this moment, I just saving the "href" property and want to set it to the document.location.href if true comes back.
Now, the question: Is there a better way to forward/reraise the existing event, than setting the href to the document.location.href?
Using document.location.href would be fine, and also seems like the simplest option to me.
But just for the sake of exploring other options, you could also have js click the link for you if it's deemed as safe. For example, something like this.
$('a').click(function() {
if( !$(this).is('.content-verified') ) {
var self = this;
// Do your content checking here, with the callback for verified good
// content being $(self).has_good_content();
return false;
}
return true;
});
// Callback for good content
// should be something like this:
$.fn.has_good_content = function() {
return $(this).each(function() {
$(self).addClass('content-verified');
$(self).click();
});
};
This sounds like a job for the jQuery Deferred object. New in jQuery 1.5+
function done() {
var dfd = $.Deferred(),
timeout;
timeout = setInterval(function() {
if (contents available) {
clearInterval(timeout);
return dfd.resolve();
}
}, 50);
return dfd.promise();
}
$('#my-link').bind('click', function(e) {
e.preventDefault();
var $this = $(this);
$.when(done())
.then(function(o) {
//unbind the click event that prevents the default action and click on the link afterward
$this.unbind('click').click();
});
});
So what is happening is it will wait for the resolve/success state from the done function. You are telling your click event to wait because done is using the Deferred object and has promised to return something.
I have put a setInterval to check every 50 mili seconds if the contents have loaded then I resolve the Deferred object therefore the then in click event will be called.
You can pass an object to dfd.resolve(); like this dfd.resolve({ test: true });. Then the o argument in then will have o.test .
I have used Deferred several times and I really liked it.
Hope this helps

How to 'queue' piece of code in javascript

I've made a class that, when initialized, starts to download a JSON file.
However, the downloading runs asynchronously.
So after I declare it I start working with it, but it fails because it hasn't loaded the file yet.
I could turn async off, but is there another way without freezing the browser?
I'm currently working around it with a setTimeout option, but that seems like an ugly hack.
var d;
$(document).ready(function() {
d = new Duk('element');
d.getBlueprint('hud.json');
setTimeout(start, '2000');
});
function start(){
test = new d.Dialog(d.blueprint.screens.test);
test.draw();
}
You have to attach event handler to the object load completion event. The library you are working with must supply this event. I don't know what a Duk is, or what getBlueprint() does, but you should check the documentation for whatever that class and method is, and see if there is an oncomplete callback. If there is, you'd do something like this:
$(document).ready(function() {
d = new Duk('element');
d.getBlueprint('hud.json', {
onComplete: function() {
test = new d.Dialog(d.blueprint.screens.test);
test.draw();
}
});
});
Obviously, I just made that up. I don't know if your library has an onComplete method defined like this, but I hope you see what I mean. Without knowing more about the library you are using, I can't give a better example.
Timeouts are not the way to solve this problem.
You want to set up a callback function alongside the request so that the object handling the request knows what to do when the response comes in.
It is unclear from your code what framework you're using (I can't make any sense of getBlueprint, which seems to be the call that initializes the remote request), so if you could provide more information on this, we can provide more customized help.
You want your getBlueprint() method to be able to accept a callback which is run when the file is finished downloading.
function getBlueprint(file, callback)
{
// do whatever to get the file
// ...
// trigger the callback
callback();
}
$(document).ready(function() {
var d;
function start(){
test = new d.Dialog(d.blueprint.screens.test);
test.draw();
}
d = new Duk('element');
d.getBlueprint('hud.json', start);
setTimeout(start, '2000');
});
I agree that a callback function is the proper way. If this is code you cannot modify, you can do something like this, but seriously, use a callback!
function wait_for_load(expr, func)
{
var interval = window.setInterval(function()
{
if(eval(expr))
{
window.clearInterval(interval);
func();
}
}, 20);
}
var d;
$(document).ready(function() {
d = new Duk('element');
d.getBlueprint('hud.json');
wait_for_load('d.blueprint', start);
});

jQuery, unable to store data returned by $.get function

I am trying to turn div#sidebar into a sidebar in my app. My code looks like the one below.
$('#sidebar').userProfile();
jQuery.fn.userProfile = function() {
$.get('/users/profile', function(data){ $(this).html(data); });
};
It didnt work because, I found the this (inside the $.get function) here contexts to the get request and not $('#sidebar'). Then I tried something like below.
$('#sidebar').userProfile();
#This doesnot work
jQuery.fn.userProfile = function() {
var side_bar = null;
$.get('/users/profile', function(data){ side_bar = data; });
$(this).html(side_bar);
console.log(side_bar);
};
This doesnt work either. In firebug console I see Null which I am setting on top when I am declaring the variable.Atlast I made it work by changing my code to something like below by hardcoding the selector.
#This works, but I cannot turn any element to a sidebar which is sick.
jQuery.fn.userProfile = function() {
$.get('/users/profile', function(data){ $('#sidebar').html(data); });
};
But this is not I wanted because I wanted to turn any element to a sidebar. Where am I goin wrong or which is the correct way of doing it?
$('#sidebar').userProfile();
jQuery.fn.userProfile = function() {
var elem = this;
$.get('/users/profile', function(data){
$(elem).html(data);
});
};
Since this changes with each context, it's an often used technique to store this in another variable at your earliest convenience to capture the right context.
Your second example doesn't work because the .get callback is executed asynchronously at an undefined later time (when the data comes back).

Categories

Resources