I have a Xamarin WindowsRT project where I am able to load a local PDF and view it within the WebView of the app using PDF.js.
I have created a custom renderer to override the Xamarin WebView and I simply call:
Control.Source = new Uri("ms-appx-web:///Assets/pdf/pdfjs/web/viewer.html?file=mypdf.pdf");
And this works great for opening and viewing the PDF.
The thing is, the PDFs I will need to view are in the form of base64 encoded strings downloaded from a server. Is there a way to pass the base64 to PDF.js somehow?
Update:
Just to see if I can get this working using javascript, I am loading a basic html page into the webview with the following script, but I just get a blank page:
<script>
var pdfAsArray = convertDataURIToBinary('data:application/pdf;base64,JVBERi0xLjcKCjEgMCBvYmogICUgZW50cnkgcG9pbnQKPDwKICAvVHlwZSAvQ2F0YWxvZwogIC9QYWdlcyAyIDAgUgo+PgplbmRvYmoKCjIgMCBvYmoKPDwKICAvVHlwZSAvUGFnZXMKICAvTWVkaWFCb3ggWyAwIDAgMjAwIDIwMCBdCiAgL0NvdW50IDEKICAvS2lkcyBbIDMgMCBSIF0KPj4KZW5kb2JqCgozIDAgb2JqCjw8CiAgL1R5cGUgL1BhZ2UKICAvUGFyZW50IDIgMCBSCiAgL1Jlc291cmNlcyA8PAogICAgL0ZvbnQgPDwKICAgICAgL0YxIDQgMCBSIAogICAgPj4KICA+PgogIC9Db250ZW50cyA1IDAgUgo+PgplbmRvYmoKCjQgMCBvYmoKPDwKICAvVHlwZSAvRm9udAogIC9TdWJ0eXBlIC9UeXBlMQogIC9CYXNlRm9udCAvVGltZXMtUm9tYW4KPj4KZW5kb2JqCgo1IDAgb2JqICAlIHBhZ2UgY29udGVudAo8PAogIC9MZW5ndGggNDQKPj4Kc3RyZWFtCkJUCjcwIDUwIFRECi9GMSAxMiBUZgooSGVsbG8sIHdvcmxkISkgVGoKRVQKZW5kc3RyZWFtCmVuZG9iagoKeHJlZgowIDYKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDEwIDAwMDAwIG4gCjAwMDAwMDAwNzkgMDAwMDAgbiAKMDAwMDAwMDE3MyAwMDAwMCBuIAowMDAwMDAwMzAxIDAwMDAwIG4gCjAwMDAwMDAzODAgMDAwMDAgbiAKdHJhaWxlcgo8PAogIC9TaXplIDYKICAvUm9vdCAxIDAgUgo+PgpzdGFydHhyZWYKNDkyCiUlRU9G');
PDFJS.getDocument(pdfAsArray);
</script>
I know the html page is loading because above the script i put TEST and that renders. And it is executing the javascript. It is also properly creating the pdfAsArray, because I also iterated through it and it has a lot of values.
Fetch your pdf string using C# and then inject it as a variable in your WebView in order to pass it (since you're using Xamarin.Forms, you can use HybridWebView's InjectJavaScript)
Then, use the function from this gist to convert that string into an array that can be accepted by pdf.js, as pointed in this answer.
var pdfAsDataUri = //Your string;
var pdfAsArray = convertDataURIToBinary(pdfAsDataUri);
PDFJS.getDocument(pdfAsArray)
You can also create a function that wraps the conversion and the PDFJS.getDocument and call it directly from C#:
//JS
function loadPdfFromBase64(pdfString) {
var pdfAsArray = convertDataURIToBinary(pdfString);
PDFJS.getDocument(pdfAsArray)
}
//C#
hybridWebView.CallJsFunction("loadPdfFromBase64", yourPdfString);
Related
I am facing an odd problem. I im trying to parse the following html:
The problem is that when I do
response.xpath('//div//section//div[#id="hiring-candidate-app"]')[0].extract()
I only get
'<div id="hiring-candidate-app"></div>'
instead of all the content under hiring-candidate-app.
I would like to get, for instance, inside-content, but it looks like I am not even getting that in the response. This webpage requires to be logged in, which I am.
Thanks in advance!
It looks like your Xpath is grabbing the right thing. But your issue might have to do with the '[0]' part of the call. I would remove that to get the full content of the div.
It looks like the elements in question sit on an <iframe>, and therefore live in a different context. You need to activate or switch to the context of the iframe, eg. using JavaScript to interact with an iframe and the document inside of it, e.g.
//Note: Assigning document.domain is forbidden for sandboxed iframes, i.e. on stacksnippets
//document.domain = "https://stacksnippets.net";
var ifrm = document.getElementById("myFrame");
// reference to iframe's window
//var win = ifrm.contentWindow;
// reference to document in iframe
var doc = ifrm.contentDocument ? ifrm.contentDocument : ifrm.contentWindow.document;
// reference an element via css selector in iframe
//var form = doc.getElementById('body > div > div.message');
// reference an element via xpat in iframe
var xpathResult = doc.evaluate("/html/body/div/div[1]", doc, null, XPathResult.ANY_TYPE, null);
<iframe id="myFrame" src="https://stacksnippets.net" style="height:380px;width:100%"></iframe>
However, as you can see when you run the snipped, cross-document interactions are only possible if the documents have the same origin. There are other, more involved methods like the postMessage method that provide the means of interacting cross-domain.
Question edited:
I wrote a page with jquery-bootgrid data API.
Its should be calling with AJAX to my NancyFX REST API, but it isn't.
Client side:
I'm serving the bootgrid from a local repo:
<script src="~/scripts/jquery.bootgrid.min.js"></script>
Maybe I shouldn't be using the .min.js file but rather the open one for debugging? If so, could you walk me through what to do, or point me in the direction?
The page code is
...
data-toggle="bootgrid" data-ajax="true"
data-url="/cars/0" data-method="GET"
Server side:
I have the html page served by NancyFx and is seen ok, except for the grid which is empty. There's an API module with a breakpoint in Visual Studio (running on localhost), with the following:
Get["/cars/{status:int}?current={current}&rowCount={rowCount}"] = parameters => ...
This code is never called. How can I force the debugger to catch ANY call before the routing is checked, and show me what's coming into the server?
I'm using the chrome debugger.
The current URL is not valid by Nancy standards. We don't add query string params to the route.
You would want to write something along the lines of:
Get["/cars/{status:int}"] = parameters =>
{
var status = (int)parameters.status;
var current = (string)parameters.current.TryParse("");
var rowCount = (int)parameters.current.TryParse(10);
...
}
Along those lines. (written off the top of my head)
An alternative approach is to bind the request like so:
Get["/cars/{status:int}"] = parameters =>
{
var request = this.Bind<MyRequest>();
...
}
public class MyRequest
{
public MyRequest()
{
RowCount = 10;
}
public int Status {get;set;}
public string Current {get;set;}
public int RowCount {get;set;}
}
Changing the nancy to Get["/cars/{status:int}"] = parameters => did the trick of catching the request.
The ajax wasn't being called because I lost the JQuery first line...
$(document).ready(function() {
To get the current and rowCount you need to use
var current = (int)Request.Form["current"];
var rowCount = (int)Request.Form["rowCount];
By the way, the Get wasn't working (I think its a Bootgrid bug) so I changed it to POST.
The simplest way to debug any jQuery library is by using the in-built debugger, it's kinda difficult for me to use chrome for that , so I use Firefox but if you are habitual of chrome then use it, the functionality is almost the same, but with Firefox you can directly switch to the events associated with any element in the html (in the inspect section)
Once you get into the debugger, set the breakpoint and refresh the page either by F5 or Ctrl+F5 if you selected the valid breakpoint you can see all the values associated with every variable also with every function.
Secondly, use the step-in option in the debugger to see where the exact line is pointing, if it's refering to any other file it will pop open automatically in the debugger menu. Firefox's spider monkey is much good at debugging and relating codes (that's totally my opinion).
3- for the api calls, the reason for data not being processed or not displayed, very much lies within the structure of the library,(on what parameters the data is called/fetched/retrieved), for this try to use the "watch expressions" option in debugger and try implementing the code on loaded dom in console section with trigger on the node which you think is bugged or which should display the value.
Possible to get a text selection out from safari (host app)to an app extension, or only the URL?
Yes this is possible. You can create a JavaScript file as part of your Action Extension. This is described in the documentation since you also have to add a NSExtensionJavaScriptPreprocessingFile key to your extension's Info.plist.
Inside the JavaScript file you can define a run function which allows you to define values to pass to your native extension code. Here you can get the selected text as shown in other questions and pass this through to your extension.
Here's a quick example of how this might work on the JavaScript side:
var MyExtensionJavaScriptClass = function() {};
MyExtensionJavaScriptClass.prototype = {
run: function(arguments) {
// Pass the selected text through
arguments.completionFunction({"text": window.getSelection().toString()});
}
};
// The JavaScript file must contain a global object named "ExtensionPreprocessingJS".
var ExtensionPreprocessingJS = new MyExtensionJavaScriptClass;
I'd like to use razor syntax inside my Javascript files. Is this possible without including the javascript inline into the page?
I found a razor engine RazorJS on nuget that solves # in js files
The owner blog and explanations about the package abilities
The Nuget package
see more in this question
The Razor engine only runs against the page, not any included javascript files.
You could write a custom parser that will run the view engine against any javascript files before serving them, and I imagine any attempt to do so would be a very useful open source project.
However, the simplest solution that comes to mind (if these variables are not sematically linked to any DOM elements) is to simply declare and initialise your variables in the page (or in an included partial page) and your javascript (in .js files) relies on these variables being defined.
If however the variables that you require are logically associated with DOM elements, I prefer to use data-* attributes to define these, this way your javascript can be consumed by the html, rather than the other way around. For example, if you have a content area that should be automatically updated by javascript (using jQuery as an example here):
HTML:
<div data-auto-refresh="pathToContent" data-auto-refresh-milliseconds="1000"></div>
Javascript:
$(function() {
$('[data-auto-refresh]').each(function() {
var self = $(this);
var url = self.data('auto-refresh');
var interval = self.data('auto-refresh-milliseconds');
// Code to handle refresh here ...
});
});
You can set the value in hidden field in yout cshtml file , and then in your javascript files you can access the hidden field.
We're trying to use Stackoverflow's excellent WMD / Markdown editor (http://blog.stackoverflow.com/2009/01/updated-wmd-editor/, http://github.com/derobins/wmd/tree/master) on a Symfony project.
This works excellent on textareas without any AJAX involved. But when we have to include wmd.js first, and then later on user interaction (i.e. "click on link") have the textarea loaded via AJAX we utterly fail to make WMD work, Firebug is giving us
elem is null
addEvent()()wmd.js (Linie 110)
setupEvents()()wmd.js (Linie 1790)
init()()wmd.js (Linie 1970)
previewManager()()wmd.js (Linie 1987)
loadListener()()wmd.js (Linie 1763)
[Break on this error] if (elem.attachEvent) {
on loading the page (i.e. before textarea loading).
Symfony's AJAX Loader seems to eval() everything between tags. We tried including the whole script directly between those tags, we tried escaping this and that, but had no success with differnt errors coming up.
At this point we think we have to include the script in the normal page and after the AJAX call we have to initiate WMD manually - what functions do we have to call? Are we completely off track and need to use a different approach?
Thank you!
You can use my version. Simply put the constructor call, config values and the call to the start() method in the callback function that handles the results you get from the AJAX request. My version of mooWMD.
drobins fork of wmd on github is solving the AJAX issues as well.
I had this same issue and I initialize WMD once my textarea has been added to the page asynchronously.
Heres my code:
function loadTextEditor()
{
var instances = [];
if (!Attacklab || !Attacklab.wmd) {
alert("WMD hasn't finished loading!");
return;
}
/***** build the preview manager *****/
var textArea = document.getElementById('postcontent');
var previewPane = document.getElementById('postPreview');
var panes = {input:textArea, preview:previewPane, output:null};
var previewManager = new Attacklab.wmd.previewManager(panes);
/***** build the editor and tell it to refresh the preview after commands *****/
var editor = new Attacklab.wmd.editor(textArea,previewManager.refresh);
// save everything so we can destroy it all later
instances.push({ta:textarea, div:previewDiv, ed:editor, pm:previewManager});
}