I'm trying to take a div and basically put it in a child window. The domains are going to be the same so I figure I could do the flowing ( where parm win is the div ).
popOut = function ( win )
{
var newWindow = window.open( "about:blank", "", "toolbar=yes, scrollbars=yes, resizable=yes, top=500, left=500, width=400, height=400", false );
newWindow.onload = function ()
{
this.focus
this.document.body.appendChild( win );
};
}
This seems to do the job but the css is clobbered. In thinking about this, the new window certainly knows nothing of the includes and css files. Can I copy or inject this info over somehow?
Would I be correct in assuming the new window knows nothing of its parent, thus no function can be ran from child to parent?
New window is definitely aware of its opener as well as opener aware of the popup. If the problem is new window not seeing opener styles, the following code should copy styles from opener to popup.
Place this after var newWindow = window.open(...)
var linkrels = document.getElementsByTagName('link');
for (var i = 0, max = linkrels.length; i < max; i++) {
if (linkrels[i].rel && linkrels[i].rel == 'stylesheet') {
var thestyle = document.createElement('link');
var attrib = linkrels[i].attributes;
for (var j = 0, attribmax = attrib.length; j < attribmax; j++) {
thestyle.setAttribute(attrib[j].nodeName, attrib[j].nodeValue);
}
newWindow.document.documentElement.appendChild(thestyle);
}
}
I haven't got a chance to test it, but something like this should work.
Related
I'm having a problem with TinyMCE 4.6. I've implemented a custom button that bumps the font size of selected text:
ed.addButton('finc', {
image: '/tinymce/plugins/zackel/button_images/big.png',
title: '+ font size',
id : 'finc',
onclick:function(editor,url) {
console.log("************ In finc: ", ed);
var delta;
var currentFontSize = new Number($(ed.selection.getNode()).css('font-size').replace('px',''));
console.log("************ finc: currentFontSize = " + currentFontSize);
var node = ed.selection.getNode(); // <======= LINE 565
var nodeName = node.nodeName; // for example 'DIV ' or 'P'
console.log("************ finc: node is ", node, "nodeName = " + nodeName);
if (currentFontSize >= 24) {
delta = 2;
}
else {
delta = 1;
}
currentFontSize = currentFontSize + delta;
console.log("************ finc: New font size = " + currentFontSize);
ed.formatter.register('incfont', {
inline : 'span',
styles : {'font-size' : currentFontSize + 'px'}
});
ed.formatter.apply('incfont');
console.log("********** finc: posting to val box " + currentFontSize);
$("div#px_val button").text(currentFontSize + 'px'); // show value in value box
}
});
If the text is initially in a P the button works fine but puts the text into a span inside the P when it's done. If I then just hit the button again it fails because the node it brings back on line 565 is still the P, which still has the original font size. So if he initial font size is 16, it goes to 17 but then every bump after that stays at 17. If I deselect the text after bumping it and reselect it, line 565 gets the span and the bumps work every time.
How can I force a reselection from my code, so 565 finds the span the second time instead of the P, without me deselecting and reselecting the text?
Thanks
It seems to me that I understand you problem, but i believe that the text re-selection should not happen every time you apply the formatting - just only in the case TinyMCE is adding the new SPAN.
Here is my proposal:
var delta;
var currentFontSize = new Number($(ed.selection.getNode()).css('font-size').replace('px',''));
var node = ed.selection.getNode();
var nodeName = node.nodeName; // for example 'DIV ' or 'P'
if (currentFontSize >= 24) {
delta = 2;
}
else {
delta = 1;
}
currentFontSize = currentFontSize + delta;
ed.formatter.register('incfont', {
inline : 'span',
styles : {'font-size' : currentFontSize + 'px'}
});
var cnt = ed.selection.getContent({format : 'html'});
var lenBefore = $(cnt).length;
ed.formatter.apply('incfont');
var cnt = ed.selection.getContent({format : 'html'});
var lenAfter = $(cnt).length;
if(lenAfter > lenBefore) {
var newText = ed.selection.selectedRange.startContainer;
var rng = ed.dom.createRng();
rng.setStart(newText, 0);
rng.setEnd(newText, newText.nodeValue.length);
ed.selection.setRng(rng);
ed.nodeChanged();
}
Explanation:
when you apply the formatter for the first time, TinyMCE is adding the SPAN and you will find the new selection inside the ed.selection.selectedRange.startContainer node of type text. This is the same as the first child node of type text of the newly inserted SPAN. For subsequent actions, there shall be no need to do any re-selection.
Moreover, IMHO i feel somehow unusual to change the font size in mouse click, i would prefer a standard plugin button which works only with a already existing text selection (but this is up to you):
Of course, the main question of the re-selection is solved, and the plugin will work repeatedly with subsequent mouse clicks also by using a plugin button.
Just in case, as said before, you may also check at the very top if there is any content:
var hasContent = ed.selection.getContent({format : 'text'}.length > 0);
if(!hasContent) return;
So i believe the whole stuff should do the job but anyway, i feel there is still room for some improvements, for example if you need also to reduce the font size, and thus you will also need to delete the already existing - but no longer necessary - SPAN which contain the formatting.
So I'm using this directive to collapse a variable height card in Ionic. The directive grabs the auto height and changes it to a defined height so it can then be collapsed to 0 with a css animation. It was working fine for my needs, but now I need to use ng-src to dynamically load an image within the card. What's happening is the image is being loaded after the directive, so the image loads and overflows the card.
Directive:
.directive('collapse', ['$timeout', function ($timeout) {
return {
restrict: 'A',
link: function ($scope, ngElement, attributes) {
var element = ngElement[0];
$timeout(function(){
$scope.$watch(attributes.collapse, function (collapse) {
var newHeight = collapse ? 0 : getElementAutoHeight();
element.style.height = newHeight +"px";
ngElement.toggleClass('collapsed', collapse);
});
function getElementAutoHeight() {
var currentHeight = getElementCurrentHeight();
element.style.height = 'auto';
var autoHeight = getElementCurrentHeight();
element.style.height = currentHeight +"px";
getElementCurrentHeight(); // Force the browser to recalc height after moving it back to normal
return autoHeight;
}
function getElementCurrentHeight() {
return element.offsetHeight
}
});
}
};
}])
and HTML:
<div ng-repeat="item in items | orderBy : '-'" collapse="item.deleted">
<div class="list card">
<img class="full-image" ng-src="{{item.image}}"/>
</div>
</div>
As you can see I've injected $timeout and leaving the interval blank in hopes it will wait until the DOM is loaded, but it seems no matter how I use it, the directive still explicitly sets the height of the element in css before the image child element is rendered. How can I delay the setting of element height until after ng-src is loaded in each ng-repeat item?
First thing, angular $timeout without an $interval will not wait for the DOM tree to load, basically, what it does is waiting for the current digest cycle to finish before executing the function in the first parameter. By doing so, it will allow the your code to wait till the directive finish compile and render before calculating the height of the div.
However, there is no guarantee that the image will be loaded by that time. Images are loaded by the browser independently from DOM rendering, therefore, to calculate the height of the container having images precisely, you should make use of JS Image Object and the load event. Once the images are fully loaded, then you can update the height.
Also, for your directive, I don't think you need to calculate the height every times the collapse variable changed (inside the watch), you can simply wait till the image being loaded, calculate the height once, store it inside the scope object, and reuse it whenever the collapse variable change.
Okay, so thanks to Thai's input, I have a working solution:
.directive('collapse', [function () {
return {
restrict: 'A',
link: function ($scope, ngElement, attributes) {
var element = ngElement[0];
var img = element.querySelector('.full-image');
angular.element(img).bind('load', function() {
var autoHeight = getElementAutoHeight();
element.style.height = autoHeight + "px";
});
$scope.$watch(attributes.collapse, function (collapse) {
var newHeight = collapse ? 0 : getElementAutoHeight();
element.style.height = newHeight +"px";
ngElement.toggleClass('collapsed', collapse);
});
function getElementAutoHeight() {
var currentHeight = getElementCurrentHeight();
element.style.height = 'auto';
var autoHeight = getElementCurrentHeight();
element.style.height = currentHeight +"px";
getElementCurrentHeight(); // Force the browser to recalc height after moving it back to normal
return autoHeight;
}
function getElementCurrentHeight() {
return element.offsetHeight
}
}
};
}])
I'm trying to export the html part as a pdf file using html2canvas and jsPDF libraries. However, this functionality is working fine in IE and the contents that are available in the window scope is available in the exported pdf where the content inside the scroll bar is not available in chrome. The part has multiple rows where each row is iterated using angularjs ng-repeat and each row has customized css part. Each row should be exported with the applied css and the dynamic data that is available in the screen. Posting the codefor your reference,
Chrome Image
IE Image
Script Code:
$scope.exportFunctionViewData = function(){
html2canvas(document.getElementById('functionViewExport') , {
onrendered: function (canvas) {
var content = canvas.toDataURL('image/jpeg');
var imgWidth = 210;
var pageHeight = 295;
var imgHeight = canvas.height * imgWidth / canvas.width;
var heightLeft = imgHeight;
var doc = new jsPDF('p', 'mm');
var position = 0;
doc.addImage(content, 'JPEG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
while (heightLeft >= 0) {
position = heightLeft - imgHeight;
doc.addPage();
doc.addImage(content, 'JPEG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
}
doc.save($scope.title + '-FunctionView.pdf');
}
});
};
note: I do not have access to the HTML or javascript code
I am using the excellent Chrome plugin, Web Override, to improve usability on a vendor site my company uses. I am only looking for CSS solutions (or possibly js/jq scripts I can sideload).
I'm trying to set table rows to highlight on hover, which is easy enough:
#task-list-main-table tr:hover {
background-color: lightyellow;
}
The problem is that there is a little button that appears on each row when you hover over it. This means if I hover over the button, the corresponding row is not highlighted.
Good:
Bad:
I know I could use pointer-events:none but then I can no longer click on the button, which I need to be able to do.
So, is there any way in CSS to "pass through" hover events without affecting click events?
This is a pretty convoluted method, but if you have the ability to inject javascript, this function will check if your mouse is overlapping whatever element you supply as the selector.
https://jsfiddle.net/tr_santi/aegybp6n/8/
//Change this value to desired element
var hoverElement = "td";
//Change this value to the class you'd like to add when hovering
var addClass = "hover";
function getOffset( el ) {
var _x = 0;
var _y = 0;
while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
_x += el.offsetLeft - el.scrollLeft;
_y += el.offsetTop - el.scrollTop;
el = el.offsetParent;
}
return { top: _y, left: _x };
}
function hasClass(element, cls) {
return (' ' + element.className + ' ').indexOf(' ' + cls + ' ') > -1;
}
function overlapListener(element, x, y, classToAdd) {
var eTop = getOffset(element).top;
var eLeft = getOffset(element).left;
var eBottom = eTop + element.clientHeight;
var eRight = eLeft + element.clientWidth;
if (x <= eRight && x >= eLeft && y <= eBottom && y >= eTop) {
if (!hasClass(element, classToAdd)) {
element.className = classToAdd;
}
} else {
if (hasClass(element, classToAdd)) {
element.className = "";
}
}
}
var elementList = document.querySelectorAll(hoverElement);
document.onmousemove=function(e){
[].forEach.call(elementList, function(b) {
overlapListener(b, e.clientX, e.clientY, addClass)
});
};
I'm sure there are some JS gurus around here that could write you something a bit less obfuscated, however I found this to be a good practice exercise for myself. I chose to write it in vanilla JS as I'm unsure of what your limitations are, although JQuery could substantially reduce the amount of needed code.
I'm working on my WordPress website with Visual Composer.
I need to include a pageable container but it would be great if it can be like a slideshow.
This is my pageable container
Thanks in advance,
Regards :)
Based upon the current version of WP Bakery Page Builder the below works for me:
To build it I created a row with 3 columns, with the pageable container in the middle column and the left and right arrow images in the columns on either side.
Both arrow images and the pageable container were given IDs. In my example the IDs of the arrows were #arrow_prev and #arrow_next respectively. You can give your pageable container any unique ID.
(function ($) {
$(document).ready(function(){
$( '#arrow_prev' ).click( function( e ) {
var pageable_container = $(this).closest(".vc_row").find(".vc_tta-panels-container");
move_pageable_container(pageable_container,'prev');
});
$( '#arrow_next' ).click( function( e ) {
var pageable_container = $(this).closest(".vc_row").find(".vc_tta-panels-container");
move_pageable_container(pageable_container,'next');
});
function move_pageable_container(pageable_container,direction){
// Make a list of the panel IDs
var panel_ids = $(pageable_container.find(".vc_tta-panel"))
.map(function() { return this.id; }) // convert to set of IDs
.get();
// Find position of the active panel in list
var current_active_pos = panel_ids.indexOf($(pageable_container).find(".vc_tta-panel.vc_active").attr('id'));
var new_pos = 0;
switch(direction) {
case 'prev':
if (current_active_pos > 0){
new_pos = current_active_pos-1;
}else{
new_pos = panel_ids.length-1;
}
break;
case 'next':
if (current_active_pos < panel_ids.length-1){
new_pos = current_active_pos+1;
}else{
new_pos = 0;
}
break;
}
// Clear active panels
$(pageable_container.find(".vc_tta-panel")).each(function(i,a) {
$(this).removeClass("vc_active");
});
var new_active_panel = $(pageable_container).find('#'+ panel_ids[new_pos]);
$(new_active_panel).addClass("vc_animating");
$(new_active_panel).addClass("vc_active");
setTimeout(
function(){
$(new_active_panel).removeClass("vc_animating");
}, 350);
}
}
);
})(jQuery);
If you want a pseudo fading-in effect then you can use this additional CSS in your style sheet:
#id_of_pageable_container .vc_tta-panel.vc_animating {
opacity: 0!important;
}
Where #id_of_pageable_container is the ID that you gave your pageable container
A simpler solution with vanilla js only:
The idea is to find the target page button and press it programmatically, so that there is no need to mimic the plugin's animations as in Chaz's solution.
Add js (via Raw JS widget / other means):
function prevSlide () {
const slides = document.getElementsByClassName('vc_pagination-item');
for (let i = 0; i < slides.length; i++) {
if (slides[i].className.includes('vc_active')) {
if (i - 1 < 0) return;
slides[i - 1].firstChild.click();
return;
}
}
}
function nextSlide () {
const slides = document.getElementsByClassName('vc_pagination-item');
for (let i = 0; i < slides.length; i++) {
if (slides[i].className.includes('vc_active')) {
if (i + 1 >= slides.length) return;
slides[i + 1].firstChild.click();
return;
}
}
}
Add button widgets and set href to call js:
For left arrow button,
javascript:prevSlide();
For right arrow button,
javascript:nextSlide();
Hope this helps.
I prefer to use the Post Grid widget for that. Keep in mind that the pageable container is not totally responsive, it doesn't react to swipe touching, but the Post Grid does.
Post Grid is really powerful, although it also has its caveouts. You can create your content with posts and pages, or a custom post type and then filter what you want to show in your slider from the widget options.
In "advanced mode" you can use the Grid Builder to create your own template and control the output.
The only problems that I've found with this method is to set a variable height in sliders and that sometimes it is slow loading content and is not possible to do a lazyload.