-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
3790 lines (3069 loc) · 310 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[Aidan Ryan]]></title>
<link href="http://www.aidanjryan.com/atom.xml" rel="self"/>
<link href="http://www.aidanjryan.com/"/>
<updated>2017-01-20T05:47:24-08:00</updated>
<id>http://www.aidanjryan.com/</id>
<author>
<name><![CDATA[Aidan Ryan]]></name>
</author>
<generator uri="http://octopress.org/">Octopress</generator>
<entry>
<title type="html"><![CDATA[Exist-Spoon - AbstractSpoon ToDoList connection for exist.io]]></title>
<link href="http://www.aidanjryan.com/blog/2017/01/19/new-post/"/>
<updated>2017-01-19T16:41:00-08:00</updated>
<id>http://www.aidanjryan.com/blog/2017/01/19/new-post</id>
<content type="html"><![CDATA[<p>I use the fantastic <a href="http://abstractspoon.weebly.com/">AbstractSpoon ToDoList</a> for task tracking. I recently discovered the <a href="http://exist.io">exist.io</a> personal metrics app and wanted to track my daily completed tasks.</p>
<p>I created an integration to do just that, and used it as an opportuniy to do a little development in .NET Core. You can find <a href="http://github.com/ajryan/exist-spoon">exist-spoon</a> on my <a href="http://github.com/ajryan">GitHub page</a>.<!--more--></p>
<p>The exist.io API is simple to work with. The only tricky part was implementing the OAuth authenticaiton flow on a console app. OAuth requires you to serve a page where the user’s browser will be redirected after completing the authorization. This page accepts the token provided by the parter (exist.io in this case).</p>
<p>There is no <code>HttpListener</code> in .NET Core, so I started from the web application skeleton and stripped everything down as much as possible. As usually happens with async work, I wrote way too many <code>Task<T></code> and <code>await</code>s before arriving at the final, simpler structure. I punted on the TDL file format and used a simple regex to parse out when a task has <code>DONEDATE</code> equal to today.</p>
<p>The tool isn’t too complicated, but it’s gotten my juices flowing after a while without working on personal projects. It was cool to get a shoutout from one of exist.io’s creators (Hi <a href="https://twitter.com/joshsharp">Josh</a>!) too.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Authenticating ASP.NET Web API with Azure Mobile Services]]></title>
<link href="http://www.aidanjryan.com/blog/2014/03/05/authenticating-asp-dot-net-web-api-with-azure-mobile-services/"/>
<updated>2014-03-05T09:56:00-08:00</updated>
<id>http://www.aidanjryan.com/blog/2014/03/05/authenticating-asp-dot-net-web-api-with-azure-mobile-services</id>
<content type="html"><![CDATA[<p>Azure Mobile Services provides a really easy way to integrate social login into web, mobile, and desktop applications. At Magenic, we’re using it in our client apps for the <a href="http://modernappslive.com/Events/Las-Vegas-2014/Home.aspx">Modern Apps Live!</a> conference demo application called <a href="http://myvotelive.com">MyVote</a>. The web application and the native mobile clients share a common Web API backend deployed to a Web Role on Azure Cloud Services. For most of the Web API methods, we only want to allow calls from users who have successfully authenticated with Azure Mobile Services. Let’s dig into what it takes to develop a Web API authentication handler that verifies claims issued by Azure Mobile Services.<!--more--></p>
<h2>Scenario</h2>
<p>This authentication method applies to the following scenario:</p>
<ul>
<li>Azure Mobile Services is set up for Social Authentication. See <a href="http://azure.microsoft.com/en-us/documentation/articles/mobile-services-html-get-started-users/">here</a> for instructions.</li>
<li>Users authenticate on the client (browser) side using the Azure Mobile Services JavaScript SDK. The latest SDK at the time of this writing is version 1.1.3, and can be found <a href="http://ajax.aspnetcdn.com/ajax/mobileservices/MobileServices.Web-1.1.3.min.js">here</a>.</li>
<li>You have ASP.NET Web API services that you want to expose only to users who have authenticated with Azure Mobile Services.</li>
</ul>
<p>Normally we are authenticating users on the same system that issues credentials - think standard ASP.NET membership stuff. In this case, our Web API system needs to trust credentials issued by a third party.</p>
<h2>Method</h2>
<p>Here is a quick refresher on login with Azure Mobile Services:</p>
<figure class='code'><figcaption><span>ZuMo Authentication </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">var</span> <span class="nx">zumoUrl</span> <span class="o">=</span> <span class="s2">"https://your-zumo-service.azure-mobile.net"</span><span class="p">;</span>
</span><span class='line'><span class="kd">var</span> <span class="nx">zumoKey</span> <span class="o">=</span> <span class="s2">"your-zumo-application-key"</span><span class="p">;</span>
</span><span class='line'><span class="kd">var</span> <span class="nx">client</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">WindowsAzure</span><span class="p">.</span><span class="nx">MobileServiceClient</span><span class="p">(</span><span class="nx">zumoUrl</span><span class="p">,</span> <span class="nx">zumoKey</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// options are facebook, twitter, microsoft, or google.</span>
</span><span class='line'><span class="c1">// you must set up each provider in the azure management portal.</span>
</span><span class='line'><span class="nx">client</span><span class="p">.</span><span class="nx">login</span><span class="p">(</span><span class="s2">"facebook"</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">alert</span><span class="p">(</span><span class="s1">'login succeeded'</span><span class="p">);</span>
</span><span class='line'><span class="p">},</span> <span class="kd">function</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">alert</span><span class="p">(</span><span class="nx">error</span><span class="p">);</span>
</span><span class='line'><span class="p">});</span>
</span></code></pre></td></tr></table></div></figure>
<p>When a user successfully logs in with Azure Mobile Services, the <code>client.currentUser</code> field is set. This in turn exposes a <code>mobileServiceAuthenticationToken</code> field, which is a JSON Web Token (JWT). JWT is an <a href="http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-07">emerging standard</a> for representing authentication information. It is used by many OAuth implementations, including Azure Mobile Services.</p>
<p>In order to verify that users have truly authenticated with Azure Mobile Services, we will rely on a ”<a href="http://en.wikipedia.org/wiki/Shared_secret">shared secret</a>” known only to Azure Mobile Services and to us. The JWT issued to the user is cryptographically signed by Azure Mobile Services using the Master Key unique to our service instance. We have access to this key via the management portal, and we can use it in our Web API code to verify that a JWT was truly issued and signed by our Azure Mobile Services instance.</p>
<p>Here’s a quick diagram that sums it up:</p>
<p> <img src="http://www.aidanjryan.com/images/zumo_auth.PNG" title="Zumo Auth" alt="Zumo Auth"></p>
<p>The standard for a client to present a JWT for authentication to a server is to set the request’s Authorization header to “Bearer <JWT>” where <JWT> is the actual Base64-encoded JSON Web Token. MyVote is using AngularJS, so I set up an HTTP interceptor to set the header on every request:</p>
<figure class='code'><figcaption><span>AngularJS Authorization Header Interceptor </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">MyVote</span><span class="p">.</span><span class="nx">App</span> <span class="o">=</span> <span class="nx">angular</span><span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="s1">'MyVoteApp'</span><span class="p">,</span> <span class="p">[</span><span class="s1">'ngRoute'</span><span class="p">,</span> <span class="s1">'ngResource'</span><span class="p">]);</span>
</span><span class='line'>
</span><span class='line'><span class="nx">MyVote</span><span class="p">.</span><span class="nx">App</span><span class="p">.</span><span class="nx">factory</span><span class="p">(</span><span class="s1">'zumoAuthInterceptor'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="k">return</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">request</span><span class="o">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">config</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="nx">Globals</span><span class="p">.</span><span class="nx">zumoUserKey</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">config</span><span class="p">.</span><span class="nx">headers</span><span class="p">[</span><span class="s1">'Authorization'</span><span class="p">]</span> <span class="o">=</span> <span class="s1">'Bearer '</span> <span class="o">+</span> <span class="nx">MyVote</span><span class="p">.</span><span class="nx">Services</span><span class="p">.</span><span class="nx">AuthService</span><span class="p">.</span><span class="nx">zumoUserKey</span><span class="p">;</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="k">return</span> <span class="nx">config</span><span class="p">;</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">};</span>
</span><span class='line'><span class="p">});</span>
</span><span class='line'>
</span><span class='line'><span class="nx">MyVote</span><span class="p">.</span><span class="nx">App</span><span class="p">.</span><span class="nx">config</span><span class="p">([</span>
</span><span class='line'> <span class="s1">'$routeProvider'</span><span class="p">,</span> <span class="s1">'$httpProvider'</span><span class="p">,</span>
</span><span class='line'> <span class="kd">function</span> <span class="p">(</span><span class="nx">$routeProvider</span><span class="p">,</span> <span class="nx">$httpProvider</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">$httpProvider</span><span class="p">.</span><span class="nx">interceptors</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="s1">'zumoAuthInterceptor'</span><span class="p">);</span>
</span><span class='line'> <span class="c1">// -- snip -- other config stuff here -- //</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">]);</span>
</span></code></pre></td></tr></table></div></figure>
<p>On the server side, authentication is implemented in Web API via a delegating handler. The process begins here:</p>
<figure class='code'><figcaption><span>JWT Handler </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
</pre></td><td class='code'><pre><code class='csharp'><span class='line'><span class="k">protected</span> <span class="k">override</span> <span class="n">Task</span><span class="p"><</span><span class="n">HttpResponseMessage</span><span class="p">></span> <span class="n">SendAsync</span><span class="p">(</span><span class="n">HttpRequestMessage</span> <span class="n">request</span><span class="p">,</span> <span class="n">CancellationToken</span> <span class="n">cancellationToken</span><span class="p">)</span>
</span><span class='line'><span class="p">{</span>
</span><span class='line'> <span class="c1">// browser sends OPTIONS requests to check CORS and will not include Authorization header</span>
</span><span class='line'> <span class="c1">// allow these requests to flow through with the response from the CORS handler</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="n">request</span><span class="p">.</span><span class="n">Method</span> <span class="p">!=</span> <span class="n">HttpMethod</span><span class="p">.</span><span class="n">Options</span><span class="p">)</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="kt">string</span> <span class="n">token</span><span class="p">;</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="n">TryRetrieveToken</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="k">out</span> <span class="n">token</span><span class="p">))</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="k">try</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="kt">var</span> <span class="n">jwt</span> <span class="p">=</span> <span class="k">new</span> <span class="n">JsonWebToken</span><span class="p">(</span><span class="n">token</span><span class="p">,</span> <span class="k">new</span> <span class="n">Dictionary</span><span class="p"><</span><span class="kt">int</span><span class="p">,</span> <span class="kt">string</span><span class="p">></span> <span class="m">0</span><span class="p">);</span>
</span><span class='line'> <span class="n">jwt</span><span class="p">.</span><span class="n">Validate</span><span class="p">(</span><span class="n">validateExpiration</span><span class="p">:</span> <span class="k">true</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'> <span class="n">MyVoteAuthentication</span><span class="p">.</span><span class="n">SetCurrentPrincipal</span><span class="p">(</span><span class="k">new</span> <span class="n">MyVotePrincipal</span><span class="p">(</span><span class="n">jwt</span><span class="p">.</span><span class="n">Claims</span><span class="p">.</span><span class="n">UserId</span><span class="p">));</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="k">catch</span> <span class="p">(</span><span class="n">JsonWebTokenException</span><span class="p">)</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="k">return</span> <span class="k">base</span><span class="p">.</span><span class="n">SendAsync</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">cancellationToken</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>In the <code>SendAsync</code> override, we attempt to validate the JWT. If it is valid, we call <code>MyVoteAuthentication.SetCurrentPrincipal</code> which sets <code>HttpContext.Current.User</code>. This allows us to simply add the <code>[Authorize]</code> attribute to Web API controllers or actions where we want to require Azure Mobile authentication. Note the <code>_masterKey</code> field that is included in the <code>JsonWebToken</code> contructor call. This is the Azure Mobile Services Master Key (shared secret) that we trust as the cryptographic signer of valid JWTs.</p>
<p>The relevant bit inside <code>JsonWebToken</code> that validates the signature follows:</p>
<figure class='code'><figcaption><span>JsonWebToken.ValidateSignature </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
</pre></td><td class='code'><pre><code class='csharp'><span class='line'><span class="k">private</span> <span class="k">void</span> <span class="nf">ValidateSignature</span><span class="p">()</span>
</span><span class='line'><span class="p">{</span>
</span><span class='line'> <span class="c1">// Derive signing key, Signing key = SHA256(secret + "JWTSig")</span>
</span><span class='line'> <span class="kt">byte</span><span class="p">[]</span> <span class="n">bytes</span> <span class="p">=</span> <span class="n">_UTF8Encoder</span><span class="p">.</span><span class="n">GetBytes</span><span class="p">(</span><span class="n">_signatureKey</span> <span class="p">+</span> <span class="s">"JWTSig"</span><span class="p">);</span>
</span><span class='line'> <span class="kt">byte</span><span class="p">[]</span> <span class="n">signingKey</span> <span class="p">=</span> <span class="n">_Sha256Provider</span><span class="p">.</span><span class="n">ComputeHash</span><span class="p">(</span><span class="n">bytes</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'> <span class="c1">// UFT-8 representation of the JWT envelope.claim segment</span>
</span><span class='line'> <span class="kt">byte</span><span class="p">[]</span> <span class="n">input</span> <span class="p">=</span> <span class="n">_UTF8Encoder</span><span class="p">.</span><span class="n">GetBytes</span><span class="p">(</span><span class="n">_envelopeTokenSegment</span> <span class="p">+</span> <span class="s">"."</span> <span class="p">+</span> <span class="n">_claimsTokenSegment</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'> <span class="c1">// calculate an HMAC SHA-256 MAC</span>
</span><span class='line'> <span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">hashProvider</span> <span class="p">=</span> <span class="k">new</span> <span class="n">HMACSHA256</span><span class="p">(</span><span class="n">signingKey</span><span class="p">))</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="kt">byte</span><span class="p">[]</span> <span class="n">myHashValue</span> <span class="p">=</span> <span class="n">hashProvider</span><span class="p">.</span><span class="n">ComputeHash</span><span class="p">(</span><span class="n">input</span><span class="p">);</span>
</span><span class='line'> <span class="kt">string</span> <span class="n">base64UrlEncodedHash</span> <span class="p">=</span> <span class="n">Base64UrlEncode</span><span class="p">(</span><span class="n">myHashValue</span><span class="p">);</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="n">base64UrlEncodedHash</span> <span class="p">!=</span> <span class="n">Signature</span><span class="p">)</span>
</span><span class='line'> <span class="k">throw</span> <span class="k">new</span> <span class="nf">JsonWebTokenException</span><span class="p">(</span><span class="s">"Signature does not match."</span><span class="p">);</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>I borrowed liberally from the <a href="https://github.com/liveservices/LiveSDK/blob/master/Samples/Asp.net/AuthenticationTokenSample/JsonWebToken.cs">Windows Live SDK sample</a> for this. There is one gotcha - the JWT spec indicates that a claims “exp” member should be expressed as a double, but Azure Mobile Services uses an integer.</p>
<p>You can find all of the JWT authentication-related code in the MyVote GitHub repository, in the <a href="https://github.com/Magenic/MyVote/tree/master/src/MyVote.AppServer/Auth">MyVote.AppServer.Auth</a> namespace.</p>
<p><strong>WARNING</strong>: Do not expose your Azure Mobile Services Master Key! For example, DON’T put the key in <code>appSettings</code> in your Web.config and then host your code on GitHub. You should rely on the secure configuration facility supplied by your hosting environment. If your Web API is hosted in Azure via Web Sites or Cloud Services, you can securely set <code>appSettings</code> values from within the management portal.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Modern Apps Live! Looking back, looking forward]]></title>
<link href="http://www.aidanjryan.com/blog/2014/02/05/modern-apps-live-orlando-2013/"/>
<updated>2014-02-05T15:34:00-08:00</updated>
<id>http://www.aidanjryan.com/blog/2014/02/05/modern-apps-live-orlando-2013</id>
<content type="html"><![CDATA[<p>I presented at the <a href="http://modernappslive.com">Modern Apps Live!</a> conference in Orlando this past November. The whole show was a smashing success. It was an incredible opportunity to connect with peers and colleages, taking a step back to think about the “big picture” of where application development is headed. The next show will be in Las Vegas on March 10-14. I’ll be presenting on building a Modern HTML5 “smart client” application. Use my <a href="http://bit.ly/LSPK38Reg">registration link</a> for a $500 savings!<!--more--></p>
<h2>From backend to user experience</h2>
<p><a href="http://www.magenic.com">Magenic</a> has created a unique conference experience where the sessions build on one another to tell the whole story of building a modern application from start to finish. The common thread that ties the sessions together is the creation of a single application platform (a polling app called “MyVote”) delivered on diverse clients: Web, iOS, Android, Windows 8 (WinRT), and Windows Phone. The backend is all Azure - SQL and a Web API Cloud Service. The entire development effort was distributed, using Git on Visual Studio Online.</p>
<p>The Magenic team who delivers the conference talks are the same folks who developed the apps. All of the talks emphasize the architecture/design perspective: choosing the right components and patterns for reliable, predictable, successful dlivery. The conference program begins with sessions focused on solution architecture and the backend, follows with development of the various clients, user experience design, and touches on process and tooling around distributed development. We also do a deep-dive workshop on the last day where we dig into the actual nuts and bolts of implementation.</p>
<h2>My talk: HTML5/JavaScript application development</h2>
<p>My talk is about the development of an HTML5 “smart client” app. It is a Single Page App (SPA) developed with AngularJS, TypeScript, and Razor. I describe the benefits of taking an app the web, selection of a JavaScript application framework, architecture of the app components, testing, and cross-cutting concerns. I am really excited about this combination of technologies: AngularJS provides an “opinionated” framework that is very discoverable, and the combination of TypeScript and Razor templating allows us to extend the type-safety of .NET out into the client.</p>
<h2>Next time…</h2>
<p>We’re bringing the show to Vegas on March 10-14. We’ve added some features to the apps, and updated them to keep pace with advancements in their various platforms. Hope to see you <a href="http://modernappslive.com">there</a>!</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Silicon Valley Code Camp 2013 - Responsive Re-Engineering]]></title>
<link href="http://www.aidanjryan.com/blog/2013/10/12/silicon-valley-code-camp-2013-responsive-re-engineering/"/>
<updated>2013-10-12T10:34:00-07:00</updated>
<id>http://www.aidanjryan.com/blog/2013/10/12/silicon-valley-code-camp-2013-responsive-re-engineering</id>
<content type="html"><![CDATA[<p>I spoke at <a href="http://www.siliconvalley-codecamp.com/">Silicon Valley Code Camp</a> on October 5th. The title of my talk was “Responsive Re-Engineering,” and it focused on adapting desktop-only web sites to be mobile friendly. I want to thank the organizers and all of the folks who attended the talk. If any of you are reading this and you’d like to talk more about Responsive Web Design, <a href="http://twitter.com/ajryan">get in touch</a>! It was a great experience: lots of great questions and an awesome venue. The <a href="https://github.com/ajryan/CodeCamp2013">code samples</a> and <a href="https://www.slideshare.net/AidanRyan2/responsive-reengineering">slides</a> are online.<!--more--></p>
<p>If your company does ASP.NET development and you are interested in learning how to leverage Responsive Web Design, I would be happy to bring my show on the road. Email me: aidanr (at) magenic.com.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[TFS Test Steps Editor 2.1.0]]></title>
<link href="http://www.aidanjryan.com/blog/2013/09/04/tfs-test-steps-editor-2-dot-1-0/"/>
<updated>2013-09-04T07:15:00-07:00</updated>
<id>http://www.aidanjryan.com/blog/2013/09/04/tfs-test-steps-editor-2-dot-1-0</id>
<content type="html"><![CDATA[<p>I just published a new release of my <a href="http://teststepseditor.codeplex.com">TFS Test Steps Editor</a>. I’m pretty excited about this one, as it includes the first community contribution to the project!<!--more--> GitHub user <a href="https://github.com/mikepoz59">mikepoz59</a> built an awesome work item query / test case picker UI.</p>
<p>For this release, I have created two binary packages - one targeted at the TFS2010 API and another for the TFS2012 API. My error tracking system has been regularly emailing me reports from users who do not have the TFS2010 client assemblies installed, and a few discussions have cropped up in CodePlex reporting some TFS2012 formatting issues. I’m hoping this resolves both problems.</p>
<p>Visit the <a href="http://teststepseditor.codeplex.com">project</a> at CodePlex and pick up either the <a href="https://teststepseditor.codeplex.com/downloads/get/724002">TFS2010</a> or <a href="https://teststepseditor.codeplex.com/releases/view/111691">TFS2012</a> release.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Hide the "send feedback" smile in VS2013]]></title>
<link href="http://www.aidanjryan.com/blog/2013/07/01/hide-the-send-feedback-smile-in-vs2013/"/>
<updated>2013-07-01T14:39:00-07:00</updated>
<id>http://www.aidanjryan.com/blog/2013/07/01/hide-the-send-feedback-smile-in-vs2013</id>
<content type="html"><![CDATA[<p>Here’s a quick tip for those of you who’ve jumped into the Visual Studio 2013 Preview: hiding the “send feedback” smiley-face button in the upper-right of the window. I find it very distracting, and I know how to find the Connect site if I need to file a bug report. I searched the <code>HKEY_CURRENT_USER</code> Registry hive under <code>Software\Microsoft\VisualStudio\12.0_Config</code> for “feedback” and found a key named <code>{F66FBC48-9AE4-41DC-B1AF-0D64F0F54A07}</code> under <code>MainWindowFrameControls</code>. Its default value was “Feedback Button,” which sounded like what I was looking for. After backing up the key to disk, I deleted the key and re-started Visual Studio. No more smiley!</p>
<p>Delete <code>HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\12.0_Config\MainWindowFrameControls\{F66FBC48-9AE4-41DC-B1AF-0D64F0F54A07}</code>.<!--more--></p>
<p><img src="http://www.aidanjryan.com/images/vs2013_smiley_reg.png" title="VS2013 Feedback registry location" alt="VS2013 Feedback registry location"></p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Responsive Web Design - Introduction for ASP.NET MVC Developers]]></title>
<link href="http://www.aidanjryan.com/blog/2013/05/29/responsive-design-introduction-for-asp-dot-net-mvc-developers/"/>
<updated>2013-05-29T12:53:00-07:00</updated>
<id>http://www.aidanjryan.com/blog/2013/05/29/responsive-design-introduction-for-asp-dot-net-mvc-developers</id>
<content type="html"><![CDATA[<p>The term “Responsive Web Design” was coined in 2010 by Ethan Marcotte in his <a href="http://alistapart.com/article/responsive-web-design">canonical article</a> defining the technique. Recently, I spent some time researching the history and modern state of the art of Response Web Design (RWD). This article presents a survey of my findings, and provides examples of specific techniques. We’ll focus on Visual Studio / ASP.NET MVC tools and techniques for getting the job done.<!--more--></p>
<h2>Defining Responsive</h2>
<p>In the context of Responsive Web Design, the term “responsive” is the opposite of “prescriptive.” You’ve probably heard arguments against prescriptive approaches to language and grammar. Language and meaning are organic, evolving things that are constantly being re-created by their users. Prescriptivism in language does not account for its evolution, and can ignore the context and needs of its users. We have experienced “Prescriptive Web Design” from the dawn of the web, in the form of “best viewed in Netscape 4.0.3 on a 800 x 600 monitor at 16-bit color depth.” The modern variation is a placeholder page replacing desired content with “sorry, this page not formatted for mobile devices” How short-sighted, to actually hide content from valuable eyeballs? Responsive Web Design is decidedly <em>not</em> prescriptive.</p>
<p>Responsive Web Design is a set of techniques that allow our pages to adapt to the capabilities of the client. Client, in this case, being both the electronic and human consumer of our content. The goal is to ensure that our content can be comfortably and accessibly consumed by as many devices and people, of varying capabilities, as possible.</p>
<p>These techniques respond to</p>
<ul>
<li><p>Browser capabilities (HTML5, native audio/video, CSS3, plugins)</p></li>
<li><p>Device capabilities (geolocation, resolution, input methods)</p></li>
<li><p>User capabilities (vision, hearing)</p></li>
<li><p>Viewport (media type, resolution, density, orientation)</p></li>
</ul>
<p>Prescriptive designs enforce requirements on the client: they require certain screen sizes, input devices, or plugin support to fully render content. Responsive designs inquire about the capabilities of the client and put forth the best possible experience given those capabilities. They are as functional as possible in limited scenarios and enhance the experience as capabilities increase.</p>
<h2>Why?</h2>
<p>Why use responsive techniques? The sweet spot for RWD is a balance of development effort and reach: responsive design techniques will get your content in front of the most eyeballs, in a usable form, the the least effort. This form may not be optimal for every client scenario, but the aim is to find an appropriate compromise where required, and to take advantage of advanced capabilities where possible.</p>
<p>Consider the polar approaches:</p>
<ul>
<li><p>The cheapest option is to design only for desktop and cross your fingers for mobile users. Desktop clients less restrictive and therefore simpler to target.</p></li>
<li><p>The most expensive option is to implement separate designs for phone, tablet and desktop (potentially even developing native mobile clients.) You will deliver the most optimal experience for each device, at greatly increased cost of development <em>and maintenance.</em></p></li>
</ul>
<p>Beyond the cost-reach compromise, RWD provides several other benefits:</p>
<ul>
<li><p><strong>Consistency</strong>: Delivery of a consistent experience across devices. Styling, navigation, and user experience will be familiar and aid users who arrive at your site using different clients at different times. There is a single set of markup and CSS to maintain, reducing the chance of styles “evolving apart” over time.</p></li>
<li><p><strong>Value First</strong>: RWD forces you to first examine the value of the content being delivered, then decide how to present it. The tendancy when designing for a single platform can be to dive into <em>Lorem Ipsum</em> wireframes before content is fully realized. A content-first approach results in a design that exists purely to support the maximum transmission of the value of content; wireframe-first approaches can result in designs with vestigial features that distract from content value.</p></li>
</ul>
<p>Responsive design is about delivering the greatest possible user joy regardless of the access method. It is not a dogma or a recipe, it’s more of a mindset that is focused on experience first.</p>
<h2>Why Not?</h2>
<p>Responsive techniques are not useful or cost-effective for all applications. When the client capabilities are fully controlled, it is not necessary to design for multiple clients. Enterprise applications being deployed across known devices are the most common scenario here. Solutions that require specific interaction modes may not benefit from RWD, for example, interactions that are simply not possible on a touch device.</p>
<h2>History</h2>
<p>Here is a brief timeline of some influential articles that have led to the current state of thinking on Responsive Web Design:</p>
<ul>
<li><p><a href="http://alistapart.com/article/dao">John Allsopp - A Dao of Web Design</a>: Letting go of control in favor of accepting and adapting to client differences.</p></li>
<li><p><a href="http://alistapart.com/article/responsive-web-design">Ethan Marcotte - Responsive Web Design</a>: Original articulation of the principles.</p></li>
<li><p><a href="http://blog.cloudfour.com/css-media-query-for-mobile-is-fools-gold/">Jason Grigsby - CSS Media Query for Mobile is Fool’s Gold</a>: First major rebuttal, warning that bandwidth suffers - additional HTML, CSS, and (potentially) JavaScript to render on a smaller screen with <em>more</em> code. Important for pivoting the discussion toward general adaptability away from a mobile “silver bullet.”</p></li>
<li><p><a href="http://alistapart.com/article/organizing-mobile">Luke Wroblewski - Mobile First</a>: Refinement of responsive design approach working from most-constrained to least.</p></li>
</ul>
<h2>Core Techniques</h2>
<p>The original <strong>Responsive Web Design</strong> article defined the approach as being composed of three techniques: a Fluid Grid, Media Queries, and Flexible Images. Thinking in these areas has evolved a bit since the original article – here are some thoughts on each of the original techniques:</p>
<ul>
<li><p><strong>Fluid Grid (Adaptive Grid)</strong> The original article names it “fluid,” but this has come to mean proportional, which is not strictly required. An adaptive grid (and an adaptive layout in general) provides a framework for adjusting presentation to window sizes, resolutions, and screen densities. The earliest fluid layouts were focused on achieving proportional columns using CSS (a technique that was trivial using the common table-based layouts at the time). Further developments of adaptive layout are more focused on typography, especially given the emergence of high-density displays that make pixel- and point-sizing less reliable. A fully-fledged adaptive grid framework (potentially combined with a pre-defined base style set) can be a nice productivity boost.</p></li>
<li><p><strong>Media Queries</strong> The media query is a CSS3 feature that allows you to target CSS rules at specific ranges of window or screen size. The most common application of media queries in RWD is for adjusting the layout by repositioning or hiding certain elements as the available space is reduced.</p></li>
<li><p><strong>Flexible Images (Adaptive Media)</strong> The original article focuses on flexibly sizing and positioning images for smaller screen dimensions and lower bandwidth. Further thinking in this area has included selectively loading smaller image files in lower-bandwidth scenarios. With the addition of HTML5 Video and Audio capabilities, these media types should be considered as well.</p></li>
</ul>
<p>It is absolutely not essential to use all of these techniques in every web design effort. These primary techniques form the foundation of RWD thinking, and many additional techniques have been developed along the way. There is a toolbox available, and you can choose the appropriate tools for each job.</p>
<h2>Grid Systems</h2>
<h3>Grid System Rationale</h3>
<p>Many responsive sites rely on a grid system for layout. Abstracting the foundation of the layout provides a clean, consistent framework for positioning site components. A grid can be defined as a horizontal sectioning of the canvas in to columns and gutters. Content lives in columns, and gutters provide whitespace between them. Grids help us think about design, and they help users engage with your content. You can compare a CSS grid to the “snap to grid” function provided in form designer and presentation software: it provides convenient, consistent alignment and spacing for the elements on a page.</p>
<p>Several frameworks and generators exist that provide grid systems. They fall into four basic categories:</p>
<p>• <strong>Fixed</strong>: the container width is set to a fixed width, and the column count, column width, and gutter width are set to fixed fractions of the container. Column spanning is possible, but the container will never resize with the window.</p>
<p>• <strong>Responsive</strong>: a set of media queries provides a progressive step-down of container width, column count, column width, and gutter width.</p>
<p>• <strong>Fluid</strong>: the column count is fixed; container width, column width, and gutter width are percentages of the canvas.</p>
<p>• <strong>Fluid + Responsive</strong>: media queries set the container width and column count; within each breakpoint, column and gutter widths are proportional.</p>
<h3>Grid System Framework Comparison</h3>
<p>There are dozens of CSS grid frameworks available, at varying degrees of complexity, maturity, and compatibility. This list selects some notable popular frameworks that focus on different use cases, roughly ordered from simplest to more fully-featured. New ones are constantly being released that build on principles developed in those that came before.</p>
<table class="content-table">
<tr>
<th>Framework</th><th>Classification</th><th>Max size</th><th>Columns</th><th>Notes</th>
</tr>
<tr>
<td><a href="http://960.gs">960gs</a></td><td>Static</td><td>960px</td><td>12 / 16</td><td>Grid only<br/>IE7+<br/>CSS</td>
</tr>
<tr>
<td><a href="http://www.getskeleton.com/">Skeleton</a></td>
<td>Responsive</td>
<td>960px (desktop/tablet-landscape)<br/>768px (tablet-portrait)<br/>420px (mobile-landscape)<br/>300px (mobile-portrait)</td>
<td>16</td>
<td>Lightweight CSS framework<br/>IE7+<br/>CSS</td>
</tr>
<tr>
<td><a href="http://responsive.gs/">responsive.gs</a></td>
<td>Fluid + Responsive</td>
<td>Any<br/>Columns stack below 768px</td>
<td>12/16/2024</td>
<td>Grid only<br/>IE7+<br/>CSS</td>
</tr>
<tr>
<td><a href="http://neat.bourbon.io/">Bourbon Neat</a></td><td>Fluid + Responsive</td><td>Any</td><td>12<br/>(or custom)</td><td>Grid addon to Bourbon<br/>IE9+<br/>Sass</td>
</tr>
<tr>
<td><a href="http://twitter.github.io/bootstrap/">Bootstrap</a></td>
<td>Static OR<br/>Fluid OR<br/>Fluid + Responsive</td>
<td>Static: 940px<br/>Others: Any (nestable)</td>
<td>16</td>
<td>Full client-side framework<br/>IE6+<br/>LESS</td>
</tr>
</table>
<p>Some additional notes on the grid frameworks:</p>
<ul>
<li><p><strong>960gs</strong> The granddaddy. A good starting point to understand the concepts to launch you toward rolling your own. Viable for sites that are likely to have only desktop traffic. Consider 1140 grid given rise of wider screens.</p></li>
<li><p><strong>Skeleton</strong> Relatively minimal and easily customizable. Provides a layout skeleton (output) and CSS skeleton (source) with reset/sane defaults /media-query breakpoints for you to customize. Has not been maintained in a while (lots of open Github issues) - the dmur fork is more active.</p></li>
<li><p><strong>Bourbon Neat</strong> Relies exclusively on Sass mixins - no classes applied to markup. Extremely customizable.</p></li>
<li><p><strong>Responsive.gs</strong> Enforces border-box box-sizing model on all elements.</p></li>
<li><p><strong>Bootstrap</strong> Probably the best-known (or at least most buzzed-about) client-side framework available. A complete suite of layout, styling, and input/interactivity tools. Tends to leave a recognizable stamp on the appearance of the site.</p></li>
</ul>
<p>Note that responsive features rely on media queries - grids with responsive features will serve either the desktop or mobile view to <= IE8 depending on whether they are “mobile first” or “desktop first.” JavaScript polyfills may be leveraged to extend back-compatibility to earlier browsers than those targeted by the framework.</p>
<p>It can be difficult to adapt an existing project to use a CSS framework - the framework may rely on certain style reset features, typographic assumptions, box-model settings, or other styling techniques that are not compatible with the existing codebase. Even for new work, adoption of a framework can assert a certain identifiable look and feel (for example, the Bootstrap buttons and navbar). It is worth spending time customizing the base style to ensure unique branding. This will often require you to learn at least the basics of a CSS preprocessor tool like LESS or Sass.</p>
<h3>Grid System Demo</h3>
<p>A page demonstrating the <a href="http://responsive.gs">responsive.gs</a> grid system can be found here: <a href="http://codecampresponsive.apphb.com/Home/ResponsiveGs">DEMO</a>. View the page on a mobile device and/or experiment with resizing the browser window.</p>
<p>Responsive.gs provides a very lightweight framework purely focused on grid layout; it does not enforce any look-and-feel on the site. The framework applies the <code>box-sizing: border-box</code> CSS rule to all elements, enabling a simpler sizing model. This rule makes it easier to calculate sizing in layouts: it changes the box size calculation to include padding and border in the overall height and width of a box. Due to this low-level rule, adoption of responsive.gs into an existing layout could result in some painful size adjustments. However, you’ll find that starting fresh with this rule applied can make layout size calculations much more intuitive.</p>
<p>The framework is proportionally fluid at widths above 768px, while all columns stack vertically at 100% width on smaller screens. It’s very simple to lay out columns with responsive.gs: apply the <code>row</code> class to define a full-width row, then use the <code>col</code> and <code>span_*</code> classes to set proportional column widths within the row.</p>
<figure class='code'><figcaption><span>responsive.gs </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='html'><span class='line'><span class="nt"><section</span> <span class="na">class=</span><span class="s">"row"</span><span class="nt">></span>
</span><span class='line'> <span class="nt"><article</span> <span class="na">class=</span><span class="s">"col span_6 bg4"</span><span class="nt">><h3></span>col span_6<span class="nt"></h3><img</span> <span class="na">class=</span><span class="s">"left-quarter"</span> <span class="na">src=</span><span class="s">"http://fillmurray.com/200/300"</span><span class="nt">><p></span>Column 1.<span class="nt"></p></article></span>
</span><span class='line'> <span class="nt"><article</span> <span class="na">class=</span><span class="s">"col span_6 bg3"</span><span class="nt">><h3></span>col span_6<span class="nt"></h3><p></span>Column 2.<span class="nt"></p></article></span>
</span><span class='line'> <span class="nt"><article</span> <span class="na">class=</span><span class="s">"col span_4 bg2"</span><span class="nt">><h3></span>col span_4<span class="nt"></h3><p></span>Column 3.<span class="nt"></p></article></span>
</span><span class='line'><span class="nt"></section></span>
</span></code></pre></td></tr></table></div></figure>
<p>Compatibility notes: Because the <code>box-sizing</code> rule is not supported on IE7 or lower, a polyfill (for inclusion using conditional comments) is provided. The framework is “mobile first”, in that all columns are laid out to 100% width in the base style, then set to proportional widths within a media query. This means that users of IE8 and below will receive the mobile experience.</p>
<h2>Media Queries</h2>
<p>Media queries are mostly about width, width, width. We generally assume that our layouts are equal to the width of the browser window and that they can be infinitely long. Thank goodness we aren’t working with fixed-height pages! Everyone expects to scroll vertically when viewing a page, and the mouse wheel convention is to scroll the page vertically. There are some clever / artistic layouts that scroll horizontally, but these can be difficult for users to “grok” initially, and should only be used for specialized content and audiences. (A media query can reference height, and there are some interesting cases where that can be useful described <a href="http://trentwalton.com/2012/01/11/vertical-media-queries-wide-sites/">here</a>.)</p>
<p>Given that the primary constraint on our designs is the width of the page, it’s very useful to be able to write width-aware CSS rules. When there is room, allow elements to be positioned next to one another horizontally. When there isn’t, reposition elements into a vertical flow, remove non-essential elements, and/or provide methods for the user to toggle the visibility of lower-priority elements.</p>
<h3>Selecting Breakpoints</h3>
<p>The horizontal width where a media query changes the layout is referred to as a “breakpoint.” There are many references that attempt to provide the screen dimensions of popular devices, which one could adopt for layout breakpoints. This approach is <em>not</em> workable, because the device landscape is constantly changing - it would result in an explosion of difficult-to-maintain rules, and bloat the size of the CSS transmitted to the browser.</p>
<p>Here is an overview of a workflow that can help identify the most appropriate breakpoints for an application:</p>
<ul>
<li><p>CONTENT is king. It’s not possible to select and design around a set of device-based breakpoints. CSS will be heavy and under-performing, and compromises will be made to “fit in the box.” It’s better to evaluate your content and think in general terms about presenting it in “big,” “middle,” and “small” contexts.</p></li>
<li><p>Start at a size near your initial wireframe target, and start resizing until things start looking “wrong”</p></li>
<li><p>Set a breakpoint with some breathing room before things go haywire, then apply rules to fix the layout in the “wrong side”</p></li>
<li><p>Repeat this exercise moving inward and outward until unreasonably small and large</p></li>
<li><p>Three sizes, small /medium / large, represent a maintainable sweet-spot</p></li>
</ul>
<p>Additional considerations for media query breakpoints:</p>
<ul>
<li><p>Line length is important. On wider layouts, it may be useful to set max-width to a comfortable reading length and allow margins to increase as the viewport grows wider. In browsers that support it, CSS3 multi-column can allow content to flow to multiple columns and avoid whitespace in wider scenarios.</p></li>
<li><p>Scaling widths out in ems proportionally to a base-font em ensure your design renders consistently across operating systems with missing fonts, user-selected fonts, zooms, and display densities.</p></li>
<li><p>Knowledge of common device widths is useful (along with testing the site on biggest-marketshare devices), but creating a layout appropriate to the content is most important of all.</p></li>
<li><p>iOS reports <em>portrait</em> <code>device-width</code> and <code>device-height</code> regardless of orientation (use <code>orientation</code> in query to differentiate)</p></li>
</ul>
<h3>Size, Move, Hide, Replace, or Transform?</h3>
<p>There are many options available for style specializations inside a media query. This list defines the most common techniques:</p>
<ul>
<li><p><strong>Size</strong>: Shrink box and / or font</p></li>
<li><p><strong>Move</strong>: Reposition an element (most commonly moving horizontally laid out columns to vertically stacked)</p></li>
<li><p><strong>Hide</strong>: Remove entirely</p></li>
<li><p><strong>Replace</strong>: Provide the same function in a smaller package (common example – list of navigation links collapsed to navigation menu menu)</p></li>
<li><p><strong>Transform</strong>: Maintain the same markup, but change its initial presentation (e.g. collapsing a context box into an accordion)</p></li>
</ul>
<p>Selection of the most appropriate technique depends on several factors:</p>
<ul>
<li><p>Semantics of the element / component</p></li>
<li><p>May descend from size > move > hide at cascading breakpoints</p></li>
<li><p>Remember that the most-important content should come first in markup for accessibility and SEO (see grid system push / pull classes)</p></li>
</ul>
<h3>The Mobile Viewport</h3>
<p>In order to effectively leverage media queries for mobile devices, it is necessary to include the <code>viewport</code> meta tag in the <code>head</code> of the page, like so:</p>
<pre><code><meta name="viewport" content="width=device-width" />
</code></pre>
<p>This instructs the mobile browser to treat device pixels as CSS pixels, that is, for the physical and virtual dimensions of the screen to be the same. (This is a bit of an oversimplification, since multiple high-density display pixels are sometimes treated as a single virtual device pixel. See <a href="http://www.quirksmode.org/mobile/viewports.html">A Tale of Two Viewports</a> for an excellent explanation.) When the <code>viewport</code> tag is not present, most mobile browsers will effectively “zoom” a larger virtual viewport into the smaller physical device viewport, by treating a device pixel as multiple CSS pixels. When mobile devices first became capable of browsing the web, this behavior was necessary to provide enough space to lay out pages designed exclusively for the desktop. Users employ the “double-tap zoom” gesture to focus on portions of the page.</p>
<p>The <code>viewport</code> meta tag allows us to instruct the browser how to treat the Layout Viewport relative to the Visual Viewport. Let’s examine the differences between the Layout Viewport and the Visual Viewport.</p>
<p><strong>Layout Viewport</strong></p>
<ul>
<li><p>CSS pixels available to layout</p></li>
<li><p>Resize of a Desktop browser window resizes the Layout Viewport and causes a reflow</p>
<ul>
<li><p>The Mobile Layout Viewport is set at the initial load and does not change</p></li>
<li><p>Mobile devices play some extra tricks with text wrapping - divs will lay out correctly, but the wrap point may be adjusted based on double-tap zoom size; text may re-wrap on zoom as well.</p></li>
</ul>
</li>
</ul>
<p><strong>Visual Viewport</strong></p>
<ul>
<li><p>The Visual Viewport is the CSS pixel dimension of the visible area of the page</p></li>
<li><p>Resize of a Desktop browser window resizes the Visual Viewport</p></li>
<li><p>Mobile zoom resizes the Visual Viewport</p></li>
</ul>
<p>Additional notes on the <code>viewport</code> meta-tag:</p>
<ul>
<li><p>Controls the layout viewport</p></li>
<li><p>Determines the number of device pixels per CSS pixel at zoom = 1</p></li>
<li><p>99% of the time, use the <meta name=“viewport” content=“width=device-width”> tag</p></li>
<li><p>For most modern devices, the default iOS and Android layout viewport width is 980 CSS pixels, with initial scale set to match the visual viewport</p></li>
<li><p>The content attribute is comma-separated</p>
<ul>
<li><p>Width = [px|device-width]. Device width is “screen width in CSS pixels at 100% zoom”</p></li>
<li><p>Height – little-used</p></li>
<li><p>Initial-scale = device pixel multiplier</p></li>
<li><p>Maximum-scale = device pixel multiplier</p></li>
<li><p>User-scalable = [true|false]. Whether to allow the user to scale. Think very carefully before limiting this.</p></li>
<li><p>CSS 2.1 recommends that CSS pixels correlate to one 96dpi pixel at arms’ length; initial-scale=1 requests this</p></li>
</ul>
</li>
<li><p>IE10 on Windows 8 ignores the viewport meta tag in snapped view. You need the following CSS declaration:</p></li>
</ul>
<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='css'><span class='line'><span class="k">@-ms-viewport</span> <span class="p">{</span>
</span><span class='line'> <span class="nt">width</span><span class="o">:</span> <span class="nt">device-width</span><span class="o">;</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<h3>Tangent: ASP.NET MVC Technique - Server-side mobile refinement</h3>
<p>This topic is not directly related to media queries, but can be used in conjunction with them to achieve comfortable layout <em>and</em> bandwidth reduction while maintaining a single page of markup. It is important to remember that content hidden with <code>display: none</code> is still downloaded. The idea behind the following technique is to remove a component from the layout flow using a media query, and apply server-side processing to entirely eliminate its content from the payload.</p>
<p>Here is an overview of the steps involved:</p>
<ul>
<li><p>Add the <a href="http://nuget.org/packages/51Degrees.mobi">NuGet package</a> from <a href="http://51degrees.mobi">51Degrees.mobi</a> for better device detection. Inclusion of this package causes the <code>Request.Browser.IsMobileDevice</code> property to be set for each request, detecting a much broader range of devices than the out-of-box behavior.</p></li>
<li><p>An alternative is the <a href="http://DetectMobileBrowsers.com">DetectMobileBrowsers.com</a> regex for simple useragent-based detection.</p></li>
<li><p>In your server-side markup (.aspx or .cshtml), wrap the component you wish to hide from mobile devices as follows:</p></li>
</ul>
<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='html'><span class='line'>@if (!Request.Browser.IsMobileDevice) {
</span><span class='line'> <span class="nt"><section</span> <span class="na">class=</span><span class="s">"sidebar"</span><span class="nt">></span>
</span><span class='line'> <span class="nt"><p></span>Check out my twitter feed:<span class="nt"></p></span>
</span><span class='line'> <span class="nt"><ul></span>
</span><span class='line'> <span class="nt"><li></span>Post 1<span class="nt"></li></span>
</span><span class='line'> <span class="nt"><li></span>Post 2<span class="nt"></li></span>
</span><span class='line'> <span class="nt"><li></span>Post 3<span class="nt"></li></span>
</span><span class='line'> <span class="nt"></ul></span>
</span><span class='line'> <span class="nt"></section></span>
</span><span class='line'>}
</span></code></pre></td></tr></table></div></figure>
<ul>
<li><p>Implement a media query to flow the remainder of the layout into the previously-occupied space.</p></li>
<li><p>When Output Caching is in use, ensure the mobile and non-mobile variations of the page are cached separately. Include the attribute <code>[OutputCache(Duration = 60, VaryByCustom = "Mobile")]</code> on each controller action that relies on this technique. Provide the custom cache key by overriding <code>GetVaryByCustomString</code> in <code>Global.asax.cs</code>:</p></li>
</ul>
<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='csharp'><span class='line'><span class="k">public</span> <span class="k">override</span> <span class="kt">string</span> <span class="nf">GetVaryByCustomString</span><span class="p">(</span><span class="n">System</span><span class="p">.</span><span class="n">Web</span><span class="p">.</span><span class="n">HttpContext</span> <span class="n">context</span><span class="p">,</span> <span class="kt">string</span> <span class="n">custom</span><span class="p">)</span>
</span><span class='line'><span class="p">{</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="n">custom</span> <span class="p">!=</span> <span class="s">"Mobile"</span><span class="p">)</span>
</span><span class='line'> <span class="k">return</span> <span class="k">null</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'> <span class="k">return</span> <span class="n">context</span><span class="p">.</span><span class="n">Request</span><span class="p">.</span><span class="n">Browser</span><span class="p">.</span><span class="n">IsMobileDevice</span><span class="p">.</span><span class="n">ToString</span><span class="p">();</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<ul>
<li>Depending on the nature of the content, you may wish to provide a way for the user to asynchronously request it after the initial page load.</li>
</ul>
<p>You can see this technique in action here: <a href="http://codecampresponsive.apphb.com/Home/OutputCacheMobileHiding">DEMO</a>. View the source of the page from a mobile device (or with a mobile user agent string) to verify that the sidebar is not transmitted. On desktop devices, the sidebar is included and hidden/shown as the page width changes.</p>
<h2>Adaptive Media</h2>
<p>In Marcotte’s original article, the only medium considered for adaptability was the image. Modern sites deliver images, video, and audio to devices of varying capabilities. The following sections describe responsive techniques for dealing with all three.</p>
<p>A page demonstrating many of the following techniques is available here: <a href="http://codecampresponsive.apphb.com/Home/ResponsiveMedia">DEMO</a>.</p>
<h3>Responsive Images</h3>
<p>We must consider layout and bandwidth when planning a responsive approach for images. Here are some notes on responsive image techniques:</p>
<ul>
<li><p><strong>Scaling</strong>: Basic image scaling can be accomplished with <code>width: 100%</code> and <code>max-width</code> CSS properties. IE7 may require a polyfill depending on other CSS properties in use.</p></li>
<li><p><strong>Cropping</strong>: At media query breakpoints, set negative margins with overflow: hidden to avoid shrinking the image and</p></li>
<li><p><strong>Swap/Omit</strong>: On the server side, alternative image href may be set in markup, or images may be omitted when a mobile browser is detected. An HTTP handler may be employed to dynamically scale images according to user agent. Client-side techniques for selecting images according to page size exist, but there is no silver bullet - most of these techniques will result in multiple image downloads or suffer from very un-semantic (and potentially non-accessible) markup.</p></li>
<li><p><strong>SVG</strong>: Scalable Vector Graphics images have the benefit of full-fidelity scaling with a single file. Internet Explorer introduced SVG support in IE9, so a fallback is required for earlier IE versions. As with most “swap” techniques, it’s important to monitor network traffic and avoid transmission of both files, when possible, or to employ a server-side approach.</p></li>
</ul>
<p>Be sure to choose the correct image format. PNG works best for logos and vectors, JPEG for photos and other realistic images. GIF should rarely be used, though can be effective for <= IE6 as a work around for PNG transparency. Losslessly optimize your PNG images - this can reduce size by a surprising amount (see <a href="http://http://pnggauntlet.com/">PngGauntlet</a> for example).</p>
<h3>Responsive Video</h3>
<p>HTML5 introduced full video support, which modern browsers all handle. HTML5 video is generally more performant and less buggy than relying on plugin-based approaches. It is necessary, though, to provide a fallback for older browsers - the goal with video is a balance of performance and compatibility.</p>
<ul>
<li><p>Supply VP8- and H.264-encoded files and you will cover nearly all user agents</p></li>
<li><p>Omitting the type from the final source will cause most browsers to check the metadata to determine if it can be played. Bandwidth / accessibility tradeoff.</p></li>
<li><p>One <em>could</em> use the little-known <code>media</code> attribute to serve smaller files to smaller devices, although at the time of this writing it appears this attribute may be dropped.</p></li>
<li><p>IIS NOTE: need to add mime types for video, or IIS will return a 404.3. See <code>Web.config</code> section <code>system.webserver\staticContent\mimeMap</code>.</p></li>
<li><p>Within the video tag, below the sources, include a flash fallback and video download link (for users without flash).</p></li>
<li><p>Use JavaScript and/or HTTP module and user agent detection to serve appropriate codec and filesize</p></li>
<li><p>Good resources:</p>
<ul>
<li><p>Mark Pilgrim’s excellent, very in-depth (though getting out-of-date) guide: http://diveintohtml5.info/video.html</p></li>
<li><p>Concise, easy to read guide from JWPlayer: http://www.longtailvideo.com/support/jw-player/jw-player-for-flash-v5/22644/using-the-html5-video-tag/</p></li>
<li><p><a href="http://flowplayer.org/">FlowPlayer</a> is a pre-packaged, responsive, broadly compatible option.</p></li>
<li><p>Nice tool for generating markup: <a href="http://camendesign.co.uk/code/video_for_everybody">Video for Everybody</a></p></li>
</ul>
</li>
</ul>
<p>Don’t discount the option of hosting the video externally (Vimeo, Youtube) and IFRAMEing in a player – they’ve solved the cross-browser issues and will take bandwidth & connection pressure off your server.</p>
<p><strong>Flexible video sizing:</strong></p>
<ul>
<li><p>For the <code><video></code> tag, you can rely on <code>width: 100%</code> and <code>max-width</code> with <code>height: auto</code> to flexibly resize video while maintaining aspect ratio.</p></li>
<li><p>Flash and IFRAMEs have issues – can’t automatically set height to preserve the aspect ratio. Thierry Koblentz “Creating Intrinsic Ratios for Video” to the rescue - <a href="http://www.alistapart.com/articles/creating-intrinsic-ratios-for-video/">post</a>.</p>
<ul>
<li><p>Set a wrapper DIV with relative position, zero height, and bottom-padding representing the aspect radio (e.g. 56.25% == 16:9)</p></li>
<li><p>Set an inner div absolutely positioned with 100% width and height</p></li>
<li><p>This technique has issues in IE7 and below – use a conditional style sheet</p></li>
</ul>
</li>
</ul>
<h3>Responsive Audio</h3>
<p>Techniques for audio largely follow those for video, except that layout adjustment is not really a concern. Rely on the HTML5 <code>audio</code> tag with fallbacks, and provide download links for users without plugin support. On the server side, consider serving files encoded to lower bit rates when mobile devices are detected.</p>
<h2>Other Considerations</h2>
<p>Now that we have covered the the three primary categories of responsive web design techniques, let’s briefly visit some related considerations.</p>
<h3>Forms</h3>
<p>Forms must provide a usable mobile layout and be touch-friendly. The following are important points to consider for responsive forms:</p>
<ul>
<li><p>Note the “field zoom” feature of many mobile browsers. When an input field is focused, the browser will zoom the viewport to the width of the field.</p>
<ul>
<li><p>This makes top-aligned labels better, otherwise the label or field may be cut off.</p></li>
<li><p>Set <code>input, textarea { font-size: 1em }</code> to avoid extra zoom on iDevices – lower font sizes will introduce additional zoom</p></li>
</ul>
</li>
<li><p>Touch-friendly</p>
<ul>
<li><p>Sizing (finger targets): extra padding on inputs, and larger label targets for radio and checkbox controls.</p></li>
<li><p>Click versus Touch events: there is a delay between the initial touch event and final click event, during which the browser is waiting for a potential gesture. If you know that a given control has no gesture interaction, you can trigger interactions from the touch event, resulting in faster response. This can be difficult to implement reliably - see <a href="http://labs.ft.com/articles/ft-fastclick/">FT Fastclick</a> for a solid implementation.</p></li>
</ul>
</li>
<li><p>Input Types</p>
<ul>
<li><p>HTML5 introduces new values for the <code>input</code> element’s <code>type</code> attribute. Mobile browsers key off this attribute to provide the most appropriate keyboard layout for the type (e.g. email type results in keyboard with @ symbol).</p></li>
<li><p>Some desktop browsers implement some native validation and specialized input controls for certain input types. This may not be desired - the <code>form</code> element’s <code>novalidate</code> attribute can prevent native validation, and CSS can be used to prevent browser-substituted controls.</p></li>
</ul>
</li>
</ul>
<h3>Typography</h3>
<p>The trend is toward more minimal interfaces that place typography in the forefront. The increased prevalence of high-density displays makes the display of beautiful type accessible to a greater number of users. Consider the following points related to typography:</p>
<ul>
<li><p>The user (or at least platform developer) has already specified his / her preferred default font size. Rather than overriding this, we should respect it as a base and scale from there.</p></li>
<li><p>New “retina”-class devices and high density displays can make pixel sizing unreliable. The future of the “pixel” is uncertain - <code>em</code> should be the default sizing unit, unless you have a really good reason to use pixels.</p></li>
<li><p>On higher-density displays, you may wish to increase font weight to achieve a uniform result across displays. Antialiasing on lower-density displays results in greater perceived weight given the same font on a higher-density display.</p></li>
<li><p>On standard-density displays, serif fonts below 12px are not sharp enough. But you should be over 12px anyway.</p></li>
<li><p>Good Metrics</p>
<ul>
<li><p>Font size: bigger than you think. Hold a book or magazine at a comfortable distance and compare.</p></li>
<li><p>Contrast: ratio font color to background brightness. Steer clear of full black (looks like a “hole”) and full white.</p></li>
<li><p>Line Height: for text, 140% of font size is a good general rule, but depends on face (ascender/descender ratio to x-height and “blackness”). In CSS, use proportional line-height notation (e.g. <code>font: Arial 1em/1.44</code>) with no units.</p></li>
<li><p>Line Length (measure): From 45 to 75 characters is good balance of ease in tracking to next line versus not having to do it too often. When browsers support it, you can use CSS3 column count when the view gets very wide. You can leave width “on the table” and set a <code>max-width</code>. Ensure text blocks have good height in proportion to width.</p></li>
<li><p>Spacing: Headings can often use more vertical padding to breathe</p></li>
</ul>
</li>
<li><p>Gotchas</p>
<ul>
<li><p>Note that when using fluid (proportional) layout techniques, you give up some control over line length</p></li>
<li><p>Web Font browser quirks and format support (use <a href="http://www.fontsquirrel.com/">Font Squirrel</a>)</p></li>
<li><p>When serving locally, apply correct mimetype to web fonts</p></li>
</ul>
</li>
</ul>
<h2>Conclusion</h2>
<p>Responsive Web Design has been around for several years, and has been gathering steam since its introduction. There is no “one size fits all” approach for cross-device sites - careful evaluation and application of the available techniques is required. Good luck on your projects!</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[HTML and jQuery: Displaying and Positioning Custom Tooltips]]></title>
<link href="http://www.aidanjryan.com/blog/2013/04/25/html-and-javascript-positioning-a-custom-tooltip/"/>
<updated>2013-04-25T11:15:00-07:00</updated>
<id>http://www.aidanjryan.com/blog/2013/04/25/html-and-javascript-positioning-a-custom-tooltip</id>
<content type="html"><![CDATA[<p>At a client I was recently tasked with implementing a site that presents tooltips with a custom appearance. I thought I’d share some lessons learned about consistently displaying the tooltips and working around positioning problems.<!--more--></p>
<h2>Removing existing tooltips</h2>
<p>In the site I am working with, the custom tooltips must be displayed for an ASP.NET control that has its own tooltip implementation. This means that when the page is initially rendered, the <code>title</code> attribute is already set on the items where I need custom tooltips. This means I need to remove the title attribute but preserve its text for displaying later in my custom tooltip. There is an other wrinkle, in that the site is displayed in Internet Explorer’s quirks mode, which means the <code>alt</code> attribute will also display a browser tooltip on hover. I needed to remove the <code>alt</code> attribute as well, to avoid “double tooltips” with my custom tooltip and the browser tooltip being displayed simultaneously.</p>
<p>Here’s a snippet illustrating the technique:</p>
<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">$map</span><span class="p">.</span><span class="nx">children</span><span class="p">(</span><span class="s1">'area'</span><span class="p">).</span><span class="nx">each</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">$area</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
</span><span class='line'> <span class="nx">$area</span><span class="p">.</span><span class="nx">attr</span><span class="p">(</span><span class="s1">'data-customtooltip'</span><span class="p">,</span> <span class="nx">$area</span><span class="p">.</span><span class="nx">attr</span><span class="p">(</span><span class="s1">'title'</span><span class="p">));</span>
</span><span class='line'> <span class="nx">$area</span><span class="p">.</span><span class="nx">removeAttr</span><span class="p">(</span><span class="s1">'alt title'</span><span class="p">);</span>
</span><span class='line'><span class="p">})</span>
</span></code></pre></td></tr></table></div></figure>
<p>The control uses an image map to provide tooltip and interactive functions. I get a jQuery object for the <code>map</code>, then iterate is children. The <code>title</code> attribute is copied to a <code>data-customtooltip</code> attribute, and the <code>alt</code> and <code>title</code> attributes are removed to prevent display of the native browser tooltip.</p>
<h2>Displaying and positioning the custom tooltip</h2>
<p>The next piece of the puzzle is displaying a custom tooltip when the mouse is over an <code>area</code>. I include the following HTML in the master layout page:</p>
<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='html'><span class='line'><span class="nt"><div</span>
</span><span class='line'> <span class="na">id=</span><span class="s">"tooltip"</span>
</span><span class='line'> <span class="na">style=</span><span class="s">"</span>
</span><span class='line'><span class="s"> position: fixed;</span>
</span><span class='line'><span class="s"> z-index: 999;</span>
</span><span class='line'><span class="s"> display: none;"</span><span class="nt">></span>
</span><span class='line'><span class="nt"></div></span>
</span></code></pre></td></tr></table></div></figure>
<p>This creates a <code>div</code> that is initially invisible and detached from the overall page layout. Of course, the <code>div</code> is also styled to fit with the site’s overall theme.</p>
<p>Back in the JavaScript, I hooked the <code>mouseenter</code> event to set the tooltip text and then display the hidden <code>div</code>. Initially, I simply positioned the <code>div</code> below and to the left of the mouse pointer. Unfortunately, this naive positioning technique falls down when the mouse is near the lower-right corner of a page or frame: the tooltip is cut off at the page/frame edge. Native browser tooltips “float” above the page surface and can even extend beyond the edges of the browser window, but this is not possible with an HTML element. To work around this issue, I detect the “quadrant” of the page that the mouse cursor occupies, and position the tooltip away from the page corner. For example, if the mouse cursor is nearest the lower-right corner, I position the tooltip above and to the left of the cursor.</p>
<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">$tooltipDiv</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s1">'#tooltip'</span><span class="p">);</span>
</span><span class='line'><span class="nx">$map</span>
</span><span class='line'> <span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">'mouseenter'</span><span class="p">,</span> <span class="s1">'area'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">$this</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'> <span class="c1">// set the tooltip text from our custom attribute</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">customTitle</span> <span class="o">=</span> <span class="nx">$this</span><span class="p">.</span><span class="nx">attr</span><span class="p">(</span><span class="s1">'data-customtooltip'</span><span class="p">);</span>
</span><span class='line'> <span class="nx">$tooltipDiv</span><span class="p">.</span><span class="nx">html</span><span class="p">(</span><span class="nx">customTitle</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'> <span class="c1">// determine if the mouse is in the left / top "halves" of the document</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">mouseLeft</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">pageX</span> <span class="o"><</span> <span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">clientWidth</span> <span class="o">/</span> <span class="mi">2</span><span class="p">);</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">mouseTop</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">pageY</span> <span class="o"><</span> <span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">clientHeight</span> <span class="o">/</span> <span class="mi">2</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'> <span class="c1">// position the tooltip away from the mouse quadrant</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">css</span> <span class="o">=</span> <span class="p">{};</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="nx">mouseLeft</span><span class="p">)</span>
</span><span class='line'> <span class="nx">css</span><span class="p">.</span><span class="nx">left</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">pageX</span> <span class="o">+</span> <span class="mi">10</span> <span class="o">+</span> <span class="s1">'px'</span><span class="p">;</span>
</span><span class='line'> <span class="k">else</span>
</span><span class='line'> <span class="nx">css</span><span class="p">.</span><span class="nx">left</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">pageX</span> <span class="o">-</span> <span class="p">(</span><span class="mi">7</span> <span class="o">+</span> <span class="nx">$tooltipDiv</span><span class="p">.</span><span class="nx">width</span><span class="p">())</span> <span class="o">+</span> <span class="s1">'px'</span><span class="p">;</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="nx">mouseTop</span><span class="p">)</span>
</span><span class='line'> <span class="nx">css</span><span class="p">.</span><span class="nx">top</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">pageY</span> <span class="o">+</span> <span class="mi">10</span> <span class="o">+</span> <span class="s1">'px'</span><span class="p">;</span>
</span><span class='line'> <span class="k">else</span>
</span><span class='line'> <span class="nx">css</span><span class="p">.</span><span class="nx">top</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">pageY</span> <span class="o">-</span> <span class="p">(</span><span class="mi">7</span> <span class="o">+</span> <span class="nx">$tooltipDiv</span><span class="p">.</span><span class="nx">height</span><span class="p">())</span> <span class="o">+</span> <span class="s1">'px'</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'> <span class="nx">$tooltipDiv</span><span class="p">.</span><span class="nx">css</span><span class="p">(</span><span class="nx">css</span><span class="p">);</span>
</span><span class='line'> <span class="nx">$tooltipDiv</span><span class="p">.</span><span class="nx">show</span><span class="p">();</span>
</span><span class='line'> <span class="p">})</span>
</span><span class='line'> <span class="p">.</span><span class="nx">mouseleave</span><span class="p">(</span>
</span><span class='line'> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="nx">$tooltipDiv</span><span class="p">.</span><span class="nx">hide</span><span class="p">();</span> <span class="p">}</span>
</span><span class='line'> <span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>
<p>Note that I am always using the <code>left</code> and <code>top</code> CSS attributes to position the <code>div</code>. I ran into strange issues when I attempted to set the left/top to auto and position using right/bottom. I don’t think Internet Explorer supports positioning in this manner.</p>
<p>Also note that I don’t attach the <code>mouseenter</code> handler to each <code>area</code> directly. There can be dozens of areas on the page that require a tooltip - hooking up that many event handlers would not perform as well. It’s better to attach a single handler for when the event bubbles to the <code>map</code>, then let jQuery filter for the child elements that we want to deal with.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Updated WinHaste hastebin client]]></title>
<link href="http://www.aidanjryan.com/blog/2013/02/08/updated-winhaste-hastebin-client/"/>
<updated>2013-02-08T13:17:00-08:00</updated>
<id>http://www.aidanjryan.com/blog/2013/02/08/updated-winhaste-hastebin-client</id>
<content type="html"><![CDATA[<p>I just pushed a small update to my <a href="https://github.com/ajryan/WinHaste">WinHaste</a> hastebin client (<a href="http://ajryan-github-downloads.s3.amazonaws.com/WinHaste.exe">binary</a>). <a href="http://hastebin.com/">Hastebin</a> is a cool, minimal, login-free alternative to Pastebin. Now, when a command’s output is being piped to WinHaste, the output will be echoed to the console. This is especially handy if you are running an interactive command, so you aren’t entering values “blind.”<!--more--></p>
<p>WinHaste supports piped or interactive entry - if you enter <code>WinHaste.exe</code> with no arguments, it will read user input from the console until an end-of-file <code>^Z</code> character is entered. In this mode, Windows already writes user input to the console, so echoing within WinHaste would result in doubling every character typed. I ran across a nice technique in <a href="http://stackoverflow.com/a/9712392/1042">this StackOverflow answer</a> for checking whether the current input stream is piped or interactive:</p>
<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='csharp'><span class='line'><span class="k">private</span> <span class="k">static</span> <span class="kt">bool</span> <span class="nf">IsInputPiped</span><span class="p">()</span>
</span><span class='line'><span class="p">{</span>
</span><span class='line'> <span class="k">try</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="kt">var</span> <span class="n">tmp</span> <span class="p">=</span> <span class="n">Console</span><span class="p">.</span><span class="n">KeyAvailable</span><span class="p">;</span>
</span><span class='line'> <span class="k">return</span> <span class="k">false</span><span class="p">;</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="k">catch</span> <span class="p">(</span><span class="n">InvalidOperationException</span><span class="p">)</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="k">return</span> <span class="k">true</span><span class="p">;</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>I navigated into the disassembly of the <code>Console.KeyAvailable</code> (thanks, ReSharper!) found found the following:</p>
<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='csharp'><span class='line'><span class="kt">bool</span> <span class="n">r</span> <span class="p">=</span> <span class="n">Win32Native</span><span class="p">.</span><span class="n">PeekConsoleInput</span><span class="p">(</span><span class="n">ConsoleInputHandle</span><span class="p">,</span> <span class="k">out</span> <span class="n">ir</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="k">out</span> <span class="n">numEventsRead</span><span class="p">);</span>
</span><span class='line'><span class="k">if</span> <span class="p">(!</span><span class="n">r</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="kt">int</span> <span class="n">errorCode</span> <span class="p">=</span> <span class="n">Marshal</span><span class="p">.</span><span class="n">GetLastWin32Error</span><span class="p">();</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="n">errorCode</span> <span class="p">==</span> <span class="n">Win32Native</span><span class="p">.</span><span class="n">ERROR_INVALID_HANDLE</span><span class="p">)</span>
</span><span class='line'> <span class="k">throw</span> <span class="k">new</span> <span class="nf">InvalidOperationException</span><span class="p">(</span><span class="n">Environment</span><span class="p">.</span><span class="n">GetResourceString</span><span class="p">(</span><span class="s">"InvalidOperation_ConsoleKeyAvailableOnFile"</span><span class="p">));</span>
</span><span class='line'> <span class="n">__Error</span><span class="p">.</span><span class="n">WinIOError</span><span class="p">(</span><span class="n">errorCode</span><span class="p">,</span> <span class="s">"stdin"</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>So, I could P/Invoke PeekConsoleInput myself, but this works fine for me!</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[A Windows Store (WinRT) TFS Work Item Browser]]></title>
<link href="http://www.aidanjryan.com/blog/2013/02/04/a-windows-store-winrt-tfs-work-item-browser/"/>
<updated>2013-02-04T13:55:00-08:00</updated>
<id>http://www.aidanjryan.com/blog/2013/02/04/a-windows-store-winrt-tfs-work-item-browser</id>
<content type="html"><![CDATA[<p>It’s time to dive into Windows Store application development. I’ve developed several tools for the Team Foundation Server (TFS) ecosystem, so I think an appropriate first project that will intersect my existing skill set would be a TFS Work Item browser.<!--more--></p>
<h2>The Plan</h2>
<p><img class="right" src="http://www.aidanjryan.com/images/tfsworkitems_start_icon.png" title="TFS Work Items start screen icon" alt="TFS Work Items start screen icon"></p>
<p>The app will be a simple work item browser. After entering your TFS server connection information, you’ll be presented with a list of Team Projects. Select a Team Project and browse through Work Items. This will allow me to explore navigation, paging, presentation, and other new idioms in the WinRT platform.</p>
<p>There is an interesting wrinkle here: the TFS API client assemblies are not usable from WinRT. A brief attempt at hitting the TFS ASMX services directly proved too frustrating to waste time on - after all, the goal is to push my Windows Store app skills. This led me to the decision to host a TFS proxy on <a href="http://appharbor.com">AppHarbor</a> that would provide a simple ASP.NET Web API endpoint for retrieving Work Items, which should be simple to access from WinRT. Another option for proxying to TFS could have been the <a href="http://blogs.msdn.com/b/briankel/archive/2013/01/07/odata-service-for-team-foundation-server-v2.aspx">OData Service for Team Foundation Server</a>, except that it is configured to connect only to a single server. I want my app’s users to be able to connect an arbitrary TFS server, which would require them to set up the OData service themselves. I opted instead to stand up a small service that allows connection to any TFS server and provides just enough functions for my app.</p>
<p>You can find the source for the <a href="http://github.com/ajryan/TfsWorkItems">TfsWorkItems</a> work item browser and <a href="http://github.com/ajryan/TfsProxy">TfsProxy</a> API proxy at my <a href="http://github.com/ajryan">Github profile</a>.</p>
<p>The app itself is available for testing in this <a href="http://www.aidanjryan.com/assets/TfsWorkItems_1.0.0.0_AnyCPU_Test.zip">ZIP archive</a>. Extract, and then run the <code>Add-AppDevPackage.ps1</code> PowerShell script to install the app.</p>
<h2>Web API Proxy</h2>
<p>As described above, in order to get to the TFS data from WinRT, I will stand up a simple Web API service with methods for retrieving Team Projects and Work Items. I created a Web API project and added controllers named <code>ProjectsController</code> and <code>WorkItemsController</code>. Along the way, I am using the handy <a href="https://chrome.google.com/webstore/detail/postman-rest-client/fdmmgilgnpjigdojojpjoooidkmcomcm">Postman</a> REST Client extension for Chrome to hit my service methods as I build them out. Fiddler or curl would be just as effective.</p>
<h3>TFS Connection</h3>
<p>The first thing I need is a connection to TFS - this requires a TFS URI, username, and password. Both of my controllers will require a connection, so I will compose this dependency to avoid cluttering all of the API calls with the TFS connection information. This will be implemented via an action filter that provides a TFS Connection in the current HttpContext.</p>
<p>My <code>TfsBasicAuthenticationAttribute</code> action filter passes the request headers to a <code>UserDataPrincipal</code> (<code>IPrincipal</code>) that provides a factory method named <code>InitFromHeaders</code>. This method handles the parsing of the connection information from the request headers. If no principal is returned, or if connecting to TFS with the given connection information fails, an HTTP 401 Unauthorized response is returned. When a connection issue occurs, the specific reason for failure is provided in the response content.</p>
<figure class='code'><figcaption><span>TfsBasicAuthenticationAttribute </span></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>