FullCalendar print on iPad - javascript

I have been working on this task off and on for awhile trying to find an optimal solution (other than telling users to disable popup blocking) and am stumped.
Essentially how it works is this (I omit code because there is a lot of it and propriety info):
I have an angular app implemented in an angular and utilizes fullcalendar.js who's content I want to print. Inside my angular controller I have the jQuery that manages the calendar itself (don't hit my fingers with a ruler please :) )
I want a specific set of styles when I want to print the calendar, so I have a directive that prepares all the content to be ported, and then use a uniform angular factory that is used for all printing activities. This uniform factory opens a new window that contains all the new styles I want and, via a callback, "cleans up" the HTML, which in my case I use for porting the HTML content of the calendar over to the new window.
So the flow basically is this: User clicks print button -> click event in calendar print directive is invoked, the directive calls the factory. -> Factory opens a new window and ports the content via the callback from the directive and then calls JavaScript's print() to print the window.
The problem I am encountering is this:
The print works fine on PC and Mac, but on iOS safari, the window does not pop up. I found that the issue was because iOS Safari requires all new window popups to be inside a click event.
To get around this, I thought I would add some modification to the uniform factory to suit my case: I would open a new window in the directive's click event, then pass a reference of that window into the factory, which can then use the reference to add the html content to its body. This introduced another issue with iOS Safari in that it stalls javascript execution of parent windows if a child window is open, so once the new window is open, the generation of the HTML and the calling of the factory is stalled until the user switches back to the parent tab. This is the point where I got stumped. Any suggested way to get around these issues? Or would I be stuck telling the user to disable popup blocking?

once the new window is open, the generation of the HTML and the
calling of the factory is stalled...
Can you change so the generation of HTML and factory call is made before opening the new window? Otherwise it sounds like you need to avoid the popup.
If you want to display another view in the same window, use ngInclude.
You can choose to switch the path of the ngInclude to swap the HTML or combine with ngShow to show and hide the correct parts when the user clicks.
It sounds like you use a factory as a parent scope and if so it should be converted to a controller that acts as a global scope above the different views.
If this is on the right track I could make a plunkr out of it.
Also, check out fullcalendar-ui for a premade directive if you want to go towards best practice.
Good luck!

After ~3 times coming and going from this over the course of a month, I finally figured it out.
Inside my directive that prepares the content to be printed, I generate a new calendar, then call the rest of the code (including the factory that opens a new window) inside a document.ready. Having the code inside the ready check seems to cause iOS Safari to think it is no longer directly inside a click event, so it would sometimes block the new window popup. Removing the document.ready check seems to have made it work, and has no ill effect on the other browsers.
I decided to create a function inside the directive for the factory call and call inside a document.ready if not iOS, otherwise just call it, to preserve functionality for desktop browsers.

Related

Angular - child component in new window has very odd behavior

I am using Angular's DomPortalHost to open a child component in a new browser window. It works for the most part.
Here is a working prototype of opening a child component in a new browser window:
https://stackblitz.com/edit/portalfun
I am able to pass data to and from child component - works great right?
Well.. when I have let's say, a map in the new browser window some of the map events seem to think the map exists in the original browser window. See:
https://www.youtube.com/watch?v=0387htqmXZw
Hilarity ensues:
I am able to click once in the new window to begin a selection (circle, etc) but not a second time. Using the view +/- controls seems to work fine. However, the map navigation/scrolling and the selection events think they are controlled from the original window.
The endgame for this little innovation project was to pass whatever data was selected in the map (syncing the grid you see in the background and the map). This works fine when I don't use DomPortalHost (that area below the grid is where the map was originally and it functions correctly).
I think that because *cdkPortal (see the stackblitz) is technically inside the original browser window:
<window *ngIf="showPortal" [dataFromParent]="dataToPass" (dataToParent)="this.onClosed($event)">
</window>
selector: 'window'...
<ng-container *cdkPortal>
<h1>Child component window</h1>
<div>{{dataFromParent}}</div>
<a class="btn-3" (click)="emitClose()">Close Window</a>
You can also close the parent window, or navigate to a different route. It will close the child window component.
</ng-container>
It's as if the map think's its been rendered in the original window, but is actually rendered in the new window.
The closest.. discovery that i've had was that some of the event handling has a [[Scope]] property set to the original window - not the new on. However I don't see how to overwrite this property.
If anyone has any ideas, resources, or a better approach please feel free to let me know. This is part of an innovation sprint, but it'd be cool to get it working as i'm sure it has a lot of use cases for others out there.
Imagine opening child components and passing data back and forth in new windows.. and not having to run a second cloned app or deal with the overheard of state management :)

How to pass values from HTML webresource to javascript on window close MSCRM

I am opening HTML webresource using Xrm.Navigation.openWebResource but on closing of HTML window I want to pass values from HTML to javascript file from where it is opened. Is there call back function can be implemented?
If I open HTML window using window.open I can call parent javascript function using window.opener.functionname on close but click but I want to know how I can pass values to parent javascript file on close button click of HTML window.
I tried with window.parent.opener.Functionname() but it is not working - getting functionname is undefined but it is defined in parent javascript. Pls suggest.
If you're using the 'old' (as it not the unified interface) user interface with turboforms enabled then the parents javascript is actually in a extra iframe called customScriptFrame, and not on the parent itself.
To call something on the parent you can use
parent.customScriptsFrame.functionname() for IE
and
parent.customScriptsFrame.contentWindow.functionname() on chrome.
On the unified interface its much the same, but far more troublesome.
Now the scripts are in a iframe called ClientApiFrame_[n] where [n] is some random number. And i haven't found a good way to determin that number ahead of time from a webresource.
You could go over all frames of the parent in javascript (parent.frames) to find one that has a id that starts with ClientApiFrame_ but that will throw errors trying to read frames with sources set to external domains, and i dont think is very good practice.
Another possibility is registering the function you want to call with the parent ahead of time. so in the main javascript use this.
parent.functionname = functionname
And then from the webResource you can use the normal
parent.functionname
If the webresource is embedded in the form, then use window.parent
If you Xrm.Navigation.openWebResource to open it, then use window.opener

Cytoscape event cxttap called multiple times

I am trying to create a network workflow with Cytoscape and AngularJS 1.6
After creating two nodes, user is able to create an edge between them. For this I am using cy.on('cxttap') cytoscape function to detect right clicks on both the nodes and then insert a new edge.
This works fine. Until I added a new view, where the user can see saved workflows from the database. The problem is, if I open the Viewing tab and then go back to creation tab, the cy.on('cxttap') function is called twice, and two edges are inserted on the cytoscape canvas, but only one entry is made in my scope variable. I used to have the same factory for both the views but now I am using different angular factories for each.
If I open switch between these tabs multiple times, the number of times I open the viewing tab, is the number of times the function is called, hence more the number of lines.
Here (https://jsfiddle.net/y47kwpg7/4/) is the snippet with the code for both my views with their controllers. "Creation View" --> "MlalTextWorkflowGeneratorCtrl" and "Viewing View" --> "MlalTextWorkflowViewerCtrl".
(Please forgive me, I don't know how to make it work in a single file)
The factory I have here is for the cytoscape canvas, and have a similar one for another view, but with a different id selection.
Thank you for your help!
You either need to
(1) remove old listeners if you're re-using an instance, or
(2) create a new instance each time you create a new view.
#maxkfranz 's answer guided me to do a little more debugging and finally I found out the problem.
The problem was I had some listener functions that I called from my controller in order to detect events on the cytoscape nodes. And the listeners to fire were stored in a a variable workflowGraph.listeners. you can see the old code []1. The problem was this variable was not emptied anytime my controller was reloaded, because I was calling the listeners from my controller, they were being added to this variable once again. So, one listener had multiple functions to call.
The method I used to solve this was to create a new "listener" type function I had earlier for cytoscape events, which I would call everytime my controller is loaded, to empty the listeners variable for my cytoscape factory.
In cytoscape factory
workflowGraph.reinitialize = function () {
workflowGraph.listeners = {};
};
In controller
workflowCreation is the name of the factory
workflowCreation.reinitialize();
This would simply empty the listeners variable from the factory.
Remember to call this in your controller after cytoscape initialisation and before calling any other listener functions
Hope this helps someone.

Filepicker.io widget appending to "window.history"

The Filepicker.io modal widget (specifically the "IMAGE_SEARCH" service) appends to the window.history in the DOM after a search is made. This creates an issue working with Backbone.js when attempting to go back a previous page.
What causes this, and is there any way to prevent it?
[edit] Incorrectly referenced "IMAGE_SEARCH"
What causes this:
Navigation around the modal manipulates window.location.hash for compatibility with the window view and a number of other conveniences. Why these changes are affecting the window.history outside the iframe sandbox, I'm not sure.
How to prevent it:
I'm looking into whether we are leaking state somehow, but one easy way to prevent it is to use the {container: 'window'} option for filepicker.pick(), so that the dialog is created in a separate window.

How can I bind a javascript dialog using Knockout?

I've got a list of data in an observableArray and I want to show it in a javascript dialog window (I'm using jQuery.blockUI if it matters). Unfortunately the dialog seems to come unbound after the page is loaded. The dialog initializes correctly (the data is displayed), but it isn't updating with changes.
There are no Javascript errors and I've moved the binding to after the dialog is generated and added to the document (no effect). I've also tried calling ko.applyBinding on the main div that makes up the dialog but that, for some reason, causes part of the main page to hide (the DOM is there, but they are hidden).
EDIT: I've created a project on jsfiddle that reproduces the problem. The main culprit seems to be wrapping the content of the dialog in a div. If I show the content directly it seems to work (of course I can't do that, the wrappers provide a common style for our dialogs).
I'm recovering from the flu and could easily be missing something obvious, but I've been trying all day and nothing is coming to me. Any ideas?
The problem is that the dialog does not exist in the DOM (despite your calling $(document).append(). You cannot append a div as a child of the document itself). Instead, append the dialog to the body and hide it.
$dlg = $('<div></div>').hide();
$('body').append($dlg);
Works here: http://jsfiddle.net/yL6ds/4/

Categories