Archive for the ‘JavaScript/AJAX’ Category

Using the Camera from Mobile Web

In the early days of mobile web, it was not possible to access the camera from a web page. While Android added the functionality a while ago in Honeycomb, it seems to have quietly slipped in to iOS in 6.0. So now, the majority of smartphone users can use the camera from a web site.

The HTML code is to access the camera is surprisingly simple:

<input type='file' accept="image/*" capture="camera" onchange="previewImage(this);" />

The accept and capture attributes do all the work. In my example I’ve also added an onchange handler to add a preview of the image to the HTML page:

<div style="height: 400px; width:200px"><img id="preview" src="" alt="your image here" /></div>
<script type="text/javascript">
     $('#preview').on('load',function() {
         var css;
         var ratio=$(this).width() / $(this).height();
         var pratio=$(this).parent().width() / $(this).parent().height();
         if (ratio<pratio) {
             css={width:'auto', height:'100%'};
         } else {
             css={width:'100%', height:'auto'};
         }
         $(this).css(css);
     });
     function previewImage(input) {
         if (input.files && input.files[0]) {
             var reader = new FileReader();
             reader.onload = function (e) {
                 $('#preview').attr('src',e.target.result)
             };
             reader.readAsDataURL(input.files[0]);
         }
      }
</script>

Try it out

Interestingly, on iOS the photo stays in memory and is not persisted to the device. On Android, the photo is automatically persisted to the gallery. I suspect this is for memory management.

I have tested this code successfully on the following platforms:

  • Android 3.0+ browser
  • Chrome on Android
  • Mobile Safari on iOS6+
  • BlackBerry 10

The only large group of smartphone users left out in the cold are Android users still on Gingerbread.

I tested this code on WP8, and it didn’t work. There may be a work around, but I haven’t had a chance to work that out yet.

Posted on:
Posted in JavaScript/AJAX, Mobile | Comments Off on Using the Camera from Mobile Web

Cross-Domain Calls for a WCF Service

As I develop more services for mobile devices (and mobile web in particular), cross-domain calls are becoming more frequent. Essentially, a cross-domain call happens when a web page uses JavaScript to access resources at a different URL. This can be as complex as hosting services completely separate from the app in another web site, or in another subdomain or on another port, and also can happen simply because the web page is calling a service over SSL.

The problem occurs because the browser only trusts calls from the exact same domain, unless the server says that it trusts your domain (or all domains). This is by design.

So the resolution is to have the server tell the clients what domains it trusts for cross-site calls. Seems simple, but of course there is more to it than that.

When the browser detects a cross-domain call, it behaves differently if your service is configured correctly. If you were expecting your JavaScript code to perform a POST to a REST URL, that is no longer what happens, at least initially. Instead of performing the POST, the browser actually sends an OPTIONS verb call to your REST URL. This is called a preflight request. Your service needs to respond to the preflight request containing the OPTIONS verb and let the browser know which parameters are acceptable on a cross-site call. If everything matches up and your client is in an allowed domain, then the browser performs the original POST that you expected and everything continues on normally.

If your service is not configured correctly, the browser doesn’t cooperate and you don’t see anything at all. Again, this is by design.

Configuring The Service

From an HTTP point of view, what we need to do in code is add the following name/value headers to our HTTP responses:

  • Access-Control-Allow-Origin
  • Access-Control-Allow-Methods
  • Access-Control-Allow-Headers
  • Access-Control-Max-Age

The first, Access-Control-Allow-Origin, needs to be added to any request so the browser knows to use the OPTIONS verb for cross-site calls, which just error out otherwise with an XmlHttpReqeust Error 101 in JavaScript. The other three need to be added in response to the OPTIONS verb request. 

The Access-Control-Allow-Origin header can be added with one or more values. If you expect any client anywhere to call your services, like Google Maps does, you want to add * for the value, allowing all callers. Otherwise, the most secure way to use Access-Control-Allow-Origin is to set it for the specific domains you want to accept. Say your services are at https://service.yoursitehere.com, and your client site is http://www.yousitehere.com, you want to set the value to http://www.yoursitehere.com.

The code adds the Access-Control-Allow-Methods header to tell the browser which HTTP verbs your calls will accept as cross-domain.

By adding the Access-Control-Allow-Headers header, you control exactly which HTTP headers are allowed in the actual request which follows the preflight request.

The Access-Control-Max-Age header tells the browser how long to cache the preflight call result before doing it again during this browser session. I set this long to avoid multiple preflight requests.

In my case, I’m using configuration-less WCF services as described here. In my experience much of the pain with WCF in the past has been with the complex configurations, and this option circumvents that problem substantially.

The C# Code

So this is how it’s done programmatically, in Global.asax in my case:

 protected void Application_BeginRequest(object sender, EventArgs e)
 {
  HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin" , ”http://www.yoursitehere.com”);

 if (HttpContext.Current.Request.HttpMethod == "OPTIONS" )
 {
 //These headers are handling the "pre-flight" OPTIONS call sent by the browser
 HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods" , "GET, POST" );
 HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers" , "Content-Type, Accept" );
 
 
 HttpContext.Current.Response.AddHeader("Access-Control-Max-Age" "1728000" );
 HttpContext.Current.Response.End();
 }
 }
 

I put it Global.asax in order to avoid having each request handle multiple verbs and calling the handler in each service call. The downside to this is that any non-service request also gets the additional header and processing as well. For me, there are only service requests in this app so this is not a big deal.

The Client

I’ve regularly used jQuery to access services from client HTML code. It’s not complicated, the important parts are formatting the call to send JSON properly in response to our preflight request, which includes the dataType, contentType and data parameters to the $.ajax call.

 $.ajax({
 url: "https://www.yoursitehere.com/person/add",
 type: "POST" ,
 dataType: "json" ,
 contentType: "application/json" ,
 data: JSON.stringify(personObject),
 success: function (data) {
 //Process success here 
 },
 error: function (jqXHR, textStatus, errorThrown) {
 //process errors here 
 }
 });
 

Tools

If you are debugging REST services, you need some good tools. I have used Fiddler, the Chrome Developer Tools (the Network panel in particular) and the Chrome app Advanced REST Client Application successfully.

References

This post is the basis of the fix I’m describing

W3C description of how the HTTP process works

FireFox and CORS, also a lot of good info on HTTP headers

BlackBerry Simulator Error with SQLite and HTML5

When testing an HTML5 app on multiple BlackBerry simulators, on some of them I was getting the error:

SECURITY_ERR: DOM Exception 18

What this turned out to be was the lack of an SD card in certain simulators. The browser was expecting to use that storage space for the SQLite DB files. Once I set up the SD card for the emulator the error disappeared.

Printing a PDF from a web page

The requirement in this case is to print a PDF document, but not allow the user to manipulate the PDF in any other way. The user should see the Print Dialog box and be able to manipulate the settings. This requirement was born out of an auditing policy where the document is audited differently for printing or viewing the PDF.  I found lots of web discussion about printing a pop-up window from the parent window, or trying to print the contents of an IFrame, but none of it worked for me. I was hit with security errors from IE about cross-domain issues, even though my pages were in the same domain, or just got a blank page. So to get a working solution, I had to step out of the web page.

The tool that really allowed me to do this is iTextSharp, a C# implementation of the iText open source java library for manipulating PDF files, and the fact that you can use JavaScript within a PDF file. I used an ASPX page which manipulates and streams the PDF file into a zero-height, zero-width IFrame to keep the user from manipulating the PDF in any way (I realize tech-savvy users could still get the PDF).  I embedded the JavaScript into the PDF, where it runs once the document is loaded and pops up the Print dialog from Adobe Reader (not the browser).

The JavaScript for printing a PDF is not so complex, I embedded it into a function:

protected string GetAutoPrintJs()
{
    var script = new StringBuilder();

    script.Append("var pp = getPrintParams();");
    script.Append("pp.interactive = pp.constants.interactionLevel.full;");
    script.Append("print(pp);");

    return script.ToString();
}

In the code-behind for the ASPX page that will stream the PDF and place the script inside the PDF file, I built this function:

protected void StreamPdf(Stream pdfSource)
{
    var outputStream = new MemoryStream();

    var pdfReader = new PdfReader(pdfSource);

    var pdfStamper = new PdfStamper(pdfReader, outputStream);

    //Add the auto-print javascript
    var writer = pdfStamper.Writer;
    writer.AddJavaScript(GetAutoPrintJs());

    pdfStamper.Close();

    var content = outputStream.ToArray();
    outputStream.Close();
    Response.ContentType = "application/pdf";
    Response.BinaryWrite(content);
    Response.End();
    outputStream.Close();
    outputStream.Dispose();
}

The PDFReader and PDFStamper  classes are members of the iTextSharp library. I used the classes together to get the stream for output and also access to PDFWriter object that allowed me to add the JavaScript.

Invalid postback or callback argument – AJAX Control Toolkit

I was working to add some Ajax interactivity to a page that needed some asynchronous workings. Unfortunately, after displaying a ModalPopupExtender and the user clicking the Close button on the “popup”, the page would throw the following exception:

System.ArgumentException occurred
  Message=”Invalid postback or callback argument.  Event validation is enabled using <pages enableEventValidation=\”true\”/> in configuration or <%@ Page EnableEventValidation=\”true\” %> in a page.  For security purposes, this feature verifies that arguments to postback or callback events originate from the server control that originally rendered them.  If the data is valid and expected, use the ClientScriptManager.RegisterForEventValidation method in order to register the postback or callback data for validation.”
  Source=”System.Web”
  StackTrace:
       at System.Web.UI.ClientScriptManager.ValidateEvent(String uniqueId, String argument)
  InnerException:

There are lots of explanations out there for this kind of problem, but none of the common answers really fit. The most common answer was to disable event validation. That wasn’t a good answer for me, I don’t think disabling an important security feature because you have complex code is a good solution. The other common answer was to override the render event to register the control posting back with the ScriptManager. This did not work at all for me.

After finding an reading through this post and the comments, I realized the problem was indeed in the code. After the call to ModalPopupExtender.Show(), the original code was re-binding a grid on the page. This order of events was causing the problem. I changed the binding to occur before the call to the ModalPopupExtender by changing which event containing the rebinding call, and the error went away.

Linq Foray

I’m working on a website with lots of user controls interacting with one another using ASP.NET Ajax using events. So I end up needing to find other controls often, especially within repeaters. Nested controls all over the place. I found this wonderful post about using an extension method to the Controls Collection that flattens the tree and allows for much simpler Linq queries. The extension method adds and All() function which is seriously useful.

Using XMLHTTP in Javascript

I attended a terrific meeting tonight of GANG, the Great Lakes Area .Net user Group, based in the Detroit-area. The main presenter was Jason Beres who gave an excellent presentation on ASP.Net tricks, many of them utilizing Javascript. One of my favorites came up in discussion and I agreed to post the code snippet here.


I have used this techniqe mainly on classic ASP pages as a way to populate secondary dropdown lists before ASP.Net and control-based postbacks came along. It still works with ASP.Net… BUT…the secondary listbox is not in the Viewstate so it’s value isn’t set properly when accessing the control in server code on the postback where you handle the data. The secondary list’s selected value is still in the Request.Form collection though. It is still a great technique to use to fetch data from the server without reloading the page.


The technique is IE-only as it uses an ActiveX object (XMLHTTP) in the Javascript. It also uses the XML DOM to parse the response from the server. In this example the server is an ASP page, but it could easily be a web service as well with the addition of one a little more code. This code is related to a previous post where I used VBA to enable Excel to be a web service client.


Here is the Javascript function:


/*  Purpose: Fill secondary list boxes with content
    Arguments:  oItem – the primary dropdownlist object
                sFieldName – The name of the field filling the secondary list
                oDestination – the secondary list object
    Returns: None, fills list box using MSXML*/
 
function fillSecondary(oItem, sFieldName, oDestination)
{
  var nValue = oItem[oItem.selectedIndex].value;
 
  var xmlHTTP = new ActiveXObject(“Microsoft.XMLHTTP”);
  xmlHTTP.open(“POST”, “./listboxesXML.asp”, false);
  xmlHTTP.send(‘<‘ + sFieldName + ‘>’ + nValue + ‘    
  var xmlDOM = new ActiveXObject(“Microsoft.XMLDOM”);


  xmlDOM.loadXML(xmlHTTP.ResponseText);


  if (xmlDOM.parseError != 0)
  {
   alert(“Error occurred: ” + xmlDOM.parseError.reason);
   return false;
  }


  var oNode = xmlDOM.documentElement.firstChild;
  var n=0;
  if(oNode != null)
  {
   //Clear out the secondary list box, it might already have items
   oDestination.length = 0;
   while (oNode != null)
   {
    oDestination[n] = new Option(oNode.text, oNode.attributes(0).text);
    n++;
    oNode = oNode.nextSibling;
   }
   if(n==1)
    oDestination.selectedIndex = 0;
  }
}


In the HTML on the page, add an OnChange handler to the primary dropdown list to call the Javascript when the user changes the value:


<FORM id=“Form1“>

<SELECT id=“Primary“ onchange=“fillSecondary(this, “Primary“, document.form1.secondary)“><OPTION value=“1“ selected>A list Item</OPTION></SELECT>

<SELECT id=“Secondary“ name=“Secondary“></SELECT>
</FORM>


The classic ASP page (listboxesXML.asp) called by the Javascript looks like this:


Dim xmlDOM   ‘XML DOM object
Dim oRS    ‘Recordset for child records
Dim sXML   ‘XML String returned to browser
Dim oNode   ‘DOM node containing data for searching
Dim sType   ‘The type/name of the unknown child node


set xmlDOM = Server.CreateObject(“MSXML2.DOMDocument”)
xmlDOM.async = false
xmlDOM.Load Request
xmlDOM.setProperty “SelectionLanguage”, “XPath”
if xmlDOM.parseError = 0 then
 ‘Select the request node
    set oParent = xmlDOM.selectSingleNode(“request”)
    ‘Get the child of the request node
    set oNode = oParent.firstChild
    ‘Store the child’s value
    sSearchValue = oNode.Text
    ‘Store the child’s name
    sType = oNode.nodeName

    set oRS = Server.CreateObject(“ADODB.recordset”)

    ‘Fetch the appropriate data into the recordset using sType to tell which
    ‘  secondary list we should be fetching, in this case it is called “secondarytype“

    ‘ADO Code should be here, removed for brevity

 if not oRS.EOF then
  ‘Build a response XML string
  sXML = “
  while not oRS.EOF
   sXML = sXML & “<” & sType & ” id=””” & oRS.Fields(0).Value & “””>”
   sXML = sXML & Server.HTMLEncode(ors.Fields(1).Value) & “   oRS.MoveNext
  wend
  sXML = sXML & “

  oRS.Close
 else
  sXML = “
  sXML = sXML & “<” & sType & ” id=””0″”>”
  sXML = sXML & “None listed  sXML = sXML & “

  oRS.Close
 end if
 set oRS = nothing
Response.Write sXML
else
Err.Raise 1, “ParseError”, “There was a parse error in the request.”
end if


This can be very tricky to debug if you have any problems. Try looking at the xmlHTTP.ResponseText in an alert in the Javascript function to see any 500 errors generated by the server.

Posted on:
Posted in ASP.NET, JavaScript/AJAX | Comments Off on Using XMLHTTP in Javascript