ABCPdf add document javascript - javascript

Is it possible to add document JavaScripts to a generated PDF using ABCPdf?

If you mean, Javascript that is being executed after the document has been loaded, then have a look at this documentation page.
doc.HtmlOptions.UseScript = true;
doc.HtmlOptions.GeckoSubset.OnLoadScript =
#"(function() {
window.ABCpdf_go = false;
// your javascript code here
window.ABCpdf_go = true;
})();";

I think you should be able to. There are some settings in the c# which will help.
You need to set the
doc.HtmlOptions.UseScript = true;
this will enable javascript to run.
it's probably worth setting the timeout to give it more time to finish loading
doc.HtmlOptions.Timeout = 10000;
and i've always had better results with the gecko rendering engine
doc.HtmlOptions.Engine = EngineType.Gecko;

Related

document.getElementById always returns "null" for ribbons

I need to set the background color of one of the buttons in the form's ribbon. This isn't supported through Ribbon Workbench, so I have written following javascripts to achieve the same:
function setOpportunityRibbonsAppearance() {
var submitToForeCastButton = parent.document.getElementById("opportunity|NoRelationship|Form|sfw.opportunity.Button1.Button");
if (submitToForeCastButton != null) {
submitToForeCastButton.style.backgroundColor = "lightyellow";
}
}
I have registered this scripts in Form Load event. However the issue is that, I always get parent.document.getElementById as null only.
Surprisingly, I am able to see the control while running the parent.document.getElementById statement in the browser's console, and can also change the styling attributes.
Can anyone please suggest what could be wrong here?
P.S. - I understand document.getElementById is not recommended to use in CRM, however, I am left with no other choice while trying to change the appearance of some of the buttons.
Any help on this, will be much appreciated.
You could upload an icon with a yellow background, to keep everything supported. You won't see text on yellow but it might work for you. Easy and standard.
To keep it unsupported and ugly, you could just keep on trying until you make it, setInterval allows for a function to be repeated:
function setOpportunityRibbonsAppearance() {
var submitToForeCastButton = null;
var interval = setInterval(function(){
submitToForeCastButton = parent.document.getElementById("opportunity|NoRelationship|Form|sfw.opportunity.Button1.Button");
if(submitToForeCastButton != null) {
submitToForeCastButton.style.backgroundColor = "lightyellow";
clearInterval(interval);
}
}, 500); // Every 500ms. Adjust as needed, not too fast or browser will choke.
}
Its probably because your script is running before the page is fully loaded.
Try adding a delay to the to the function Put a Delay in Javascript

Bug in my lazyload plugin for mootools

I want to implement a plug-in serial download pictures in MooTools. Let's say there are pictures with the img tag inside a div with the class imageswrapper. Need to consistently download each image after it loads the next and so on until all the images are not loaded.
window.addEvent('domready', function(){
// get all images in div with class 'imageswrapper'
var imagesArray = $$('.imageswrapper img');
var tempProperty = '';
// hide them and set them to the attribute 'data-src' to cancel the background download
for (var i=0; i<imagesArray.length; i++) {
tempProperty = imagesArray[i].getProperty('src');
imagesArray[i].removeProperty('src');
imagesArray[i].setProperty('data-src', tempProperty);
}
tempProperty = '';
var iterator = 0;
// select the block in which we will inject Pictures
var injDiv = $$('div.imageswrapper');
// recursive function that executes itself after a new image is loaded
function imgBomber() {
// exit conditions of the recursion
if (iterator > (imagesArray.length-1)) {
return false;
}
tempProperty = imagesArray[iterator].getProperty('data-src');
imagesArray[iterator].removeProperty('data-src');
imagesArray[iterator].setProperty('src', tempProperty);
imagesArray[iterator].addEvent('load', function() {
imagesArray[iterator].inject(injDiv);
iterator++;
imgBomber();
});
} ;
imgBomber();
});
There are several issues I can see here. You have not actually said what the issue is so... this is more of a code review / ideas for you until you post the actual problems with it (or a jsfiddle with it)
you run this code in domready where the browser may have already initiated the download of the images based upon the src property. you will be better off sending data-src from server directly before you even start
Probably biggest problem is: var injDiv = $$('div.imageswrapper'); will return a COLLECTION - so [<div.imageswrapper></div>, ..] - which cannot take an inject since the target can be multiple dom nodes. use var injDiv = document.getElement('div.imageswrapper'); instead.
there are issues with the load events and the .addEvent('load') for cross-browser. they need to be cleaned up after execution as in IE < 9, it will fire load every time an animated gif loops, for example. also, you don't have onerror and onabort handlers, which means your loader will stop at a 404 or any other unexpected response.
you should not use data-src to store the data, it's slow. MooTools has Element storage - use el.store('src', oldSource) and el.retrieve('src') and el.eliminate('src'). much faster.
you expose the iterator to the upper scope.
use mootools api - use .set() and .get() and not .getProperty() and .setProperty()
for (var i) iterators are unsafe to use for async operations. control flow of the app will continue to run and different operations may reference the wrong iterator index. looking at your code, this shouldn't be the case but you should use the mootools .each(fn(item, index), scope) from Elements / Array method.
Anyway, your problem has already been solved on several layers.
Eg, I wrote pre-loader - a framework agnostic image loader plugin that can download an array of images either in parallel or pipelined (like you are trying to) with onProgress etc events - see http://jsfiddle.net/dimitar/mFQm6/ - see the screenshots at the bottom of the readme.md:
MooTools solves this also (without the wait on previous image) via Asset.js - http://mootools.net/docs/more/Utilities/Assets#Asset:Asset-image and Asset.images for multiple. see the source for inspiration - https://github.com/mootools/mootools-more/blob/master/Source/Utilities/Assets.js
Here's an example doing this via my pre-loader class: http://jsfiddle.net/dimitar/JhpsH/
(function(){
var imagesToLoad = [],
imgDiv = document.getElement('div.injecthere');
$$('.imageswrapper img').each(function(el){
imagesToLoad.push(el.get('src'));
el.erase('src');
});
new preLoader(imagesToLoad, {
pipeline: true, // sequential loading like yours
onProgress: function(img, imageEl, index){
imgDiv.adopt(imageEl);
}
});
}());

How to execute heavy javascript code without the browser freezing?

I had a page which executes heavy javascript code after loading. To prevent the page from freezing upon loading, I spaced the execution into batches with some "no-execution" time in between (using Timeouts), and things worked well.
Lately, I've had to add additional heavy javascript code which can execute upon client actions, yet these actions can occur even before the original heavy script is done executing. This time, spacing the action won't help, since at the "downtime" of one script the other can run and vice versa, which will cause the browser to freeze.
The problem is actually more complicated as there are multiple such actions, each executing a different heavy script, and each script sort of has a different "priority" as to how fast i'd like it to finish, compared to the other ones.
My question is, what is the common practice in such situations? I tried thinking of a way to solve it, but all I could think of was quite a complex solution which would pretty much be like writing an operating system in javascript - i.e., writing a "manager" code which executes every X time (using an "interrupt"), and chooses which "context to switch to" ( = which job should run right now), etc...
This however sounds pretty complicated to me, and I was hoping there might be other solutions out there. My problem sounds like one which I'd assume many people have stumbled upon before, so even if the only solution is what I suggested, I'd assume someone already wrote it, or there is some library support for this.
Any help would be greatly appreciated. Thank you.
== EDIT ==
by "heavy code", I mean for example the DOM manipulation of a great number of elements.
You will need to think of defining your UI/Problem domain as a set of Asynchronous tasks. Here's some more insight http://alexmaccaw.com/posts/async_ui until I formulate a better answer for you.
If you don't want to block your script you can use web workers. See MDN: Using web workers for a good introduction. Note that web workers are still relative new and not supported by most browser.
However, if you want to support all browser and add some kind of priority for your "heavy scripts", you should define something yourself, e.g:
function WorkerQueue(this_argument){
this.queue = [];
this.this_argument = this_argument;
this.priority = 1;
}
WorkerQueue.prototype.enqueue = function(callback){
this.queue.push(callback);
}
WorkerQueue.prototype.dequeue = function(){
return this.queue.splice(0,1)[0];
}
function WorkerPool(){
this.pool = [];
this.status = "running";
this.timeout = null;
}
WorkerPool.prototype.addWorker = function(this_argument){
this.pool.push(new WorkerQueue(this_argument));
return this.pool[this.pool.length - 1];
}
WorkerPool.prototype.nextTask = function(){
var max_priority = 0;
var max_priority_task = this.pool.length;
for(var i = 0; i < this.pool.length; ++i){
if(this.pool[i].priority > max_priority && this.pool[i].queue.length !== 0){
max_priority = this.pool[i].priority;
max_priority_task = i;
}
}
// pool is empty or all tasks have an invalid priority
if(max_priority_task === this.pool.length)
return;
if(this.pool[max_priority_task].this_argument)
this.pool[max_priority_task].dequeue().apply(this.pool[max_priority_task].this_argument);
else
this.pool[max_priority_task].dequeue().apply();
if(this.status !== "running")
return;
this.timeout = setTimeout(function(t){return function(){t.nextTask();};}(this),1000);
}
var Workers = new WorkerPool();
var worker1 = Workers.addWorker();
worker1.enqueue(function(){
console.log("Hello");
});
worker1.enqueue(function(){
console.log("World");
});
var worker2 = Workers.addWorker();
worker2.priority = 2;
worker2.this_argument = worker2;
worker2.enqueue(function(){
console.log("Worker 2 - changing priority");
this.priority = .2;
});
worker2.enqueue(function(){
console.log("Worker 2 - after change");
});
Workers.nextTask();
Demo
In this case, every "heavy script" is a worker, which is basically a queue of tasks. You create a new worker in the pool by using addWorker and add tasks to the specific workers queue by using worker.enqueue(callback).

Loading external Javascript Sequentially

I am working on a javascript that sequentially loads a list of other external javascript.
The code I have so far:
function loadJavascript(url){
var js = document.createElement("script");
js.setAttribute("type", "text/javascript");
js.setAttribute("src", url);
if(typeof js!="undefined"){
document.getElementsByTagName("head")[0].appendChild(js)
}
}
loadJavascript("Jquery.js");
loadJavascript("second.js");
loadJavascript("third.js");
The problem I ran into is that sometimes the other js files loads before the Jquery file completes its loading. This gives me some errors.
Is it possible to make it so that the next JS file is only initiated when the previous file is finished loading.
Thanks in advance
Sure there is, but there's entire libraries written around doing this. Stop reinventing the wheel and use something that already works. Try out yepnope.js or if you're using Modernizr it's already available as Modernizr.load
loadJavascript("Jquery.js");
$(function(){
$.getScript('second.js', function(data, textStatus){
$.getScript('third.js', function(data, textStatus){
console.log("loaded");
});
});
}
Also, consider using the Google or Microsoft CDN for the jQuery, it will save you bandwidth and hopefully your visitors will already have it cached.
Actually, it's not necessary to load jquery within a js function. But if you insist, you can callback to make sure other js loaded after jquery.
Still, I recommend you load jquery just before </body> then use $.getScript to load other .js
You could do a check to see if jQuery is loaded, not the best way to do it, but if you really have to wait until jQuery is loaded before loading the other scripts, this is how I would do it, by checking for $ :
loadJavascript("Jquery.js");
T=0;
CheckIfLoaded();
function CheckIfLoaded() {
if (typeof $ == 'undefined') {
if (T <= 3000) {
alert("jQuery not loaded within 3 sec");
} else {
T=T+200;
setTimeout(CheckIfLoaded, 200);
} else {
loadJavascript("second.js");
loadJavascript("third.js");
}
}
In technical terms: Browsers have a funny way of deciding I which order to execute/eval dynamically loaded JS, so after suffering the same pain and checking a lot of posts, libraries, plugins, etc. I came up with this solution, self contained, small, no jquery needed, IE friendly, etc. The code is extensively commented:
lazyLoader = {
load: function (scripts) {
// The queue for the scripts to be loaded
lazyLoader.queue = scripts;
lazyLoader.pendingScripts = [];
// There will always be a script in the document, at least this very same script...
// ...this script will be used to identify available properties, thus assess correct way to proceed
var firstScript = document.scripts[0];
// We will loop thru the scripts on the queue
for (i = 0; i < lazyLoader.queue.length; ++i) {
// Evaluates if the async property is used by the browser
if ('async' in firstScript ) {
// Since src has to be defined after onreadystate change for IE, we organize all "element" steps together...
var element = document.createElement("script");
element.type = "text/javascript"
//... two more line of code than necessary but we add order and clarity
// Define async as false, thus the scripts order will be respected
element.async = false;
element.src = lazyLoader.queue[i];
document.head.appendChild(element);
}
// Somebody who hates developers invented IE, so we deal with it as follows:
// ... In IE<11 script objects (and other objects) have a property called readyState...
// ... check the script object has said property (readyState) ...
// ... if true, Bingo! We have and IE!
else if (firstScript.readyState) {
// How it works: IE will load the script even if not injected to the DOM...
// ... we create an event listener, we then inject the scripts in sequential order
// Create an script element
var element = document.createElement("script");
element.type = "text/javascript"
// Add the scripts from the queue to the pending list in order
lazyLoader.pendingScripts.push(element)
// Set an event listener for the script element
element.onreadystatechange = function() {
var pending;
// When the next script on the pending list has loaded proceed
if (lazyLoader.pendingScripts[0].readyState == "loaded" || lazyLoader.pendingScripts[0].readyState == "complete" ) {
// Remove the script we just loaded from the pending list
pending = lazyLoader.pendingScripts.shift()
// Clear the listener
element.onreadystatechange = null;
// Inject the script to the DOM, we don't use appendChild as it might break on IE
firstScript.parentNode.insertBefore(pending, firstScript);
}
}
// Once we have set the listener we set the script object's src
element.src = lazyLoader.queue[i];
}
}
}
}
Of course you can also use the minified version:
smallLoader={load:function(d){smallLoader.b=d;smallLoader.a=[];var b=document.scripts[0];for(i=0;i<smallLoader.b.length;++i)if("async"in b){var a=document.createElement("script");a.type="text/javascript";a.async=!1;a.src=smallLoader.b[i];document.head.appendChild(a)}else b.readyState&&(a=document.createElement("script"),a.type="text/javascript",smallLoader.a.push(a),a.onreadystatechange=function(){var c;if("loaded"==smallLoader.a[0].readyState||"complete"==smallLoader.a[0].readyState)c=smallLoader.a.shift(),
a.onreadystatechange=null,b.parentNode.insertBefore(c,b)},a.src=smallLoader.b[i])}};

Calling external javascript function through url bar

I want to use javascript in the url bar to manipulate the rendered html of a given page. Please note that I'm not trying to do something illegal here. Long story short, my university generates a weekly schedule based on your courses. I'd like to use javascript to add a button on the generated schedule page that will allow you to push the schedule to a google calendar. Unfortunately, I can't just go and edit the source itself (obviously), so I figured I would use javascript to edit the page once it has been rendered by my browser. I'm having some trouble calling an external javascript file to parse the rendered html.
As it is, this is what I have:
javascript:{{var e=document.createElement('script');
e.src = http://www.url.of/external/js/file.js';
e.type='text/javascript';
document.getElementsByTagName('head')[0].appendChild(e);}
functionToCall(document.body.innerHTML);}
Which, when pasted into the URL bar, SHOULD add my javascript file to the head and then call my function.
Any help would be greatly appreciated, thanks!
EDIT: Here's a working example if you're interested, thanks everyone!
javascript:(function(){var e=document.createElement('script');
e.src = 'http://www.somewebsite.net/file.js';
e.type='text/javascript';e.onload =function(){functiontocall();};
document.getElementsByTagName('head')[0].appendChild(e);})();
If you need the code to execute once it is loaded you can do one of two things:
Execute functionToCall(document.body.innerHTML); at the bottom of your script (http://www.url.of/external/js/file.js) rather than at the end of your bookmarklet.
Use e.onload = function(){ functionToCall(document.body.innerHTML); }; after e.type='text/javascript' near the end of your JavaScript snippet / bookmarklet, rather than calling functionToCall right after appending e to the document head (since e will most likely not have been loaded and parsed right after appendChild(e) is called.
I see that you've accepted an answer, and that's perfectly valid and great, but I would like to provide a useful tool I made for myself.
It's a bookmarklet generator called zbooks.
(Yes it's my website, no I'm not trying to spam you, there are no ads on that page, I gain nothing from you using it)
It's jQuery enabled and I think it's simple to use (but I built it, so who knows). If you need an extensive explanation of how to use it, let me know so I can make it better. You can even browse over the source if you'd like.
The important part is the business logic that gets jQuery on the page:
//s used for the Script element
var s = document.createElement('script');
//r used for the Ready state
var r = false;
//set the script to the latest version of jQuery
s.setAttribute('src', 'http://code.jquery.com/jquery-latest.min.js');
//set the load/readystate events
s.onload = s.onreadystatechange = function()
{
/**
* LOAD/READYSTATE LOGIC
* execute if the script hasn't been ready yet and:
* - the ready state isn't set
* - the ready state is complete
* - note: readyState == 'loaded' executes before the script gets called so
* we skip this event because it wouldn't have loaded the init event yet.
*/
if ( !r && (!this.readyState || this.readyState == 'complete' ) )
{
//set the ready flag to true to keep the event from initializing again
r = true;
//prevent jQuery conflicts by placing jQuery in the zbooks object
window.zbooks = {'jQuery':jQuery.noConflict()};
//make a new zbook
window.zbooks[n] = new zbooks(c);
}
};
//append the jQuery script to the body
b.appendChild(s);
Can't you use a proper tool like Greasemonkey?

Categories