wkhtmltopdf exclude css files from PDF but show in HTML - wkhtmltopdf

I was working with wkhmtltopdf and found that I have a problem with it.
I am using the html page to also preview the content in a browser prior to converting to PDF.
But to make it more readable in my preview, I have added some css on the page: (In javascript/jQuery on document ready)
$(document).ready(function () {
if(window.location.href.indexOf("preview") > -1 && window.location.href.indexOf(".pdf") == -1) {
appendCSStoHtml();
}
});
function appendCSStoHtml() {
$("body").css({"background-color":" #F0F8FF"});
$("#previewHtml").css({
"margin-left":"15%",
"margin-right":"15%",
"margin-top":"15px",
"max-width":"1024px"});
$("#previewHtml > h2").css({"margin-left":" 15px"});
}
So this is all good and dandy, but when I generate my PDF based on this HTML page, then the css is also renderend inside my PDF and the outlining and background are also present inside the PDF.
I don't want this to happen, I only want my css style elements to be renderen on the HTML page and not inside the PDF.
How can I achieve this?
I tried with the following:
$(document).ready(function () {
if(window.location.href.indexOf("preview") > -1 && window.location.href.indexOf(".pdf") == -1) {
appendCSStoHtml();
}
});
But that did not matter.
Any ideas?

it's me again.
Not sure if there is something that resembles what I am looking for.
Thus I made my own.
I added a page parameter and check this in the code to see if it's true or false:
<script type="text/javascript">
$(document).ready(function () {
var url_string = window.location.href;
var url = new URL(url_string);
var renderCss = url.searchParams.get("renderCss");
if(renderCss == 'true') {
appendCSStoHtml();
}
});
function appendCSStoHtml() {
$("body").css({"background-color":" #F0F8FF"});
$("#previewHtml").css({
"margin-left":"15%",
"margin-right":"15%",
"margin-top":"15px",
"max-width":"1024px"});
$("#previewHtml > h2").css({"margin-left":" 15px"});
}
</script>

Related

Rich Text Editor (WYSIWYG) in CRM 2013

Sometimes it is useful to have the HTML editor in CRM interface. It is possible to implement the editor directly to CRM 2013. As editor we will use ckeditor which allows to use it without installation on the server.
Identify the field where you would like to use the rich text editor.
Create html-webresource which will define ckeditor. Go to Settings-Customizations-Customize the System-Web Resources.
In html editor of web resource, select the Source tab and insert the following code:
<html>
<head>
<title></title>
<script src="//cdn.ckeditor.com/4.4.7/standard/ckeditor.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript">
function getTextFieldName()
{
var vals = new Array();
if (location.search != "")
{
vals = location.search.substr(1).split("&");
for (var i in vals)
{
vals[i] = vals[i].replace(/\+/g, " ").split("=");
}
//look for the parameter named 'data'
for (var i in vals)
{
if (vals[i][0].toLowerCase() == "data")
{
var datavalue = vals[i][2];
var vals2 = new Array();
var textFieldName = "";
vals2 = decodeURIComponent(datavalue).split("&");
for (var i in vals2)
{
var queryParam = vals2[i].replace(/\+/g, " ").split("=");
if (queryParam[0] != null && queryParam[0].toLowerCase() == "datafieldname")
{
textFieldName = queryParam[1];
}
}
if (textFieldName == "")
{
alert('No "dataFieldName" parameter has been passed to Rich Text Box Editor.');
return null;
}
else
{
return textFieldName;
}
}
else
{
alert('No data parameter has been passed to Rich Text Box Editor.');
}
}
}
else
{
alert('No data parameter has been passed to Rich Text Box Editor.');
}
return null;
}
CKEDITOR.timestamp = null;
​// Maximize the editor window, i.e. it will be stretched to target field
CKEDITOR.on('instanceReady',
function( evt )
{
var editor = evt.editor;
editor.execCommand('maximize');
});
var Xrm;
$(document).ready(function ()
{
​ // Get the target field name from query string
var fieldName = getTextFieldName();
var Xrm = parent.Xrm;
var data = Xrm.Page.getAttribute(fieldName).getValue();
document.getElementById('editor1').value = data;
/*
// Uncomment only if you would like to update original field on lost focus instead of property change in editor
//Update textbox on lost focus
CKEDITOR.instances.editor1.on('blur', function ()
{
var value = CKEDITOR.instances.editor1.getData();
Xrm.Page.getAttribute(fieldName).setValue(value);
});
*/
// Update textbox on change in editor
CKEDITOR.instances.editor1.on('change', function ()
{
var value = CKEDITOR.instances.editor1.getData();
Xrm.Page.getAttribute(fieldName).setValue(value);
});
// Following settings define that the editor allows whole HTML content without removing tags like head, style etc.
CKEDITOR.config.allowedContent = true;
CKEDITOR.config.fullPage = true;
});
</script>
<meta>
</head>
<body style="word-wrap: break-word;">
<textarea class="ckeditor" cols="80" id="editor1" name="editor1" rows="10"></textarea>
</body>
</html>
Note:
As you can see, there are a few important sections
a) The following code loads the ckeditor and jquery from web so that they don't have to be installed on server.
<script src="//cdn.ckeditor.com/4.4.7/standard/ckeditor.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
b) Function getTextFieldName() which gets the name of target field where should be rich text editor placed. This information is obtained from query string. This will allow to use this web resource on multiple forms.
c) Initialization of ckeditor itself - setting the target field and properties of ckeditor. Also binding the editor with predefined textarea on the page.
Open the form designer on the form where you would like to use ​WYSIWYG editor. Create a text field with sufficient length (e.g. 100 000 chars) which will hold the html source code.
Insert the iframe on the form. As a webresource use the resource created in previous steps. Also define Custom Parameter(data) where you should define the name of the text field defined in step 4. In our situation we created new_bodyhtml text field so the parameter holds this value. This value is returned by the getTextFieldName() of the web resource.
Do not forget to save and publish all changes in CRM customization otherwise added webresources and updated form are not available.
That's all, here is example how it looks like:
Yes, you can use CKEditor, but when I implemented the CKEditor on a form, it turns out it is quite limited in the functionality in provides. Also, the HTML it generates leaves much to be desired. So, I tried a similar idea to Pavel's but using a backing field to store the actual HTML using TinyMCE. You can find the code here:
Javascript
HTML
Documentation
I have package my solution as a managed and unmanaged CRM solution that can be imported and utilised on any form. Moreover, it works on both CRM 2013 and CRM 2015

Twitter Bootstrap 3 dropdown menu disappears when used with prototype.js

I have an issue when using bootstrap 3 & prototype.js together on a magento website.
Basically if you click on the dropdown menu (Our Products) & then click on the background, the dropdown menu (Our Products) disappears (prototype.js adds "display: none;" to the li).
Here is a demo of the issue:
http://ridge.mydevelopmentserver.com/contact.html
You can see that the dropdown menu works like it should without including prototype.js on the page at the link below:
http://ridge.mydevelopmentserver.com/
Has anyone else ran into this issue before or have a possible solution for the conflict?
EASY FIX:
Just replace Magento's prototype.js file with this bootstrap friendly one:
https://raw.github.com/zikula/core/079df47e7c1f536a0d9eea2993ae19768e1f0554/src/javascript/ajax/original_uncompressed/prototype.js
You can see the changes made in the prototype.js file to fix the bootstrap issue here:
https://github.com/zikula/core/commit/079df47e7c1f536a0d9eea2993ae19768e1f0554
NOTE: JQuery must be include in your magento skin before prototype.js.. Example:
<script type="text/javascript" src="/js/jquery.js"></script>
<script type="text/javascript" src="/js/prototype/prototype.js"></script>
<script type="text/javascript" src="/js/lib/ccard.js"></script>
<script type="text/javascript" src="/js/prototype/validation.js"></script>
<script type="text/javascript" src="/js/scriptaculous/builder.js"></script>
<script type="text/javascript" src="/js/scriptaculous/effects.js"></script>
<script type="text/javascript" src="/js/scriptaculous/dragdrop.js"></script>
<script type="text/javascript" src="/js/scriptaculous/controls.js"></script>
<script type="text/javascript" src="/js/scriptaculous/slider.js"></script>
<script type="text/javascript" src="/js/varien/js.js"></script>
<script type="text/javascript" src="/js/varien/form.js"></script>
<script type="text/javascript" src="/js/varien/menu.js"></script>
<script type="text/javascript" src="/js/mage/translate.js"></script>
<script type="text/javascript" src="/js/mage/cookies.js"></script>
<script type="text/javascript" src="/js/mage/captcha.js"></script>
I've also used code from here: http://kk-medienreich.at/techblog/magento-bootstrap-integration-mit-prototype-framework but without a need to modify any source. Just put code below somewhere after prototype and jquery includes:
(function() {
var isBootstrapEvent = false;
if (window.jQuery) {
var all = jQuery('*');
jQuery.each(['hide.bs.dropdown',
'hide.bs.collapse',
'hide.bs.modal',
'hide.bs.tooltip',
'hide.bs.popover',
'hide.bs.tab'], function(index, eventName) {
all.on(eventName, function( event ) {
isBootstrapEvent = true;
});
});
}
var originalHide = Element.hide;
Element.addMethods({
hide: function(element) {
if(isBootstrapEvent) {
isBootstrapEvent = false;
return element;
}
return originalHide(element);
}
});
})();
Late to the party, but found this github issue which links to this informational page which links to this jsfiddle which works really nicely. It doesn't patch on every jQuery selector and is, I think, the nicest fix by far. Copying the code here to help future peoples:
jQuery.noConflict();
if (Prototype.BrowserFeatures.ElementExtensions) {
var pluginsToDisable = ['collapse', 'dropdown', 'modal', 'tooltip', 'popover'];
var disablePrototypeJS = function (method, pluginsToDisable) {
var handler = function (event) {
event.target[method] = undefined;
setTimeout(function () {
delete event.target[method];
}, 0);
};
pluginsToDisable.each(function (plugin) {
jQuery(window).on(method + '.bs.' + plugin, handler);
});
};
disablePrototypeJS('show', pluginsToDisable);
disablePrototypeJS('hide', pluginsToDisable);
}
Using the * selector with jQuery is not advised. This takes every DOM object on the page and puts it in the variable.
I would advice to select the elements that use a Bootstrap component specific. Solution below only uses the dropdown component:
(function() {
var isBootstrapEvent = false;
if (window.jQuery) {
var all = jQuery('.dropdown');
jQuery.each(['hide.bs.dropdown'], function(index, eventName) {
all.on(eventName, function( event ) {
isBootstrapEvent = true;
});
});
}
var originalHide = Element.hide;
Element.addMethods({
hide: function(element) {
if(isBootstrapEvent) {
isBootstrapEvent = false;
return element;
}
return originalHide(element);
}
});
})();
Very late to the party: if you don't feel like having extra scripts running, you can add a simple CSS override to prevent it from getting hidden.
.dropdown {
display: inherit !important;
}
Generally the use of !important in CSS is advised against, but I think this counts as an acceptable use in my opinion.
see http://kk-medienreich.at/techblog/magento-bootstrap-integration-mit-prototype-framework/.
It's quite an easy fix to validate the namespace of the element clicked.
Add a validation function to prototype.js:
and after that, validate the namespace before the element will be hidden:
hide: function(element) {
element = $(element);
if(!isBootstrapEvent)
{
element.style.display = 'none';
}
return element;
},
Replacing Magento's prototype.js file with the bootstrap friendly version suggested by MWD is throwing an error that prevents saving configurable products:
Uncaught TypeError: Object [object Array] has no method 'gsub' prototype.js:5826
(Running Magento Magento 1.7.0.2)
evgeny.myasishchev solution worked great.
(function() {
var isBootstrapEvent = false;
if (window.jQuery) {
var all = jQuery('*');
jQuery.each(['hide.bs.dropdown',
'hide.bs.collapse',
'hide.bs.modal',
'hide.bs.tooltip'], function(index, eventName) {
all.on(eventName, function( event ) {
isBootstrapEvent = true;
});
});
}
var originalHide = Element.hide;
Element.addMethods({
hide: function(element) {
if(isBootstrapEvent) {
isBootstrapEvent = false;
return element;
}
return originalHide(element);
}
});
})();
This answer helped me to get rid of bootstrap and prototype conflict issue.
As #GeekNum88 describe the matter,
PrototypeJS adds methods to the Element prototype so when jQuery tries
to trigger the hide() method on an element it is actually firing the
PrototypeJS hide() method, which is equivalent to the jQuery
hide() method and sets the style of the element to display:none;
As you suggest in the question itself either you can use bootstrap friendly prototype or else you can simply comment out few lines in bootstrap as bellow,
inside the Tooltip.prototype.hide function
this.$element.trigger(e)
if (e.isDefaultPrevented()) return
I realise that this is a pretty old post by now, but an answer that no-one else seems to have mentioned is simply "modify jQuery". You just need a couple of extra checks in jQuery's trigger function which can be found around line 332.
Extend this if statement:
// Call a native DOM method on the target with the same name name as the event.
// Don't do default actions on window, that's where global variables be (#6170)
if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) {
... to say this:
// Call a native DOM method on the target with the same name name as the event.
// Don't do default actions on window, that's where global variables be (#6170)
// Check for global Element variable (PrototypeJS) and ensure we're not triggering one of its methods.
if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) &&
( !window.Element || !jQuery.isFunction( window.Element[ type ] ) ) ) {
"#6170" only seems to be mentioned in jQuery once so you can do a quick check for that string if you're working with a compiled (complete) jQuery library.

CKEDITOR - how to add permanent onclick event?

I am looking for a way to make the CKEDITOR wysiwyg content interactive. This means for example adding some onclick events to the specific elements. I can do something like this:
CKEDITOR.instances['editor1'].document.getById('someid').setAttribute('onclick','alert("blabla")');
After processing this action it works nice. But consequently if I change the mode to source-mode and then return to wysiwyg-mode, the javascript won't run. The onclick action is still visible in the source-mode, but is not rendered inside the textarea element.
However, it is interesting, that this works fine everytime:
CKEDITOR.instances['editor1'].document.getById('id1').setAttribute('style','cursor: pointer;');
And it is also not rendered inside the textarea element! How is it possible? What is the best way to work with onclick and onmouse events of CKEDITOR content elements?
I tried manually write this by the source-mode:
<html>
<head>
<title></title>
</head>
<body>
<p>
This is some <strong id="id1" onclick="alert('blabla');" style="cursor: pointer;">sample text</strong>. You are using CKEditor.</p>
</body>
</html>
And the Javascript (onclick action) does not work. Any ideas?
Thanks a lot!
My final solution:
editor.on('contentDom', function() {
var elements = editor.document.getElementsByTag('span');
for (var i = 0, c = elements.count(); i < c; i++) {
var e = new CKEDITOR.dom.element(elements.$.item(i));
if (hasSemanticAttribute(e)) {
// leve tlacitko mysi - obsluha
e.on('click', function() {
if (this.getAttribute('class') === marked) {
if (editor.document.$.getElementsByClassName(marked_unique).length > 0) {
this.removeAttribute('class');
} else {
this.setAttribute('class', marked_unique);
}
} else if (this.getAttribute('class') === marked_unique) {
this.removeAttribute('class');
} else {
this.setAttribute('class', marked);
}
});
}
}
});
Filtering (only CKEditor >=4.1)
This attribute is removed because CKEditor does not allow it. This filtering system is called Advanced Content Filter and you can read about it here:
http://ckeditor.com/blog/Upgrading-to-CKEditor-4.1
http://ckeditor.com/blog/CKEditor-4.1-Released
Advanced Content Filter guide
In short - you need to configure editor to allow onclick attributes on some elements. For example:
CKEDITOR.replace( 'editor1', {
extraAllowedContent: 'strong[onclick]'
} );
Read more here: config.extraAllowedContent.
on* attributes inside CKEditor
CKEditor encodes on* attributes when loading content so they are not breaking editing features. For example, onmouseout becomes data-cke-pa-onmouseout inside editor and then, when getting data from editor, this attributes is decoded.
There's no configuration option for this, because it wouldn't make sense.
Note: As you're setting attribute for element inside editor's editable element, you should set it to the protected format:
element.setAttribute( 'data-cke-pa-onclick', 'alert("blabla")' );
Clickable elements in CKEditor
If you want to observe click events in editor, then this is the correct solution:
editor.on( 'contentDom', function() {
var element = editor.document.getById( 'foo' );
editor.editable().attachListener( element, 'click', function( evt ) {
// ...
// Get the event target (needed for events delegation).
evt.data.getTarget();
} );
} );
Check the documentation for editor#contentDom event which is very important in such cases.

Can Dojo .ajax load image?

There is a similar question for Jquery here:
Asynchronously load images with jQuery
The accepted answer is quite clever and works well (for Jquery):
var img = $("<img />").attr('src', 'http://somedomain.com/image.jpg')
.load(function() {
if (!this.complete || typeof this.naturalWidth == "undefined" || this.naturalWidth == 0) {
alert('broken image!');
} else {
$("#something").append(img);
}
});
My question is, what would be the canonical equivlent to this in Dojo (including the error handling)?
You can use "dojox.form.Uploader" to ajax upload image.
Dojo code:-
require(['dojox/form/Uploader'],function(Uploader)
{
var uploader = new Uploader(
{
uploadOnSelect:true, //Upload file on file select
url:'UploadFile.php', //Path of your server file
label:'', //Label of uploader
multiple:true //Select multiple file to upload
},'uploader');
uploader.startup();
});
HTML code:-
<div id="uploader"></div>
"dojox.form.Uploader" provide various properties, method and event which is help to upload file.
I hope this will help you.

Ajax file upload is not working when used the second time

I'm using this jquery plugin ajaxFileupload in our project. My design is I have a file upload control and set the opacity to 0.01 and then using an anchor link, I trigger the file upload control click event. This works fine until I try to click the anchor link the second time which it doesn't open the file dialog box.
Here is my code.
$(".btnUpload").live("click", function () {
$(".lblUploadError").text("");
$(".fleAttachment").trigger("click");
});
$(".fleAttachment").change(function () {
var reg = /^.*\.(jpg|JPG|gif|GIF|jpeg|JPEG)$/;
var vals = $(this).val(),
val = vals.length ? vals.split("\\").pop() : "";
if (reg.test(vals) == false) {
$(".lblUploadError").text("Invalid Image Type. We only accept .GIF or .JPG");
} else {
ajaxFileUpload();
eval($(".btnRefreshAttachmentList").attr("href"));
}
});
I don't see any error in the console so it makes it difficult to debug it.
Change
$(".fleAttachment").change(function() {
to
$(".fleAttachment").live('change', function() {
$( document ).on( "click", ".fleAttachment", function() {
//--> Logic Here // jQuery 1.7+
});
this.value="";
at the end should work

Resources