Thursday, April 30, 2015

Responsive Web Design with ASP

Getting off the table, viewport, devicewidth, code-behind and the lifecycle of an ASP page.

Until the nasty gram from Google about de-ranking a clients pages for not properly supporting mobile devices, I was plenty happy. I thought the pages looked fine, and actually adding the viewport header made them look worse on mobile devices. Can't fight google.

I had heard grumblings about tables and how pedestrian they are, but getting off them, it was like losing Flash again. Maybe that is a bit dramatic, but I have learned to coexist with HTML 5 and my Action Script code still has a home, but tables. They really had to go.

Life is simply better without them, F all the matching <tr and </td, good riddance. Tables will live on with html marketing emails, where divs will ruin your day, but divs just blend and they allow for the dynamic wrapping and sizing of the target device for a more robust user experience across devices.

This opened a new can of worms; making device width usable while your page elements are created.  Most static pages can be resolved via CSS, style="width:100%; max-width:640px;"  Yet for complex dynamic pages, you need to know the device capabilities when the page is created serverside.  As an example check out http://aqua8472.com/seeitwork.aspx  The grid elements are created dynamically and based on the device serverside.   In the ASP lifecycle, all server side events process before the page is rendered. That makes sense, the page is essentially static and has no contract back to the server, ajax muddies this, but for all practical purposes, once the server is done with its back end processing, the HTML is rendered and the server has moved on. Now I was facing a chicken and egg scenario, I want to size the elements on my page server-side based on the device. Problem...

To solve this touches on several areas within the page lifecycle. Essentially, I fed my device width via a webservice call and created a session variable with the width for use during server-side processing of the page. Customizing IHttpModule with an event for PostAcquireRequestState
allows for a redirect to a detection page, this detection page via a webservice call creates the session variable. Keep in mind this is only available to asp pages.
 privatevoidOnPostAcquireRequestState(objecto,EventArgsargs)
{
<<snippet>>
HttpApplicationapp=(HttpApplication)o;
BrowserSupport.BrowserInfobi=newBrowserSupport.BrowserInfo(app.Request);
if(bi.mobile)
{
 if(app.Session["deviceWidth"]!=null)
  ProcessASPX(app,newBrowserSupport.BrowserInfo(app.Request));
 else
 {
  if(!sPageRequested.ToLower().Equals("detectdevice.aspx"))
   app.Response.Redirect("/noc/detectdevice.aspx?r="+app.Request.Path,true);
 }
}
else
 ProcessASPX(app,newBrowserSupport.BrowserInfo(app.Request));

Javascript handles the detection of the device width and calls a webservice. Keep in mind, you have created a race condition and you need to wait for the webservice call to complete before redirecting to the original page passed in as a url param from OnPostAcquireState

<metaname="robots"content="noindex,nofollow"/>
<metaname="viewport"content="width=device-width,initial-scale=1"/>
 
<scripttype="text/javascript"lang="javascript"src="/scripts/jquery.js"></script>
 
<scripttype="text/javascript"lang="javascript">
$(document).ready(function(){
 varwindowWidth=(window.innerWidth>0)?window.innerWidth:screen.width;
 jdwebservice.setdevicewidth(windowWidth,
  setDeviceWidthOnSuccess,setDeviceWidthOnFail);
});
 
functionsetDeviceWidthOnSuccess(result){
 if(result){
  varurlp=location.search;
  varoffset=urlp.indexOf('?r=');
  if(offset<0){
   alert('Unabletolocateurlparam:'+urlp);
  }
  else
  {
   window.location=urlp.substring(offset+3);
  }
 }
 else
alert('Wearehavingtroublecommunicatingwithourbackendservers.Contactsupportwithmessagedetectdevice.setDeviceWidth.(result):'+result.get_message());
}
 
functionsetDeviceWidthOnFail(result){
alert('Wearehavingtroublecommunicatingwithourbackendservers.Contactsupportwithmessagedetectdevice.setDeviceWidth.Tryreloadingthispage.'+result.get_message());
}
</script>
 
In the detection page, we want to keep the crawlers happy by notifying them this is a temp 302 redirect and not a 200 success. Response codes can only be set by the server.

protectedvoidPage_Load(objectsender,EventArgse)
{
 Response.Status="302DeviceDetectionRedirect";
 Response.StatusCode=302;
}
The webservice is a simple call and looks like this.

[WebMethod(EnableSession=true)]
publicboolsetdevicewidth(uintdeviceWidth)
{
 boolbResult=false;
 try
 {
  if(Session["deviceWidth"]==null)
   Session.Add("deviceWidth",deviceWidth.ToString());
  else
   Session["deviceWidth"]=deviceWidth.ToString();
  bResult=true;
 }
 catch(ExceptionE)
 {
  Utility.SendEventLogError("("+HttpContext.Current.Request.UserHostAddress+")
  jdWebservices:setdevicewidth,Exception:"+E.Message);
 }
 returnbResult;
}
Now on a page load, for the parent div I set the width at runat server, then on its oninit, use the session device width as below:

<divclass="divIndexHeader"id="ixHeaderDiv"oninit="Div_Init"runat="server">

protectedvoidDiv_Init(objectsender,EventArgse)
{
<snippet>
notice we only mess with mobile devices, desktop are as designed.  
 BrowserSupport.BrowserInfobi=newBrowserSupport.BrowserInfo(Request);
 if(bi.mobile)
 {
  if(Session["deviceWidth"]!=null)
  {
   uintuWindowWidth=0;
   if(!uint.TryParse((string)Session["deviceWidth"],outuWindowWidth))
    thrownewException("UnabletoconvertSession[\"deviceWidth\"]
    toanuint,"+ (string)Session["deviceWidth"]);
   if(uWindowWidth<975)
    ((HtmlGenericControl)sender).Style["Width"]=uWindowWidth+"px";
  }
  else
   thrownewException("Session[\"deviceWidth\"]isnull,
    wasdetectdevicerunfirst?");
 }
}
For embedded elements with width:X%, they all fall in line based on the parent element width.  Having the power of server-side processing, allows for creativity to adjust/remove padding between inline-block or float divs.  
Getting creative, for our low resolution devices, like the iPhone, size your images to take the width of the device, then adjust the padding for higher resolution devices support based on your original layout.

<divid="divTour4"class="divIndexTour"runat="server"oninit="divTour_Init">

if(Session["deviceWidth"]!=null)
 {
  uintuWindowWidth=0;
  if(!uint.TryParse((string)Session["deviceWidth"],outuWindowWidth))
   thrownewException("UnabletoconvertSession...
  if(uWindowWidth<375)
  {
   ((HtmlGenericControl)sender).Style["Width"]=uWindowWidth+"px";
   ((HtmlGenericControl)sender).Style["padding-left"]="0px";
  }
  elseif((uWindowWidth<900)&&(uWindowWidth>376))
  {
   ((HtmlGenericControl)sender).Style["Width"]=(uWindowWidth*.75)+"px";
   ((HtmlGenericControl)sender).Style["padding- 
   left"]=(uWindowWidth*.12)+"px";
  }
 elseif((uWindowWidth<975)&&(uWindowWidth>901))
 {
  uintuPadding=75-((975-uWindowWidth)/2);
   ((HtmlGenericControl)sender).Style["padding-left"]=uPadding+"px";
 }

A good page to check out is http://viewportsizes.com/

Monday, April 27, 2015

Frequenting Pilot and Flying J could cost you thousands of dollars

Frequenting no-name, unbranded fuel sold at Flying J and Pilot because their islands were easier to get to was a bad-habit.  My ignorance of GM's reliance on the use of fuels with a high detergent content as part of injector health over time is manifesting in my injectors on my 05 Workhorse Chassis.  Pilot, which also own's Flying J, does not, has not, and will not sell its customers top-tier fuel and has elected to supply its customers with the low EPA detergent additives which GM states is insufficient to keep its injectors clean.  My loyalty to Flying J/Pilot, and my ignorance believing this was all a marketing ploy developed by Chevron and their Techron additive was a mistake.

This started with a rough idle on my 8.1 and a comment from my service writer on injector cleaning on my duramax.  Online searched took me to X66P ACDelco upper engine and injector cleaner.  My d-max was simple to perform and with my marginal injectors, I saw an immediate improvement in balance rates; all eight are now approaching zero.  So I started looking into doing the process on  my 8.1.   Not so fast.  There are a couple of givens; the process must be done in high concentration 16oz cleaner to 64oz of fuel, which requires a separate fuel supply.  At idle this takes app 2 hours to run through the motor.  The return line goes back to the temp tank and the onboard fuel tank is isolated from the process.  This mixture circulates through the temp-tank over and over until depleted.  The reason this was simple to perform on the duramax (sierra) is the fuel pump and filter are on the fuel rail, which makes sense because the rail on a duramax is over 20,000 psi, unlike an 8.1 where the fuel rail pressure is app 60psi.

On the 8.1, from burbs to boats to workhorse, it appears there is a single fuel pump in or near the tank and fuel regulator on the fuel rail with a return line to the tank.  Now the injector cleaner process becomes more complicated because isolating the fuel tank, also removes the pump and filter.  Now to achieve a highly concentrated cleaner a temp pump and filter is required.

To perform this cleaning on the 8.1, the temp-tank setup must also include a pump and filter, differing from the d-max process where the pump and filter were part of the rail.  Also the on-board fuel pump would need to have its fuse pulled.  This temp pump would need to achieve 56-62 psi or I am assuming the ecm would through a code.

Because of the ease and observable improvement in my injector performance on my d-max, ignorance in running non-top-tier fuel in my 8.1 and a subsequent rough idle, I am investigating how to do this on the 8.1, which is raising more questions.

On the diesel there was little risk, the temp-tank lines were not under high pressure, no external pump or filter were necessary.  Now to DIY on the 8.1, if I understand correctly, I need a high-pressure external fuel pump of at least 60psi and an external filter as part of a temp tank assembly.   The cost of such a pump is not prohibitive, but the high pressure (100psi) on the supply side to the fuel rail is what is giving me caution, the failure point is a hose clamp to a temp fuel line at a hot motor.

To clean the 8.1, I found a pressure cylinder on Zoro.com.  The cylinder is pressurized from external air and has a built in regulator.  Depending on the manufacturer, there is a port on the fuel rail where the cylinder charges the rail.  On Chrysler and GM this port is type Schrader.   To keep the mixture from flowing back to the tank, which can both dilute the mixture and damage the fuel pump, you need to pull the fuse for the fuel pump -and- keep the pressure at least 5 lbs under the pressure of the regulator on the fuel rail.  This will keep the regulator closed and prevent backflow to the tank.  On the 8.1, fuel rail pressure is 60#.  To be on the safe side, I performed the procedure at 50#.
Zoro Canister Fuel Rail Cleaner 

The inconvenient truth is this was all avoidable.  Now I need to unring the bell and hope it is a recoverable error on my part for not educating myself about injector health, balance rates and the differences in EPA requirements and what Arco, Chevron, Shell and even Valero, are putting into their fuels because of what all vehicle manufacturers want.  The damage in frequenting Flying J/Pilot is done and now I need to perform a cleaning process and hope to undo the damage.

Purchasing from Pilot/Flying J over time was a mistake, the remedy was additives at my expense, or a better grade of fuel; but I simply did not appreciate what was happening slowly over time.



Tuesday, April 7, 2015

linqToTwitter example

How to do a quick test of linqToTwitter with VS 2012 .net 45 and forms.

Login with you test account on dev.twitter
Under tools/Manager




This will create the two keys that link your app on twitter to your test application.


Now lets get started

In Visual Studio (2012+), create a new windows form application.
Open the NuGet Package Manager under tools, start the console





Enter install-package linqToTwitter
https://linqtotwitter.codeplex.com/SourceControl/changeset/view/8c63d7991761#BuildScripts/ReadMe.txt

Confirm you projects package.config is up to date with:
https://linqtotwitter.codeplex.com/SourceControl/changeset/view/8c63d7991761#New/Demos/Linq2TwitterDemos_Console/packages.config


You need two more references
System.Configuration 
Microsoft.VisualBasic

Follow the code below:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;
//add references
using System.Configuration;
//this is for the messagebox in vb, not found in c#
using Microsoft.VisualBasic;
//this was added using NUGetPackageManager console PM>install-package linqtotwitter
using LinqToTwitter;  //version 3.1.2
 
/*
 * How to test linqToTwitter with PinAuthentication
 * This is the simplest way to test as there are no callbacks from twitter.
 * Written by David Dold on April 7, 2015 with VB 2012 
 * 
 */
 
/* Before you begin, in dev.twitter.com, setup up a test app under Tools, Manage Your Apps
 * Then add the values in base64 copy and paste directly from twitter into your consumerKey, consumeSecret
 * 
 * <appSettings>
    <add key="consumerKey" value="from dev.twitter" />
    <add key="consumerSecret" value="from dev.twitter" />
    <add key="ClientSettingsProvider.ServiceUri" value="" />  //
  </appSettings>
*/
 
/* compare your packagaes.config with the requirements on codeplex
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Microsoft.Bcl" version="1.1.9" targetFramework="net45" />
  <package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" />
  <package id="Microsoft.Bcl.Compression" version="3.9.85" targetFramework="net45" />
  <package id="Microsoft.Net.Http" version="2.2.28" targetFramework="net45" />
  <package id="Rx-Core" version="2.2.5" targetFramework="net45" />
  <package id="Rx-Interfaces" version="2.2.5" targetFramework="net45" />
  <package id="Rx-Linq" version="2.2.5" targetFramework="net45" />
  <package id="Rx-Main" version="2.2.5" targetFramework="net45" />
  <package id="Rx-PlatformServices" version="2.2.5" targetFramework="net45" />
</packages>
*/
 
namespace TwitterTester
{
    public partial class Form1 : Form
    {
        //be explicit, what is this vb?
        PinAuthorizer auth = null;
        public Form1()
        {
            InitializeComponent();
            try
            {
                auth = new PinAuthorizer()
                {
                    CredentialStore = new InMemoryCredentialStore
                    {
                        ConsumerKey = ConfigurationManager.AppSettings["consumerKey"],
                        ConsumerSecret = ConfigurationManager.AppSettings["consumerSecret"]
                    },
                    GoToTwitterAuthorization = pageLink => Process.Start(pageLink),
                    GetPin = () => 
                    {
                        return (string)Interaction.InputBox("Enter Pin""Twitter"string.Empty, -1,-1);
                    }
                };
                //this returns void, assuming it throws an exception if something is wrong
                //"waiting" is on twitter, this will load the default browser, and then prompt the currently logged in twitter users with "Authorize <<your twitter app>> to use your account?"  
                //from credentials above created in dev.twitter.com "Tools-> Manage Your Apps"
                //you cannot "wait" on this, the handoff to the browswe will not occur
                auth.AuthorizeAsync();
                //this is not blocking, ideally this would block until authorized and then enable the tweet button...  
                button1.Enabled = true;
            }
            catch (Exception E)
            {
                lbInfo.Text = "Exception Constructor: " + E.Message;
            }
 
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                using (TwitterContext twitterCtx = new TwitterContext(auth))
                {
                    //in pure event driven programming this should be a BeginTweet, then a callback to OnFailedTweet or OnCompleteTweet.  
                    //MS added this async/await in vs 2012, .net 45, supposedly this is the same, meaning, this is not blocking and IDispatch is still processing windows messages
                    Task<LinqToTwitter.Status> task = twitterCtx.TweetAsync(textBox1.Text);
                    task.Wait();
  
                    //Task.Result is of type LinqToTwitter.Status
                    if (task != null)
                        if (task.IsCompleted)
                            listBox1.Items.Add("tweeted: ID: " + task.Result.ID);
                        else
                            listBox1.Items.Add("Task failed");
                    else
                        listBox1.Items.Add("failed");
 
                }
            }
            catch(Exception E)
            {
                lbInfo.Text="Exception TweetAsync: "+E.Message;    
            }
        }
    }
}
Run the app.
 
The oAuth requires a code provided by twitter.com to authenticate the currently logged in user to the default browser.  Cut and past the code into the (vb) message box.

Tweet away.

Good luck,

~David