Call vue-html-to-paper print function on a new window - vue.js

I have a Vue component I'd like to print. When the user presses a print button, a function is called which opens the component in a new window to isolate the HTML, and get the formatting right.
async openPrintDetailsDialogue() {
const prtHtml = document.getElementById('printable').innerHTML;
let stylesHtml = '';
for (const node of [...document.querySelectorAll('link[rel="stylesheet"], style')]) {
stylesHtml += node.outerHTML;
}
var newWindow = window.open('', '', 'left=0,top=0,width=800,height=900,toolbar=0,scrollbars=0,status=0');
newWindow.document.write(`<!DOCTYPE html>
<html>
<head>
${stylesHtml}
</head>
<body id="printme">
${prtHtml}
</body>
</html>`);
newWindow.document.close();
newWindow.focus();
...
I then try to print using $htmlToPaper.
...
await this.$htmlToPaper('printme');
newWindow.close();
}
However, the main window alerts Element to print #printme not found!.
I add the plugin VueHtmlToPaper in my mounted() function:
mounted() {
Vue.use(VueHtmlToPaper);
}
I've already tried passing my existing styles.css to the $htmlToPaper() call options, which changed nothing. I also have some styles in my vue file's <style scoped>, which couldn't be included in the options parameter either.
How can I get VueHtmlToPaper to "point" to newWindow?

By opening a new window you have a new separate webpage, where is no Vue Js instance. It does not work so and you will not make it work this way. You should use a Modal, change the current page or make a new page with Vue instance for printing.

Related

How to embed codepen as HTML in vue.js

I can't figure out how to embed a codepen using the recommended HTML method i a Vue application.
As <script> tag cannot be part of a Vue component template, I tried to add it to index.html where my Vue application is injected without luck. However, when I tried to paste the html code outside the div where Vue resides, the code got turned into an iFrame as it should.
Here is the HTML embed:
<p data-height="265" data-theme-id="0" data-slug-hash="JyxKMg" data-default-tab="js,result" data-user="sindael" data-embed-version="2" data-pen-title="Fullscreen image gallery using Wallop, Greensock and Flexbox" class="codepen">See the Pen Fullscreen image gallery using Wallop, Greensock and Flexbox by Dan (#sindael) on CodePen.</p>
And the script:
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>
Embedding an iFrame directly works fine, but I wonder. Is there a way how to get the html working?
Look into the https://static.codepen.io/assets/embed/ei.js, then you will see it executes two steps:
check document.getElementsByClassName if exists, create it if not.
one IIFE to execute the embed.
So one hacky way as below simple demo:
copy the source codes from https://static.codepen.io/assets/embed/ei.js
copy the codes of first step then wrap it as one function = _codepen_selector_contructor
copy the codes of second step and remove () from the end, then wrap it as one function = _codepen_embed_method
create one vue-directive (I prefer using the directive to support the features which directly process Dom elements, you can use other solutions), then execute _codepen_selector_contructor and _codepen_embed_method
Probably you want to replace document inside _codepen_embed_method with el instead, then execute _codepen_embed_method(el). so that it will not affects other elements.
Below demo uses the hook='inserted', you could use other hooks if inserted can't meet your requirements.
let vCodePen = {}
vCodePen.install = function install (Vue) {//copy from https://static.codepen.io/assets/embed/ei.js
let _codepen_selector_contructor = function () {
document.getElementsByClassName||(document.getElementsByClassName=function(e){var n,t,r,a=document,o=[];if(a.querySelectorAll)return a.querySelectorAll("."+e);if(a.evaluate)for(t=".//*[contains(concat(' ', #class, ' '), ' "+e+" ')]",n=a.evaluate(t,a,null,0,null);r=n.iterateNext();)o.push(r);else for(n=a.getElementsByTagName("*"),t=new RegExp("(^|\\s)"+e+"(\\s|$)"),r=0;r<n.length;r++)t.test(n[r].className)&&o.push(n[r]);return o})
}
let _codepen_embed_method = //copy from https://static.codepen.io/assets/embed/ei.js then removed `()` from the end
function(){function e(){function e(){for(var e=document.getElementsByClassName("codepen"),t=e.length-1;t>-1;t--){var u=a(e[t]);if(0!==Object.keys(u).length&&(u=o(u),u.user=n(u,e[t]),r(u))){var c=i(u),l=s(u,c);f(e[t],l)}}m()}function n(e,n){if("string"==typeof e.user)return e.user;for(var t=0,r=n.children.length;t<r;t++){var a=n.children[t],o=a.href||"",i=o.match(/codepen\.(io|dev)\/(\w+)\/pen\//i);if(i)return i[2]}return"anon"}function r(e){return e["slug-hash"]}function a(e){for(var n={},t=e.attributes,r=0,a=t.length;r<a;r++){var o=t[r].name;0===o.indexOf("data-")&&(n[o.replace("data-","")]=t[r].value)}return n}function o(e){return e.href&&(e["slug-hash"]=e.href),e.type&&(e["default-tab"]=e.type),e.safe&&("true"===e.safe?e.animations="run":e.animations="stop-after-5"),e}function i(e){var n=u(e),t=e.user?e.user:"anon",r="?"+l(e),a=e.preview&&"true"===e.preview?"embed/preview":"embed",o=[n,t,a,e["slug-hash"]+r].join("/");return o.replace(/\/\//g,"//")}function u(e){return e.host?c(e.host):"file:"===document.location.protocol?"https://codepen.io":"//codepen.io"}function c(e){return e.match(/^\/\//)||!e.match(/https?:/)?document.location.protocol+"//"+e:e}function l(e){var n="";for(var t in e)""!==n&&(n+="&"),n+=t+"="+encodeURIComponent(e[t]);return n}function s(e,n){var r;e["pen-title"]?r=e["pen-title"]:(r="CodePen Embed "+t,t++);var a={id:"cp_embed_"+e["slug-hash"].replace("/","_"),src:n,scrolling:"no",frameborder:"0",height:d(e),allowTransparency:"true",allowfullscreen:"true",allowpaymentrequest:"true",name:"CodePen Embed",title:r,"class":"cp_embed_iframe "+(e["class"]?e["class"]:""),style:"width: "+p+"; overflow: hidden;"},o="<iframe ";for(var i in a)o+=i+'="'+a[i]+'" ';return o+="></iframe>"}function d(e){return e.height?e.height:300}function f(e,n){if(e.parentNode){var t=document.createElement("div");t.className="cp_embed_wrapper",t.innerHTML=n,e.parentNode.replaceChild(t,e)}else e.innerHTML=n}function m(){"function"==typeof __CodePenIFrameAddedToPage&&__CodePenIFrameAddedToPage()}var p="100%";e()}function n(e){/in/.test(document.readyState)?setTimeout("window.__cp_domReady("+e+")",9):e()}var t=1;window.__cp_domReady=n,window.__CPEmbed=e,n(function(){new __CPEmbed})}
let defaultProps = {class: 'codepen', 'data-height':265, 'data-theme-id':0, 'data-slug-hash':'', 'data-default-tab':'js,result', 'data-user':'sindael', 'data-embed-version':'2', 'data-pen-title':''}
Vue.directive('code-pen', {
inserted: function (el, binding, vnode) {
let options = Object.assign({}, defaultProps, binding.value)
Object.entries(options).forEach((item) => {
el.setAttribute(item[0], item[1])
})
setTimeout(() => {
_codepen_selector_contructor()
_codepen_embed_method() //_codepen_embed_method(el); you can pass el to take place of `document`
}, 100)
},
componentUpdated: function (el, binding, vnode) {
}
})
}
Vue.use(vCodePen)
Vue.config.productionTip = false
app = new Vue({
el: "#app",
data: {
keyword: '',
},
mounted: function () {
},
methods: {
}
})
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<p v-code-pen="{class: 'codepen', 'data-height':'265', 'data-theme-id':0, 'data-slug-hash':'JyxKMg', 'data-default-tab':'js,result', 'data-user':'sindael', 'data-embed-version':'2', 'data-pen-title':'Test'}">
</p>
</div>

Riot JS unmount all tags in a page and then mount only one tag is not working

I am using Riot JS and in my index.html, I have 3 custom tags - header, login-panel and candidates-panel inside my body. In my main app.js, in the callback function of $(document).ready, I execute the current route and also register a route change handler function. In my switchView, I unmount all custom tags and then try to mount only the tag pertaining to the current view being switched. Here is my code. If I do unmount, then nothing is displayed on the page
index.html
<body>
<header label="Hire Zen" icon="img/user-8-32.png"></header>
<login-panel class="viewTag" id="loginView"></login-panel>
<candidates-panel id="candidatesView" class="viewTag"></candidates-panel>
<script src="js/bundle.js"></script>
</body>
app.js
function switchView(view) {
if(!view || view === '') {
view = 'login'
}
//unmount all other panels and mount only the panel that is required
//TODO: unmount all view panels and mounting only required panel is not working
//riot.unmount('.viewTag')
riot.mount(view+'-panel')
$('.viewTag').hide()
$(view+'-panel').show()
}
$(document).ready(function () {
RiotControl.addStore(new AuthStore())
RiotControl.addStore(new CandidatesStore())
riot.mount('header')
//register route change handler
riot.route(function (collection, id, action) {
switchView(collection)
})
riot.route.exec(function (collection, id, action) {
switchView(collection)
})
})
Answer for riot.js v2.1.0:
The function
riot.unmount(...)
is not available as far as I know. However, you can unmount saved tags.
mytag.unmount(true)
Source
The trick is to remember the mounted tags to be able to unmount them later:
var viewTag = riot.mount(document.getElementById('viewTag'))
viewTag.unmount(true)
You can store all those view tags in an object and loop them to unmount all and mount only the active one.
Source
Answer for 2.3.18
Based on the previous answer and this tutorial I have created following concept:
app.currentPage = null;
var goTo = function(page){
if (app.currentPage) {
app.currentPage.unmount(true); //unmount and keep parent tag
}
app.currentPage = riot.mount(page)[0]; //remember current page
};
riot.route(function() {
console.info("this page is not defined");
//do nothing (alternatively go to 404 page or home)
});
riot.route('/inventory', function(){
goTo('inventory');
});
riot.route('/options', function() {
goTo('options');
});
I think you are looking for riot.util.tags.unmountAll(tags)
How to achieve the goal?
index.html
var tags = [];
some.tag.html
var some = this;
tags.push(some);
unmountAllTags.js
riot.util.tags.unmountAll(tags);

How to show the compiled css from a .less file in the browser?

What is the best way to show the resulting css from files compiled with less.js in the client.
In other words, how can i fill a div with the resulting css?
I need to display the result on the page, any way to do this?
THanks!
update
As already pointed out in the comments by #ertrzyiks you should replace less.parse with less.render for Less v 2.x:
var lessCode = '';
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function(){
if(xmlhttp.status == 200 && xmlhttp.readyState == 4){
var options = {}
lessCode = xmlhttp.responseText;
less.render(lessCode, options, function (error, output) {
if(!error) {
document.getElementById('lesscode').innerHTML = output.css;
}
else document.getElementById('lesscode').innerHTML = '<span style="color:red">' + error + '</span>';
});
}
};
xmlhttp.open("GET","important.less",true);
xmlhttp.send();
see also: How to detect and print changing variables LESS
But since Less v2:
In the browser, less.pageLoadFinished will be a promise, resolved when
less has finished its initial processing. less.refresh and
less.modifyVars also return promises.
When you compile filename.less the compiled CSS code has been inject in a style tag with id less:filename, so to get the compilled CSS code you can also use:
less.pageLoadFinished.then(
function() {
console.log(document.getElementById('less:filename').innerHTML);
}
);
Notice that the last example also applies the compiled CSS code on the page.
--end update
I expected that running something such as the following was possible:
<link rel="stylesheet/less" type="text/css" href="important.less">
<script src="less-1.7.3.js" type="text/javascript"></script>
<script>
css = less.tree.toCSS();
console.log(css);
</script>
unfortunately this does not work, but you can use the following code to get what you want:
<script src="less-1.7.3.js" type="text/javascript"></script>
<script>
var lessCode = '';
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function(){
if(xmlhttp.status == 200 && xmlhttp.readyState == 4){
lessCode = xmlhttp.responseText;
new(less.Parser)().parse(lessCode, function (e, tree) {
document.getElementById('lesscode').innerHTML = tree.toCSS().replace(/\n/g,"<br>");
});
}
};
xmlhttp.open("GET","important.less",true);
xmlhttp.send();
</script>
With in the body section of your HTML:
<div id="lesscode"></div>
See also: Combining two .less files in one and How to open a local disk file with Javascript?
I just use Chrome's Inspect Element.
Right click on the element CSS you are looking for, Right click and choose Inspect element. On the right you will find the compiled CSS in Styles. Hope it helps
You have two options to do this, Internet Explorer or Firefox.
Let's start with Firefox. If you install the web developer toolbar, you get a menu option that's labelled CSS. Clicking on this gives you a few options and if you choose View CSS, you are taken to a new tab that shows you all of the styles for the page, grouped by their location and you should see a section with the CSS that has been generated by LESS and dynamically applied to the elements.
IE also has a Web Developer option and if you use the toolbar to inspect an element, you can then use the short cut 'Ctrl + T' which will bring up the page source with the computed styles.
The Firefox solution is better, as you can see exactly which styles have been provided by LESS whereas IE just lumps it all together.
There is a third option, and that is to compile the CSS server side!

jCaptcha - Refresh only image not whole page

I'm using jCaptcha (http://jcaptcha.sourceforge.net/) on our website. The problem is sometimes it's very difficult to read the image. So, we are planning to provide a button named 'REFRESH' next to the jcaptcha image and upon clicking REFRESH button, it has to refresh only the jcaptcha image not the entire page/portlet. How can we do that?
This is how I solved it using JQuery, it will replace the image. The alert() is just there to show off the new filename and can of course be removed. The code is using the jquery plugin in grails but shows what to do in jquery to refresh the image.
<div>
<jcaptcha:jpeg name="captchaImage"/>
Refresh captcha
<jq:jquery>
$("#refreshCaptcha").click(function() {
$("#captchaImage").fadeOut(500, function() {
var captchaURL = $("#captchaImage").attr("src");
captchaURL = captchaURL.replace(captchaURL.substring(captchaURL.indexOf("=")+1, captchaURL.length), Math.floor(Math.random()*9999999999));
alert(captchaURL);
$("#captchaImage").attr("src", captchaURL);
});
$("#captchaImage").fadeIn(300);
});
</jq:jquery>
</div>
Make this changes in JSP :
<img src="jcaptcha" id="captcha_image"/> Refresh
Add the Javascript function like :
function reloadCaptcha(){
var d = new Date();
$("#captcha_image").attr("src", "jcaptcha?"+d.getTime());
}
You would have to load the image and the refresh button into . Than you should be able to refresh just the iframe. But the I don't know how you are performing your validation so.
Set an id for the img tag and let it call a javascript function:
<img src="jcaptcha.jpg" id="captchaImage"/>
javascript function:
<script type="text/javascript">
function refresh()
{
var captchaImage=document.getElementById("captchaImage");
captchaImage.src="jcaptcha.jpg";
}
</script>
this works fine because i implemented this one in my project just create one button on clicking that button it will come to below menctiond block of code like that you do
<script type="text/javascript">
function refresh()
{
var image=document.getElementById("kaptchaImage");
image.src="<%=request.getContextPath()%>/kaptcha.jpg?"+Math.floor(Math.random()*100)
}
</script>

Dispatching events from webkit for consumption in Adobe AIR

I have a webkit HTML component in my AIR application, and would like to be able to respond to events such as onclick and ondoubleclick generated from the HTML in the webkit component. Is there any way to accomplish this?
There is, although it took me a little while to find it.
This should serve as a pretty good starting off point: http://livedocs.adobe.com/flex/3/html/help.html?content=ProgrammingHTMLAndJavaScript_04.html
Here's the key code:
var html:HTMLLoader = new HTMLLoader();
var foo:String = "Hello from container SWF."
function helloFromJS(message:String):void {
trace("JavaScript says:", message);
}
var urlReq:URLRequest = new URLRequest("test.html");
html.addEventListener(Event.COMPLETE, loaded);
html.load(urlReq);
function loaded(e:Event):void{
html.window.foo = foo;
html.window.helloFromJS = helloFromJS;
}
The HTML content (in a file named test.html) loaded into the HTMLLoader object in the previous example can access the foo property and the helloFromJS() method defined in the parent SWF file:
<html>
<script>
function alertFoo() {
alert(foo);
}
</script>
<body>
<button onClick="alertFoo()">
What is foo?
</button>
<p><button onClick="helloFromJS('Hi.')">
Call helloFromJS() function.
</button></p>
</body>
</html>