<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Allen Wirfs-Brock</title>
	<atom:link href="http://www.wirfs-brock.com/allen/feed" rel="self" type="application/rss+xml" />
	<link>http://www.wirfs-brock.com/allen</link>
	<description>Thoughts and Things</description>
	<lastBuildDate>Wed, 09 May 2012 17:18:48 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>B2G:  From Browser to Platform</title>
		<link>http://www.wirfs-brock.com/allen/posts/394</link>
		<comments>http://www.wirfs-brock.com/allen/posts/394#comments</comments>
		<pubDate>Mon, 25 Jul 2011 19:58:03 +0000</pubDate>
		<dc:creator>allen</dc:creator>
				<category><![CDATA[Browsers]]></category>
		<category><![CDATA[Mozilla]]></category>
		<category><![CDATA[Post-PC/Ambient Computing]]></category>
		<category><![CDATA[ambient compting]]></category>
		<category><![CDATA[application platform]]></category>
		<category><![CDATA[browsers]]></category>
		<category><![CDATA[computing eras]]></category>
		<category><![CDATA[Firefox]]></category>
		<category><![CDATA[mobile]]></category>
		<category><![CDATA[native apps]]></category>
		<category><![CDATA[pervasive]]></category>
		<category><![CDATA[post-PC]]></category>

		<guid isPermaLink="false">http://www.wirfs-brock.com/allen/?p=394</guid>
		<description><![CDATA[In my post, The Browser is a Transitional Technology, I wrote that I thought  web browsers were really Personal Computing Era applications and that browsers were unlikely to continue to exist as such as we move deeply into the Ambient Computing Era. However,  I expect browser technologies to have a key role in the Ambient [...]]]></description>
			<content:encoded><![CDATA[<p></p><p>In my post, <a title="The Browser is a Transitonal Technology" href="http://www.wirfs-brock.com/allen/posts/115" target="_self">The Browser is a Transitional Technology,</a> I wrote that I thought  web browsers were really Personal Computing Era applications and that browsers were unlikely to continue to exist as such as we move deeply into the <a title="3rd Era of Computing" href="http://www.wirfs-brock.com/allen/posts/74" target="_self">Ambient Computing Era.</a> However,  I expect browser technologies to have a key role in the Ambient Computing Era. In <a title="Why Mozilla" href="http://www.wirfs-brock.com/allen/posts/210" target="_self">Why Mozilla</a>, I talked about the inevitable emergence of a universal application platform for the Ambient Era and how open web technologies could serve that role. Last month I gave a talk where I tried to pull some of these ideas together:</p>
<div style="width:425px" id="__ss_8402770"> <strong style="display:block;margin:12px 0 4px"><a href="http://www.slideshare.net/allenwb/is-the-browser-a-transitional-technology" title="Is the Browser a Transitional Technology?" target="_blank">Is the Browser a Transitional Technology?</a></strong> <iframe src="http://www.slideshare.net/slideshow/embed_code/8402770" width="425" height="355" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
<div style="padding:5px 0 12px">  </div>
</p></div>
<p>For slides, 14-19 I talked about how when you remove that PC application facade from a modern browser you have essentially an open web-based application platform that is appropriate for all classes of ambient computing devices.</p>
<p>Today Mozilla announced an embryonic project that is directed towards that goal.    <a title="B2G Project Page" href="https://wiki.mozilla.org/B2G" target="_blank">B2G</a> or (Booting to the Web) is about showing that the open the web application platform can be the primarily platform for running native-grade applications.  As the project page says:</p>
<blockquote><p>Mozilla believes that the web can displace proprietary, single-vendor stacks for application development. To make open web technologies a better basis for future applications on mobile and desktop alike, we need to keep pushing the envelope of the web to include &#8212; and in places exceed &#8212; the capabilities of the competing stacks in question.</p></blockquote>
<p>One of the first steps is to directly boot devices into running <a title="Gecko on Wikipedia" href="http://en.wikipedia.org/wiki/Gecko_%28layout_engine%29" target="_blank">Gecko</a>, Mozilla’s core browser engine.  Essentially the devices will boot directly into the browser platform, but without the baggage and overhead of a traditional PC based web browser.  This is essentially the vision of slide 17 of my presentation.  The “G” in B2G comes from the use of Gecko, but the project is really about the open web. Any other set of browser technologies could potentially be used in the same way.  As the <a title="B2G Project Page" href="https://wiki.mozilla.org/B2G" target="_blank">project web site</a> says: “We aren&#8217;t trying to have these native-grade apps just run on Firefox, we&#8217;re trying to have them run on the web.”</p>
<p>This project is just starting, so nobody yet knows all the details or how successful it will be.  But, like all Mozilla projects it will take place in the <a title="B2G on Github" href="https://github.com/andreasgal/B2G" target="_blank">open</a> and with an open invitation for you involvement.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.wirfs-brock.com/allen/posts/394/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Web App Platform: Is it a Framework or is it an OS?</title>
		<link>http://www.wirfs-brock.com/allen/posts/379</link>
		<comments>http://www.wirfs-brock.com/allen/posts/379#comments</comments>
		<pubDate>Mon, 23 May 2011 01:31:55 +0000</pubDate>
		<dc:creator>allen</dc:creator>
				<category><![CDATA[Browsers]]></category>
		<category><![CDATA[Post-PC/Ambient Computing]]></category>
		<category><![CDATA[Web Standards]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[browsers]]></category>
		<category><![CDATA[frameworks]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Longhorn]]></category>
		<category><![CDATA[post-PC]]></category>
		<category><![CDATA[standards]]></category>
		<category><![CDATA[Taligent]]></category>
		<category><![CDATA[Web IDL]]></category>
		<category><![CDATA[web platfiorm architecture]]></category>

		<guid isPermaLink="false">http://www.wirfs-brock.com/allen/?p=379</guid>
		<description><![CDATA[Recently I&#8217;ve had some conversations with some colleagues about how Web IDL is used to specify the APIs that browsers support for web applications.  I think our discussions raised some interesting questions about  the fundamental nature of the web app platform so I wanted to raise those same questions here. Basically, is the browser web [...]]]></description>
			<content:encoded><![CDATA[<p></p><p><a href="http://www.wirfs-brock.com/allen/wp-content/uploads/2011/05/pbcup2.png"><img class="alignright size-medium wp-image-384" title="pbcup2" src="http://www.wirfs-brock.com/allen/wp-content/uploads/2011/05/pbcup2-200x300.png" alt="" width="200" height="300" /></a>Recently I&#8217;ve had some conversations with some colleagues about how <a title="Web IDL draft" href="http://www.w3.org/TR/WebIDL/" target="_blank">Web IDL</a> is used to specify the APIs that browsers support for web applications.  I think our discussions raised some interesting questions about  the fundamental nature of the web app platform so I wanted to raise those same questions here.</p>
<p>Basically, is the browser web app platform an application framework or is it really  something that is more like an operating system? Stated more  concretely,  is the web app platform most similar to the Java or .Net platforms or is it more similar to Linux or Windows?  In the long term this is probably a very important question.  It  makes a different  in the sort of capabilities that can be made available to a web app and also in the integrity expectations concerning  the underlying platform.</p>
<p>In a framework, client code directly integrates and extends the platform code.  This allows client code to do very powerful things but the cost of this is that client code can do things that results in platform level errors or even failures.  Modern frameworks are pretty much all defined in terms of object-oriented concepts because those concepts permits the client extensibility that is the primary motivation for building a framework. Frameworks generally have to trust their clients because they frequently have to pass control into client code and their is no way they can anticipate or validate everything client code might do.  Frameworks are great from the perspective of what they allow developers to create, they are less great in turns of robustness and integrity.</p>
<p>In an operating system, client code almost never directly integrates with the platform code. Client code is limited to a fixed set of actions that can be requested via a fairly simple system call interface. In the absence of  platform bugs, client code can&#8217;t cause platform level errors or crash the platforms because the platform carefully validates every aspect of every system call request and never directly executes untrusted client code. Operating systems don&#8217;t trust their clients. Successful operating system API are pretty much all expressed in terms of procedure calls that only accept scalars and simple structs as arguments because such arguments can be fully validated before the platform uses them to perform any action. Operating systems are great from a robustness and integrity perspective but they don&#8217;t offer much direct help to clients that need to do complex things.</p>
<p>Historically, there have been various attempts to create operating systems that uses framework style object-oriented client interfaces.  All the major attempts at doing this that I am aware of have been dismal failures.  <a title="Taligent Wikipedia" href="http://en.wikipedia.org/wiki/Taligent" target="_blank">Taligent</a> and <a title="decouple .net and longhorm" href="http://www.theregister.co.uk/2005/05/26/dotnet_longhorn/" target="_blank">Windows Longhorn</a> are two notorious examples. The problem seems to be that the power and extensibility that comes with framework style interfaces is in direct conflict  with robustness and integrity requirements of an OS.  It is very difficult and perhaps impossible to find a comprise that provides sufficient power, extensibility, robustness, and integrity all at the same time. Systems like Taligent and Longhorn also have had significant durability issues because one of the ways they  tried to balance power and integrity was by describing their APIs in terms of static recursive object-oriented typing which are very hard to evolve in an backwards compatible fashion over multiple versions.</p>
<p>This begins to sound a lot like the way Web IDL is being used to describe web app APIs.  It has framework style APIs but browser implementers would like to have OS style system integrity and robustness.</p>
<p>One way OSes have addressed this issue is by using a  kernel.  The kernel is a small part of the overall platform that is very robust, has high integrity, and exposes very stable APIs.  The majority of the platform is outside the kernel.  In general, bugs or misuse of  non-kernel code may crash a application but it can&#8217;t crash the entire system. One way to think about large application frameworks like Java and .Net is that they out the low integrity but high leverage outer-most layer of such a kernelized design.</p>
<p>So what is the Web App platform.  Is it a framework or is it an OS? I think it needs to be designed mostly like a framework.  However, there probably is a kernel of functionality that needs to be treated more like an OS.  That kernel is not yet well identified. It probably needs to be.  Otherwise, the designer of the web application platform run the risk of going down the same dead-end paths that were taken by the designers of &#8220;object-oriented&#8221; OSes like Taligent and Longhorn.</p>
<p>(Photo <a href="http://creativecommons.org/licenses/by/2.0/"><img title="Attribution" src="http://l.yimg.com/g/images/cc_icon_attribution_small.gif" border="0" alt="Attribution" /></a> <a title="Attribution License" href="http://creativecommons.org/licenses/by/2.0/">Some rights reserved</a> by <a href="http://www.flickr.com/photos/pinksherbet/">Pink Sherbet Photography</a>)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.wirfs-brock.com/allen/posts/379/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Combining Mirror Facets</title>
		<link>http://www.wirfs-brock.com/allen/posts/314</link>
		<comments>http://www.wirfs-brock.com/allen/posts/314#comments</comments>
		<pubDate>Fri, 29 Apr 2011 22:17:58 +0000</pubDate>
		<dc:creator>allen</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Reflection]]></category>
		<category><![CDATA[ECMAScript]]></category>
		<category><![CDATA[experimental JavaScript]]></category>
		<category><![CDATA[JavaScript extensions]]></category>
		<category><![CDATA[mirrors]]></category>
		<category><![CDATA[prototypes]]></category>
		<category><![CDATA[reflection]]></category>

		<guid isPermaLink="false">http://www.wirfs-brock.com/allen/?p=314</guid>
		<description><![CDATA[In my last couple posts I introduced idea of using Mirrors for JavaScript reflection and took a first look at the introspection interfaces of my jsmirrors prototype. In this post I&#8217;m going to look at the other reflection interfaces in jsmirrors and how they are mixed together to provide various levels of reflection privilege. When [...]]]></description>
			<content:encoded><![CDATA[<p></p><p><a href="http://www.wirfs-brock.com/allen/wp-content/uploads/2011/04/mirrorfacets.png"><img src="http://www.wirfs-brock.com/allen/wp-content/uploads/2011/04/mirrorfacets-300x225.png" alt="" title="mirrorfacets" width="300" height="225" class="alignright size-medium wp-image-349" /></a>In my last couple posts I introduced idea of using <a title="Experimenting with Mirrors for JavaScript" href="http://www.wirfs-brock.com/allen/posts/228" target="_self">Mirrors for JavaScript reflection</a> and took a first look <a title="Looking into Mirrors" href="http://www.wirfs-brock.com/allen/posts/245">at the introspection interfaces</a> of my <a title="jsmirrors on github" href="https://github.com/allenwb/jsmirrors" target="_blank">jsmirrors</a> prototype.  In this post I&#8217;m going to look at the other reflection interfaces in jsmirrors and how they are mixed together to provide various levels of reflection privilege.</p>
<p>When building this prototype I knew that I wanted to have a number of separable sets of reflection capabilities that I could mix and match in various ways.  I also knew that the implementation was likely to change several times as I experimented with the prototype. I wanted to make sure that as I evolved the implementation that I could keep track of what belonged in each separable piece.  The way I ultimately accomplished this was by maintaining a  file of interface definitions  that are separate from the actual code that implements <a title="jsmirrors on github" href="https://github.com/allenwb/jsmirrors" target="_blank">jsmirrors</a>. The interface specifications are contained in the file <a title="mirrorsInterfaceSpec.js" href="https://github.com/allenwb/jsmirrors/blob/master/mirrorsInterfaceSpec.js" target="_blank">mirrorsInterfaceSpec.js</a>. I look at the interface file when I need to remind myself how to use one of the specific reflection interfaces and as a specification as I make changes to the implementaiton.  Also, whenever I perform a major refactoring of the implementation I check it against the interface specification.  Here is the interface specification of the basic object introspection interface that I  demonstrated in the  <a title="Looking into Mirrors" href="http://www.wirfs-brock.com/allen/posts/245">Looking into Mirrors</a> post: </p>
<pre class="brush: jscript; light: true; title: ; notranslate">
//Mirror for introspect upon all objects
var objectMirrorInterface = extendsInterface(objectBasicMirrorInterface, {
   prototype:  getAccess(objectMirrorInterface|null),
     //return a mirror on the reflected object's [[Prototype]]
   extensible: getAccess(Boolean),
     //return true if the reflected object is extensible
   ownProperties: getAccess(array(propertyMirrorInterface)),
     //return an array containing property mirrors
     //on the reflected object's own properties
   ownPropertyNames: getAccess(array(String)),
     //return an array containing the string names
     //of the reflected object's own properties
   keys: getAccess(array(String)),
     //return an array containing the string names of the
     //reflected object's enumerable own properties
   enumerationOrder: getAccess(array(String)),
     //return an array containing the string names of the
     //reflected object's enumerable own and inherited properties
   prop: method({name:String}, returns(propertyMirrorInterface|undefined)),
     //return a mirror on an own property
   lookup: method({name:String},returns(propertyMirrorInterface|undefined)),
     //return mirror on the result of a property lookup. It may be inherited
   has: method({name:String}, returns(Boolean)),
     //return true if the reflected object has a property named 'name'
   hasOwn: method({name:String}, returns(Boolean)),
     //return true if the reflected object has an own property named 'name'
   specialClass: getAccess(String)
    //return the value of the reflected object's [[Class]] internal property
});
</pre>
<p>I used JavaScript object literals and a few helper functions to describe these interfaces.  Here is the definition of the helper functions used for this interface:</p>
<pre class="brush: jscript; light: true; title: ; notranslate">
function getAccess(returnInterface) {}; //a &quot;get-able&quot; property
function method(arguments,returnInterface){}; // a method property
function extendsInterface(supers,members) {};//a interface adding to supers
function returns(returnInterface) {};   //return value of a method
function array(elementInterface) {};//array elements all support a interface
</pre>
<p>The JavaScript code of the interface definitions don&#8217;t actually do anything but I find that being able to parse the interface specification using JavaScript forces me to apply some useful structuring discipline that I might skip if I was just writing prose descriptions. Plus I think it is going to be quite useful to have these interface specifications in a form that is easily processed.  For example, now that I have an initial implementation of jsmirrors, I may use it to create a little tool that can reflect upon the objects created by the interface specifications and perform useful tasks.  For example I may generate unit test stubs for implementations of the interfaces. I may also use reflection over the interfaces to directly validate the completeness of my implementations.</p>
<p>In factoring the jsmirrors functionality for reflecting upon objects I divided it to three primary interfaces.  <code>objectMirrorInterface</code>, shown above, is the basic introspection interface. <code>objectMutationMirrorInterface</code> allows changes to be made to a reflected object such as adding or removing properties or changing the object&#8217;s prototype. <code>objectEvalMirrorInterface</code> allows various forms of evaluation upon reflected objects such as doing &#8220;puts&#8221; and &#8220;gets&#8221; (which may invoke accessor property functions) to access property values of a reflected object or to invoke a method property.  There are also corresponding introspection, mutation, and evaluation interfaces for function object mirrors and also for property mirrors.</p>
<p>In the actual implementation, these interfaces are combined in various ways to produce five different kinds of concrete mirrors on local objects.  These various kinds of mirrors are accessible via factory functions that are accessed as properties of the <code>Mirrors</code> module object.  The five  local object mirror factories are:</p>
<ul>
<li><code>Mirrors.introspect</code>  &#8211;  supports only introspection using <code>objectMirrorInterface</code>.</li>
<li><code>Mirrors.evaluation</code>  &#8211; supports only  evaluation using <code>objectEvalMirrorInterface</code>.</li>
<li><code>Mirrors.introspectEval</code>  &#8211;  supports introspection and evaluation using <code>objectMirrorInterface</code> and <code>objectEvalMirrorInterface</code>.</li>
<li><code>Mirrors.mutate</code>  &#8211;  supports introspection and mutation using <code>objectMirrorInterface</code> and <code>objectMutationMirrorInterface</code>.</li>
<li><code>Mirrors.fullLocal</code>  &#8211;  supports introspection, mutation, and evaluation using all three interfaces.</li>
</ul>
<p>I demonstrated the use of <code>Mirrors.introspect</code> is my previous  <a title="Looking into Mirrors" href="http://www.wirfs-brock.com/allen/posts/245">post</a>.  The other <code>Mirror</code> factories are used in exactly the same manner and, except for <code>Mirrors.evaluation</code>, could be used to run all the same examples.  However, the other factories expose additional functionality that isn&#8217;t available using <code>Mirrors.introspect</code>. Take a look at the actual interface specification in <a title="mirrorsInterfaceSpec.js" href="https://github.com/allenwb/jsmirrors/blob/master/mirrorsInterfaceSpec.js" target="_blank">mirrorsInterfaceSpec.js</a> to see which capabilities are provided by the mirror objects produced by each of these factories.</p>
<p>The reason for providing multiple mirror factories  is to demonstrate that by using mirror-based reflection we can decide exactly how much reflection capability we will make available to any specific client or tool. We might allow one tool to use the full range of reflective interfaces. For another we may only expose introspection or evaluation capabilities or perhaps introspection and mutation capabilities without the ability to actually do reflective evaluation. However, so far, I&#8217;ve only shown mirrors that know how to reflect upon local objects that exist in the same heap as the mirror objects.  In my next post I&#8217;ll look at how to use the same interfaces to reflect upon non-local objects that might be encoded in a file or exist in a remote environment.</p>
<p>(<a href="http://www.flickr.com/photos/16782093@N03/3951491170/" target="_blank">Photo</a> by “Metro Centric”, Creative Commons Attribution License)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.wirfs-brock.com/allen/posts/314/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Looking into Mirrors</title>
		<link>http://www.wirfs-brock.com/allen/posts/245</link>
		<comments>http://www.wirfs-brock.com/allen/posts/245#comments</comments>
		<pubDate>Wed, 27 Apr 2011 22:20:48 +0000</pubDate>
		<dc:creator>allen</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Reflection]]></category>
		<category><![CDATA[ECMAScript]]></category>
		<category><![CDATA[ES5]]></category>
		<category><![CDATA[experimental JavaScript]]></category>
		<category><![CDATA[JavaScript extensions]]></category>
		<category><![CDATA[mirrors]]></category>
		<category><![CDATA[prototypes]]></category>
		<category><![CDATA[reflection]]></category>

		<guid isPermaLink="false">http://www.wirfs-brock.com/allen/?p=245</guid>
		<description><![CDATA[In my last post I introduced the programming language concept of Mirrors and mentioned jsmirrors, the prototype I&#8217;ve been working on to explore using mirrors to support reflection within JavaScript.  In this post I&#8217;m going to take a deeper look into jsmirrors itself.  I had three goals for my first iteration of jsmirrors: Define basic [...]]]></description>
			<content:encoded><![CDATA[<p></p><p><a href="http://www.wirfs-brock.com/allen/wp-content/uploads/2011/04/js-mirror.png"><img src="http://www.wirfs-brock.com/allen/wp-content/uploads/2011/04/js-mirror-300x224.png" alt="" title="js-mirror" width="300" height="224" class="alignright size-medium wp-image-306" /></a>In my <a title="Experimenting with Mirrors for JavaScript" href="http://www.wirfs-brock.com/allen/posts/228" target="_self">last post</a> I introduced the programming language concept of Mirrors and mentioned <a title="jsmirrors on github" href="https://github.com/allenwb/jsmirrors" target="_blank">jsmirrors</a>, the prototype I&#8217;ve been working on to explore using mirrors to support reflection within JavaScript.  In this post I&#8217;m going to take a deeper look into jsmirrors itself.  I had three goals for my first iteration of jsmirrors:</p>
<ol>
<li>Define basic mirror-based interfaces for reflection upon upon JavaScript objects and properties.</li>
<li>Demonstrate that jsmirrors  can support different levels of reflection privilege.</li>
<li>Demonstrate that the jsmirrors interface can work with both local and external objects.</li>
</ol>
<p>In this post I&#8217;m going to concentrate on showing details of the basic interfaces I designed to meet the first goal.  In subsequent posts I talk about the other two goals.</p>
<p>The actual implementation of jsmirrors is contained in the file <a title="mirrors.js" href="https://github.com/allenwb/jsmirrors/blob/master/mirrors.js" target="_blank">mirrors.js</a>.  Note that jsmirrors requires an ECMAScript 5 compatible JavaScript implementation. The jsmirrors implementation is structured using the <a title="module pattern" href="http://yuiblog.com/blog/2007/06/12/module-pattern/" target="_blank">module pattern</a> and when loaded defines a single global named <code>Mirrors</code> whose properties are factory functions that can be used to create various kinds of mirror objects.  The most basic mirror factory is called <code>introspect</code> and creates a mirror on a local object that only supports introspection (examination without modification):  </p>
<pre class="brush: jscript; title: ; notranslate">
//create a test object
var obj = {a:1, get b() {return &quot;b value&quot;}, c: undefined};
obj.c = {back: obj};  //make a circular reference to obj

//create an introspection mirror on obj
var m=Mirrors.introspect(obj);
console.log(m);   //output:  &quot;Object Introspection Mirror #0&quot;
</pre>
<p>In the above example, lines 2-3  create a couple of test objects and line 6 is creating an introspection mirror on one of them.  We see from the output of line 7 how such mirror objects identify themselves using the <code>toString</code> method.  Once we have such a mirror, we can use it to examine the structure and state of its reflected object:</p>
<pre class="brush: jscript; first-line: 8; title: ; notranslate">
console.log(m.ownPropertyNames) ;  //output:  &quot;a,b,c&quot;
console.log(m.extensible); //output:  true
console.log(m.has(&quot;toString&quot;)); //output:  true
console.log(m.hasOwn(&quot;toString&quot;)); //output:  false
var p=m.prototype;
console.log(p); //output:  &quot;Object Introspection Mirror #3&quot;
console.log(p.hasOwn(&quot;toString&quot;)); //output:  true
</pre>
<p>Lines 8-11 are querying various characteristics of the object reflected by the mirror <code>m</code> such as a list of its own property names, whether or not additional properties may be added, and whether it locally defines or inherits a specific property. Line 12 queries for the object that is the prototype object for the reflected object.  Note from line 13 that the value returned is also an introspection mirror.  This is one of the important characteristics of this style of mirror interface.  When an object value is accessed a mirror on the object is always returned rather than the actual object.  You may be curious why the mirror <coce>p</code> is "Mirror #3" rather than "Mirror #1".  The reason is that some of the preceding method calls internally generated Mirrors #1-2 as part of their internal implementation.</p>
<p>
Mirror objects aren't unique.  Multiple mirror objects may simultaneously exist that reflect on the same underlying object.  The <code>sameAs</code> method can be used to determine if two mirrors are reflecting the same object:</p>
<pre class="brush: jscript; first-line: 15; title: ; notranslate">
console.log(m.sameAs(p)) ;  //output:  false
var opm = Mirrors.introspect(Object.prototype);
console.log(p.sameAs(opm)); //output:  true
</pre>
<p>Introspection mirrors support several other methods.  The complete list can be seen by looking at the <code>objectMirrorInterface</code> specification in <a title="mirrorsInterfaceSpec.js" href="https://github.com/allenwb/jsmirrors/blob/master/mirrorsInterfaceSpec.js" target="_blank">mirrorsInterfaceSpec.js</a>.  Some of the most important methods provide access to information about specific properties.  Property mirrors are returned to enable introspection of actual property definitions:</p>
<pre class="brush: jscript; first-line: 18; title: ; notranslate">
var pmb = m.lookup(&quot;b&quot;);
console.log(pmb);
  //output: &quot;Accessor Property Introspection Mirror name: b #6&quot;
</pre>
<p>In line 18 the method <code>lookup</code> on a mirror object is used to retrive the property named "b".  What is return in this case is a property introspection mirror. The interface specifications <code>propertyMirrorInterface</code>, <code>dataPropertyMirrorInteface</code>, and <code>accessorPropertyMirrorInteface</code> in <a title="mirrorsInterfaceSpec.js" href="https://github.com/allenwb/jsmirrors/blob/master/mirrorsInterfaceSpec.js" target="_blank">mirrorsInterfaceSpec.js</a> describe the operations that can be performed on property introspection mirrors.  For example:</p>
<pre class="brush: jscript; first-line: 21; title: ; notranslate">
console.log(pmb.isData);  //output: false
console.log(pmb.isAccessor); //output: true
console.log(pmb.enumerable); //output: true
Object.defineProperty(obj,&quot;b&quot;,{enumerable: false});
console.log(pmb.enumerable); //output: false
</pre>
<p>Lines 21-22 show tests to determine whether the reflected property is a data property or an accessor property and line 23 reports the state of the property's enumerable attribute.  Lines 24-25 demonstrate that the mirror is presenting a live view of the reflected object.  Line 24 modifies the enumerable attribute of the "b" property of the reflected object.  When the mirror is again used in line 25 we see that the reported state of the enumerable attribute has changed to false.  Note that we had to use a built-in reflection function to change the enumerable attribute because the mirrors we are using in the above examples only support introspection and don't allow any changes to the reflected objects to be made using the mirrors.</p>
<pre class="brush: jscript; first-line: 26; title: ; notranslate">
console.log(pmb.definedOn.sameAs(m)); //output: true
var fm=pmb.getter;
console.log(fm);  //output: &quot;Function Introspection Mirror #8&quot;
console.log(fm.source); //output: &quot;function () {return \&quot;b value\&quot;;}&quot;
</pre>
<p>Property mirrors know what object "owns" the reflected property.  Line 26 shows using  <code>definedOn</code>  to get a mirror on the owning object.  We then use <code>sameAs</code> to verify that this mirror is actually reflecting the same object as our original mirror <code>m</code>. Because the property we are reflecting upon is an accessor property it has getter and setter functions.  In line 27 we use the property mirror to access the property's getter  function and in line 28 we see that the results is yet another kind of mirror, a "Function Introspection Mirror".  As specified by the <code/>functionMirrorInterface</code>  in <a title="mirrorsInterfaceSpec.js" href="https://github.com/allenwb/jsmirrors/blob/master/mirrorsInterfaceSpec.js" target="_blank">mirrorsInterfaceSpec.js</a> this is a kind of object mirror that adds reflection capabilities that are specific to function objects. For example, in line 29 we see that we can use the function mirror to retrieve the source code of the getter function.</p>
<p>The above examples provide just a quick overview of the capability of jsmirrors introspection mirrors and how they are used.  But these mirrors only allow the inspection of objects.  In many situations that is the only kind of reflection you need or  that you will want to permit.  However, there are  situations where reflection needs to be able to perform other operations such as modifying the definitions of properties or calling reflected functions. In my next post, I'll explore how jsmirrors supports those kinds of reflection and how it can be used to control or limit access to them.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.wirfs-brock.com/allen/posts/245/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Experimenting with Mirrors for JavaScript</title>
		<link>http://www.wirfs-brock.com/allen/posts/228</link>
		<comments>http://www.wirfs-brock.com/allen/posts/228#comments</comments>
		<pubDate>Mon, 25 Apr 2011 23:12:40 +0000</pubDate>
		<dc:creator>allen</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Reflection]]></category>
		<category><![CDATA[ECMAScript]]></category>
		<category><![CDATA[ES5]]></category>
		<category><![CDATA[experimental JavaScript]]></category>
		<category><![CDATA[JavaScript extensions]]></category>
		<category><![CDATA[mirrors]]></category>
		<category><![CDATA[prototypes]]></category>
		<category><![CDATA[reflection]]></category>
		<category><![CDATA[self]]></category>

		<guid isPermaLink="false">http://www.wirfs-brock.com/allen/?p=228</guid>
		<description><![CDATA[A common capability of many dynamic languages, such as JavaScript, is the ability of a program to inspect and modify its own structure.  This capability is generally called reflection. Examples of reflective capabilities of JavaScript include things like the hasOwnProperty and isPrototypeOf methods. ECMAScript 5 extended the reflection capability to JavaScript via functions such as [...]]]></description>
			<content:encoded><![CDATA[<p></p><p>A common capability of many dynamic languages, such as JavaScript, is the ability of a program to inspect and modify its own structure.  This capability is generally called <em>reflection</em>.  Examples of reflective capabilities of JavaScript include things like the <code>hasOwnProperty</code> and <code>isPrototypeOf</code> methods. ECMAScript 5 extended the reflection capability to JavaScript via functions such as <code>Object.defineProperty</code> and <code>Object.getOwnPropertyDescriptor</code>. There are many reasons you might use reflection but two very common uses are for creating development/debugging tool and for meta-programming.<br />
<a href="http://www.wirfs-brock.com/allen/wp-content/uploads/2011/04/mirrors.jpg"><img class="alignright size-medium wp-image-234" title="mirrors" src="http://www.wirfs-brock.com/allen/wp-content/uploads/2011/04/mirrors-225x300.jpg" alt="" width="225" height="300" /></a><br />
There are many different ways you might define a reflection API for a programming language. For example, in JavaScript <code>hasOwnProperty</code> is a method defined by <code>Object.prototype</code> so it is, in theory,  available to be called as a method on all objects. But there is a problem with this approach.  What happens if an application object defines its own method named <code>hasOwnPro</code><code>perty</code>?  The application object definition will override the definition of <code>hasOwnProperty</code> that is normally inherited from <code>Object.prototype</code>.  Unexpected results are likely to occur if such an object is passed to code that expects to do reflection using the built-in <code>hasOwnProperty</code> method.  This is one of the reasons that the new reflection capabilities in ES5 are defined as functions on <code>Object</code> rather than as methods of <code>Object.prototype</code>.</p>
<p>Another issue that arises with many reflection APIs is that they  typically only work with local objects.  Consider a tool that gives application developers the ability to graphically browse and inspect the objects in an application. If such a tool is effective, developers might want to use it in other situations.  For example, they  might want to inspect the objects on a remote server-based JavaScript application or to inspect a diagnostic JSON dump of objects produced when an application crashed. If JavaScript&#8217;s existing reflection APIs were used to create the tool there is no direct way it can be used to inspect such objects because the JavaScript reflection APIs only operate upon local objects within the current program.</p>
<p>There is also a tension between the power of reflection and security concerns within applications.  Many of the reflection capabilities that are most useful to tool builders and meta-programmers can also be exploited for malicious purposes.  Reflection API designers sometimes exclude potentially useful features in order to eliminate the potential of such exploits.</p>
<p><em>Mirrors</em> is the name of  an approach to reflection API design that attempts to address many of the issue that have been encountered with various programming languages that support reflection. The basic idea of mirrors is that you never perform reflective operations directly upon application objects.  Instead all such operations are performed upon distinct &#8220;mirror&#8221; objects that &#8220;reflect&#8221; the structure of corresponding application objects.  For example, instead of coding something like:</p>
<p style="padding-left: 30px;"><code>if (someObj.hasOwnProperty('customer')) {...</code></p>
<p>you might accomplish the same thing via mirrors via something like:</p>
<p style="padding-left: 30px;"><code>if (Mirror.on(someObj).hasOwnProperty('customer')) {...</code></p>
<p>Mirrors don&#8217;t have the sort of issues I discussed above because when using them you never directly reflect on application objects. There is never any problem  if the application just happens to define a method that  has the same name as a reflection API method.  Because reflection-based tools only indirectly interact with the underlying objects via mirror objects, it is possible to create different mirrors that use a common interface to access either local object, remote objects, or static objects stored in a file. Similar, it is possible to have have mirrors that present a common interface but differ in terms how much reflection they allow.  A trusted tool might be given access to a mirror that supports the must power reflective operations while an untrusted plug-in might be restricted to using mirrors that support only a limited set of reflective operations.</p>
<p>Gilad Bracha and David Ungar are the authors of a paper that explain the principals behind mirror-based reflection: <a href="http://bracha.org/mirrors.pdf">Mirrors: Design Principles for Meta-level Facilities of Object-Oriented Programming Languages</a>.  I highly recommend it if you are interested in the general topic of reflection.</p>
<p>Mirrors were originally developed for the <a title="selflanguage.org" href="http://selflanguage.org/" target="_blank">self</a> programming language, one of the languages that influenced the original design of JavaScript. Recently, I&#8217;ve been experimenting with defining a mirror based reflection interface for JavaScript.  An early prototype of this interface named <a href="https://github.com/allenwb/jsmirrors">jsmirrors</a> is now up on github.  It uses a common interface to support reflection on both local JavaScript objects and on a JSON-based object encoding that could be used for remote or externally stored objects. It also supports three levels of reflection privilege.</p>
<p>In my <a title="Looking into Mirrors" href="http://www.wirfs-brock.com/allen/posts/245">next post</a> I&#8217;ll explain more of the usage and design details of jsmirrors.  In the meantime, please feel free to take a look at the prototype.</p>
<p><span class="fontsizexsmall">(<a href="http://www.flickr.com/photos/dichohecho/4259487517/" target="_blank">Photo</a> by &#8220;dichohecho&#8221;, Creative Commons Attribution License)</span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.wirfs-brock.com/allen/posts/228/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Why Mozilla?</title>
		<link>http://www.wirfs-brock.com/allen/posts/210</link>
		<comments>http://www.wirfs-brock.com/allen/posts/210#comments</comments>
		<pubDate>Fri, 25 Mar 2011 20:07:13 +0000</pubDate>
		<dc:creator>allen</dc:creator>
				<category><![CDATA[Mozilla]]></category>
		<category><![CDATA[Post-PC/Ambient Computing]]></category>
		<category><![CDATA[ambient compting]]></category>
		<category><![CDATA[computing eras]]></category>
		<category><![CDATA[innovation]]></category>
		<category><![CDATA[standards]]></category>
		<category><![CDATA[transitional periods]]></category>
		<category><![CDATA[ubiquitous]]></category>

		<guid isPermaLink="false">http://www.wirfs-brock.com/allen/?p=210</guid>
		<description><![CDATA[As somebody who is on record as believing that web browsers are a transitional technology, people occasionally ask me why I decided to go to work for a &#8220;browser company&#8221; like Mozilla. You can find a big part of the answer here: As we move deeper into The Next Era of Computing there are still [...]]]></description>
			<content:encoded><![CDATA[<p></p><p>As somebody who is on record as believing that <a title="The Browser is a Transitonal Technology" href="http://www.wirfs-brock.com/allen/posts/115" target="_self">web browsers are a transitional technology</a>,   people occasionally ask me why I  decided to go to work for a &#8220;browser company&#8221; like Mozilla. You can find a big part of the answer here:</p>
<p><video id="mozilla_firefox_manifesto_video" controls="controls" width="600" height="360" poster="http://www.mozilla.org/images/about/poster.jpg"><br />
    <source src="http://videos-cdn.mozilla.net/brand/Mozilla_Firefox_Manifesto_v0.2_640.webm" type="video/webm"/><br />
    <source src="http://videos-cdn.mozilla.net/brand/Mozilla_Firefox_Manifesto_v0.2_640.theora.ogv" type="video/ogg"/><br />
    <source src="http://videos-cdn.mozilla.net/brand/Mozilla_Firefox_Manifesto_v0.2_640.mp4" type="video/mp4"/><br />
    <object type="application/x-shockwave-flash" style="width: 640px; height: 360px;" data="http://www.mozilla.com/includes/flash/playerWithControls.swf?flv=/serv/brand/Mozilla_Firefox_Manifesto_v0.2_640.mp4&amp;autoplay=false&amp;msg=Play%20Video"><param name="movie" value="http://www.mozilla.com/includes/flash/playerWithControls.swf?flv=/serv/brand/Mozilla_Firefox_Manifesto_v0.2_640.mp4&amp;autoplay=false&amp;msg=Play%20Video"/><param name="wmode" value="transparent" /></object><br />
  </video></p>
<p>As we move deeper into  <a href="http://www.wirfs-brock.com/allen/posts/74">The Next Era of Computing</a> there are still many questions about  which technologies, organizations, and business models will define it. In every previous computing era and sub-era, a single proprietary &#8220;platform&#8221; emerged to dominate it.  Will this happen again for the  Ambient Computing era?  A common platform is essential because it provides the foundation that everything else is built upon. This enables innovators to focus on creating their unique  value rather than wasting most of their time recreating necessary infrastructure. It also enables, these technical innovations to be made ubiquitously available.</p>
<p>The current foundations of the emerging computing era are open web technologies. Can the standards-based open web maintain its role as the universal platform for this era? If so, it will need to continue to evolve and embrace innovation.  Having just returned from a JavaScript standards meeting, I&#8217;m again reminded about how messy and slow  consensus driven &#8220;standards&#8221; processes can be.  Standards committees are not places  where rapid innovation can or necessarily should occur.  Proprietary platform vender have a real advantage in their ability to unilaterally make innovative choices about the evolution of their platform. However, those choices are always first and foremost driven by the business interest of the organizations and their shareholders.</p>
<p>If the standard&#8217;s based open web platform is going to continue to be the dominant platform for this era, its evolution needs to be driven by agile innovative organizations who are dedicated to its success.  We need  pragmatic organizations who are driven by the interests of computing users and not just their own dominance and profitability. Mozilla is such an organization. I think it has an essential role to play in advancing  the next generation of computing technology and I&#8217;m really excited to be a part of it. so, I encourage everybody to <a href="http://www.mozilla.org/about/mission.html">find out more</a> <a href="http://www.mozilla.org/about/manifesto">about Mozilla</a> and <a href="http://www.mozilla.com">how</a>  <a href="http://www.mozilla.org/contribute/">you can</a> <a href="https://donate.mozilla.org/page/contribute/protect-the-web">contribute</a>. </p>
]]></content:encoded>
			<wfw:commentRss>http://www.wirfs-brock.com/allen/posts/210/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
<enclosure url="http://videos-cdn.mozilla.net/brand/Mozilla_Firefox_Manifesto_v0.2_640.theora.ogv" length="28759931" type="video/ogg" />
<enclosure url="http://videos-cdn.mozilla.net/brand/Mozilla_Firefox_Manifesto_v0.2_640.webm" length="10484095" type="video/webm" />
<enclosure url="http://videos-cdn.mozilla.net/brand/Mozilla_Firefox_Manifesto_v0.2_640.mp4" length="37665940" type="video/mp4" />
		</item>
		<item>
		<title>The SPLASH Submission Clock is Ticking</title>
		<link>http://www.wirfs-brock.com/allen/posts/204</link>
		<comments>http://www.wirfs-brock.com/allen/posts/204#comments</comments>
		<pubDate>Thu, 10 Mar 2011 22:02:57 +0000</pubDate>
		<dc:creator>allen</dc:creator>
				<category><![CDATA[SPLASH Conference]]></category>
		<category><![CDATA[conferences]]></category>
		<category><![CDATA[SPLASH]]></category>
		<category><![CDATA[Wavefront]]></category>

		<guid isPermaLink="false">http://www.wirfs-brock.com/allen/?p=204</guid>
		<description><![CDATA[I&#8217;ve previously written about the SPLASH Conference and why you might consider writing for it. Now, is the time to get serious as April 8, 2011 is the submission deadline for the major SPLASH conference tracks. If your aren&#8217;t familiar with SPLASH, here is how the SPLASH website describes itself: Since 2010 SPLASH is the [...]]]></description>
			<content:encoded><![CDATA[<p></p><p style="text-align: left;">I&#8217;ve <a title="Make a SPLASH - Write a Paper" href="http://www.wirfs-brock.com/allen/posts/49" target="_self">previously</a> <a title="SPLASH - Write to Share" href="http://www.wirfs-brock.com/allen/posts/57" target="_self">written</a> about the <a title="http://splashcon.org/2011/" href="http://splashcon.org/2011/" target="_blank">SPLASH Conference </a>and why you might consider writing for it. Now, is the time to get serious as <strong>April 8, 2011 is the submission deadline</strong> for the major SPLASH conference tracks. If your aren&#8217;t familiar with SPLASH, here is how the SPLASH website describes itself:</p>
<p style="text-align: center;"><img class="aligncenter" title="SPLASH 2011" src="http://splashcon.org/2011/templates/splash2011/images/logotype2011-2.png" alt="" width="480" height="76" /></p>
<blockquote><p>Since 2010 SPLASH is the new umbrella  conference for OOPSLA and Onward!. This year it features a third  technical track, Wavefront, designed to publish innovative work closely  related to advanced development and production software. SPLASH takes on  the <a title="SPLASH/OOPSLA History" href="http://splashcon.org/history/" target="_blank">notable track record of OOPSLA as a premier forum for software innovation</a>, while broadening the scope of the conference  into new topics beyond objects and new forms of contributions.</p>
<p>The overall theme of the conference is <em>The Internet as the world-wide Virtual Machine</em>.   This theme captures the change in the order of magnitude of computing  that happened over the past few years. These days software systems are  rarely designed in isolation; they connect to pieces written by 3rd  parties, they communicate with other pieces over the Internet, they use  big data produced elsewhere, they touch millions of interacting users  through an ever larger variety of physical devices&#8230; in other words,  the &#8220;machine&#8221; is now a global computing network. What does this entail  for software development itself?</p>
<p>SPLASH&#8217;s mission is to engage software  innovators from all walks of life in conversations about bettering  software. This involves new ideas about programming languages, tools,  conceptual models, and methodologies that can cope with, evolve, and  leverage, the complex world-wide Virtual Machine that is emerging in  front of our eyes. With the contributions of many volunteers, we are  putting together another exciting program for next year. We look forward  to your contributions.</p></blockquote>
<p>The SPLASH <a title="http://splashcon.org/2011/cfp" href="http://splashcon.org/2011/cfp" target="_blank">Call for Papers</a> describe the various tracks and their  submission procedures.  I&#8217;m the program chair of the new <a title="SPLASH/Wavefront Call for Papers" href="http://splashcon.org/2011/cfp/101" target="_blank">Wavefront track</a> where we are  looking for submissions describing innovative real world (and particularly web-related) software innovations:</p>
<blockquote><p>Wavefront seeks papers that describe original and innovative architecture, design, and/or implementation techniques used in actual leading-edge software system. Submissions from practicing software developers are strongly encouraged. Research or advanced development papers must address a problem of immediate concern for such systems and present immediately applicable results.</p></blockquote>
<p>Our goal with Wavefront is to engage the software developers who are actually creating  next generation of software systems and to make sure  that their innovations are captured in the technical archives of computing.  So if you are creating such systems please consider submitting.  And please note that for Wavefront you don&#8217;t have to have your complete paper finished by April 8.  Wavefront is accepting 2-5 page extended abstracts and if your abstract is accepted we will  shepherd you through the process of creating a high quality technical paper. I highly recommend that you take advantage of this option if this is your first submission to a publication-oriented technical conference. Additional details can be found in the <a title="SPLASH/Wavefront Call for Papers" href="http://splashcon.org/2011/cfp/101" target="_blank">Wavefront Call for Papers</a>.</p>
<p>I look forward to receiving your submissions.  If you have any questions about Wavefront please <a title="SPLASH/Wavefront mailto:" href="mailto:wavefront@splashcon.org">email me</a>.</p>
<address>Allen Wirfs-Brock</address>
<address>2011 SPLASH/Wavefront Program Chair<br />
</address>
<blockquote></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.wirfs-brock.com/allen/posts/204/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>A JavaScript Optional Argument Hazard</title>
		<link>http://www.wirfs-brock.com/allen/posts/166</link>
		<comments>http://www.wirfs-brock.com/allen/posts/166#comments</comments>
		<pubDate>Thu, 24 Feb 2011 23:27:19 +0000</pubDate>
		<dc:creator>allen</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[ECMAScript]]></category>
		<category><![CDATA[ES5]]></category>
		<category><![CDATA[JavaScript extensions]]></category>
		<category><![CDATA[JavaScript Hacks]]></category>

		<guid isPermaLink="false">http://www.wirfs-brock.com/allen/?p=166</guid>
		<description><![CDATA[My recent post on testing for negative 0 in JavaScript created a lot of interest.  So today, I’m going to talk about another bit of JavaScript obscurity that was also inspired by a Twitter thread. I recently noticed this tweet go by: This was obviously a trick question.  Presumably some programmer expected this expression to [...]]]></description>
			<content:encoded><![CDATA[<p></p><p>My recent post on <a title="http://www.wirfs-brock.com/allen/posts/128" href="http://www.wirfs-brock.com/allen/posts/128" target="_self">testing for negative 0 in JavaScript</a> created a lot of interest.  So today, I’m going to talk about another bit of JavaScript obscurity that  was also inspired by a Twitter thread.</p>
<p>I recently noticed this tweet go by:<br />
<img style="border: 1px solid black;" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAfwAAABVCAYAAACsNlaBAAAgAElEQVR4Ae1dC0BVVdb+TMSLgk80NEShfAQTvtAsAR9gJKkJk6KN0O8jDWoGG+0PZlLTarBJM6dJ0pGcX5yUHuCvhr8PzBSTFDNJSEXBByn5AgXlykP/tfa553Lu5d7LQyyzvZV7ztmPtff+zj577bX22ns3uUUO0kkEJAISAYmAREAicE8jcN89XTtZOYmAREAiIBGQCEgEBAKS4cuGIBGQCEgEJAISgd8AApLh/wZesqyiREAiIBGQCEgEJMOXbUAiIBGQCEgEJAK/AQQkw/8NvGRZRYmAREAiIBGQCEiGb6MN6EtLUVxcikobcWSQREAiIBGQCEgEfg0ISIZv9S2V4uOIoQgMHIqX/nPMaqz6BlReyMb69evp7yuckyOJ+sIn40sEJAISAYlAAxGwa0i6qltVSjJewd+kHhTM45vvAMC0avNT81NpqfG1abVxuHhqmOHa9L6m9Sg0cP7ilXrFtxVZ/+NuvPnmSorihvjAIejkaCv2rz2sCteu6YGm9tDpmqF+qFdBr0n7a0dCll8iIBGQCPzSCDSI4RuZsspIuRbqPV8tOXMGbS2Oub/K0FV/flb9rOWlxr2tqyOmfJKBCKZhZwZTZSX0JJ3rdGb+JvlRHOJ15nF0LZwMsTqihc4kgfJQqSfadjXSmcfUE3E7nQ7WS6BHxurVyPMIxrO+rubJb/+54jz2f3MBj/h6wVI1zmV/hc17jmqmQ+zw4OCRCPDqVGve57LSsDnjhCZtB/j/fhR6tW9GaatQkJ2Nqi4Po2srfpZOIiARkAhIBOqCgHV+UVtqcwavMvTa0mnDtQzbEhO35sfp1LTqVZu/em8epvpry2D1vhSp787FhrzrGBgxD1MeZ0ZViq8++htmLdsqUrn5h2NU15+w74fLQOcxWDQ3GHZn9mP5kveRuCtHodzWDWMmRuNPU/pj59/nIvnwScUfmXhuZASmvfYWXhjSCce+SsKHCf/GrpwiY7rx/zUbUX94HI44h4/+Mh/7LreD35MDcSL5A2wQ8dpizKt/w1/GDbDI+HnGQF9p0MYYcm2sS8VP3+Ngjh36+NakeCk7FRv3FMDVexge79MFuvJrOJG1C3v2bAQcJiDAo1XNRAafivP7sZGYvatPIIZ4dqEK/ITMbanY9fmXcJn+BNrgJk7sycCNIGb4VsnIAImAREAiIBEwQ6DhDF9lnnxlx8xV9RMeZj+WwtW0HFXLnNWktfmp4Wp6lZ7WX1sm9tc+q/lYuRYe3oVM4tvn+xQIhv/10gjMSjxtjH16VyKWqU9uA1Gpz8bskEhi5eTcfODf7Tp2EePfsGwWNhT8HW/hJHJyqtOjKAdf/3AJTxTF49k3lUFEW08fPIgTlO9pfLLkTyi0/wTvjryKnVszwUOIzEwlnpJtETa8HQlXz52Y4mV5bsD4gq8VIG3rlzhxoYySOqBbX38EDOiKphUF2PDZt3D1aofsjBxwaIee/hg1pBdwbj/WpQPjaEDBUnxV8REkJx/DgIBO2LHlhChC4qfN8ey4QdVSftV57CJm7+QdjOBBBs0CaSK8fENgX/UpMo+fRRUxfGvq/aqy64JuVw8PtORMda4YMmIYru06i7LrBdi1LhWFHGPLKqRQHiH97ZG+OQ05hSUinZNrXzwVNAAONHD4LP0q3NtdRdaJCxTmAO/ApzHIxmBDEJA/EgGJgETgHkXgzhjtqYzXHDSVEXO4ynjZT+uvPlvz06bV5qPeq+k4bzUPvmenxlGeav1tbojhCFIdEzNfbWD2bmMWYGdmJjI2/gM+Gip2qDA++Y+KwN/eXY2UxVEYM2YMntDpMXhuMnaunGaI44n4nZlY/UIfXCrUw83TE/7hC7Ft9Yf4cPVHGN9WiXbx0lWaUgBaGFK19X+ZJOBMpKcsJCsAxe385rjhztrlKtKSUonZO2JQYBAGe7bGyYNb8EX2JUpA8+wlhcjMyEevYcEI9OmGC0d34btzFagqv46yIoUBM+WKKz+hqPIKbt7/CPy8O5BPWzz6WC9Gp9rpr4B1FA953G/0q6qqAv91HzIOE5/oZZXZc4Jmbe8n1gzs+WQFUrZ+hawjp1Cs647gMWTv0OJ+DPDzoVwp55590ZtE/Nxt64nZ6zA4aDSCh3mjrOAg9p+6RvndQEnRCWSdb4XA4GD0da1A1s6DuMaZSCcRkAhIBH6DCBgFwHrVXWWqWoZqi5lq43FGanrzTC351+an5qvG0z6zn/psK1/zclh41p/OUSR3Cps4YTip2cl1ehwvTPPEtJUse5Oza4XOyh12LfsTfJcRYyKJfdTQMXj+2WCRprSZvSEGMTfD3YAX/o5F/Xdi54Fv8ecXVmMXqxWsuEf8H0MnfmtdBuH3nsASjlpuJbLBu+JcDk6Qfn/Q78fAuz3J1h4k2V9dgV3f/gB9D1Kbk+s27GkM6M46cmc8mHkSV8uJqLkY3lTxaEryfBfXdkBOU/RwbWMSTX/xLM29O6FjW0Pt9Hn4n9Xbq+fjHTzxh3BftBS51vxp2qoXwv/QDt/SgOroiaPIOEl/uwjHBwdjdIAXOnV/BJ12Z+LGg33g0akZzpf6YLD3Q/BybQV9cTmVPgt2xnI7ICgkAF1JU9C1aQ8c3JiPn8iuwoM1B9JJBCQCEoHfGAINYvh8wN6tm/TX5Baa3GxCPJWuTYi7ahm7ltEyqObPFoAWNMifrzdv3jSlp41PQSaOGbslPzVPvpIuo4nhn9UBhwlR0wddxweERM0K+UvXiXsa3JWrpeotMXwPzM3cjpHbt+HLHWnYRmr4opxMJPLfsnR8kvE3dKyObbirxPa/DEKMQVPf1tMf4dPGY9PKT4SkbB79d91rUjCPY/5cVaWU10Fn5ITo7OEKFCrz+xza3L56INKcWsUNcyJmzyIl2QfwVR24cBSdc2dSSBzFGeKsXV2Js+o64+nRo1Fl3xSFBzYh46ea4wgt6Yqrl3C1aXv0GxJMf6RV0BfjWOaX2JOzB9889CCGdGWdBLkqHuU0Q9t2LZC5dR32KBp9Qaq1GIJQLDs33G9g7hUike28RWL5IxGQCEgE7lEEGqTSv6/JfWhm1wzN7qM/utrb2aNZU+VeXPm+jn92Te1EXLv77JRBAwHNAwh+Ztriak6L89b+qXlb8LMjC/tmzYgO5cMDCabdIOfojG6GhCsXvIv9eWfw3fq/Y9YnPAQgRyK/Pm89IkZMwfur8zF2wYfYRlLq/7zqr4TjCC6RdKl118uI1eqP4jMDs3cLjyeV/ruIfmEi/LQRNfflFQrz1njV8dZ0bFf0E6vzDczT2p2BSYpg+mmqsFr1UUw1VD8Y7lq2gwvdHss5afDQoX2nTujYngYqvLShFncm43N8nrzfODnSTNeG5v+HCZqVJvnz4OUStn2+C5c6DsaEiKmYPn0yPKmaxlyMN7VkKoMlAhIBicBvAAFTLlDHCl+7dg052Tm07Iw4GPNPkrBZer6vKY0fVH7KUrfBMaO97z4KYz9DfPUqmDBpCzre3xFd3LqAtQfMnPNP5ePMmTOwN6jAOZ5IrxK1diX6TEM4TX7O7Z3h/qC7cVBhLblVf7seeIGY9663dwGnNyBy/IYaUXVObXGu6DSK6O/ZkXkYP6IzDm+j+ML1gQvPA1SwZMouB38aOQj+L3+Ah2lSOpMmvk8nvo8F6I+zmxKN0weoFryVZJrf2qRwNaquUw90oPy+TE1H62Af2F/8HmlHy+Dk2Q0tzZi6moavTYUKPwffF/RFn7bXsOvLo+RrWFbI6Sov42xxBTzaaGX89nh8sCs+2bMLKelAgM+DcKi6hmMZ25BRSGl4gp4Yd17GFhyp7I4g3+4mUwLO7g8CJ7Ow/isnjKC0uqblOEkSPif1NEwpMIVrl37C1XZVuEL3jq1aw6FZBU59uwM5xOS7lVG7tGzDyEmlkwhIBCQCv0kEGsTwv9j0BbZv247HfR8XDJwZLDNkwZQtwGgtTDB3ksBLSkqQtj0Nq1atglMrJ1TSOvcVy1fQPHEXtGrVCjdv3bRK2zw7I7M3BPAgpORqCb47+B3efudttG3DJl/1dAam22Pc3/FxxxSs33IQZGeHHoGj8FDuh4hJzEHbh9yg6zAEH8e/ill/eRs5RZn45BNDPm5PYPHS/0YXfuz1BMJ9Pkcic3hyJ0uaI/ZvL+OryCU4TUx5A9EiCz54OuWADfpzDudCj+4iLv9o+X9z1ZJP62mMSWp69e027YiRYwdh4/oMrP8P0Sfn4OqDp3y70gDknLCw16r0WXZm4bgZWbz7uBxDZuo6HKTntq4kuxcoZm/NSOPhRKr77Z+sR/DkcXDV8Pw2XsEYDV6HvwvrcgwDHrsO6Nu3G74/QoRIfj+bW4ACmsnXE8PXzue36h6A4JIqpGbuwSdH93BkcnboOXg0fHmKgAYL97tSzplb8L/XgzCsrwtSD6ZiFRfQzglOVOeTuzbCPdDNggZCBYRpSicRkAhIBH5bCDQhBmkQh+te8YSEBLi4uMDP3w/p6elwdHQUEnwDSAlGXk4GYu8uehd/mPQHOHdwxtUrV7H+f9fjueeeg4ODgzKfX/fiGWPywKGiogI+/X2wcuVKTHpuEjrf39kYXr+bC/gwdCRWEhP2mfYPfPgCDXYq87Bg5HhsIN7tNm0lksniXnX60mLoaY2bnYOO8DFMJKuBfBWb91RqNs+ppH37i4gVOqBDmzslnlbRnDhZ39MUiK5Z9Xy+tliW7itIk1MzTRWOfLUFP7btiwBv3qPAkqugnfbKiEXbo5VYY2cpjjU/2mmP860iE0FKa7O09I71pHHg3fx4QKDX3zTcW6Mt/SUCEgGJwG8PgQaJPKrE/sUXX+Dy5ctkD3cfLhddrrMUbg4z0wseFYyCggKcOnVKTA0MHDAQ+/ftr1bPmyeq5ZmN/tzd3XHs2DEUnC4gBkBM19ywrxYapsEd4D2EpEZampe58k/wWakNbYtpT/XSekDn2Ib+TLxMH0izoTPZwc8ObdrwUrc76ZqiGRnuaYTxOmXWjLCrmeYmrp8toB0DvBBglUoztGxZM6XV6CYBxOh1WtnfJND0gWw0BK8XvpzO5vDANK18kghIBCQCvxEEGsTwGRtmqJ0f6AzvR7yxYsUKMd/bokULywxanUu3ACoze5bEd+7cCe/e3nDQOYAlfmbUPXv2pF1t7SzTtEBL9eK557M/noWzszPGPj0W3x36TgRxXrfjHo/+GP/j9Tn+8xnNP584hxInJzzSZxQinn8WfTpZkOJvJ7NfQ1p6V30fbqjG5NdQQVlGiYBEQCJw7yDQYIbPELC1fn5ePp588kn0H9Af+/btE37MWI3W8OqEgcpr+Vlzz8y5tKQUW7duRb9+/dChYwdcKb6C77//HgMGDEBLx5Zi0xZjGk5+q5o+Gwvy8kCW3pvcR/c0Q9Hcvjnud7kfB/YfwNWrV5X0lK4hUw6UTONox7jAP+Bv9CddM/QLCZcwSAQkAhIBicCvBIHbYvhcRzaKY8O6xP9JxPVr1+Hj4wP9dbJoMzB1wZCFSb51RHjgMG/ePNysuomS4hIxRfD81OfFoOF66fWaCbWDBg6lvHgQwKf4sYZg55c70cy+Gby9vYXBH2sQeJXA7Ur4NQsifSQCEgGJgERAIvDrQOD2GL5Beq+orECb1m3Qo0cPZNNJZmU3eEd2chzOf/cpDFlI4vQsNAAGW0Fm9qwNYIZcRRu5cJj6x5v73KR/LLmL+XdVM8C0yQkGbigDx+P4bm5uCBwRiL179wq6nFakFwlEMvkjEZAISAQkAhKB3xwCt8fwVQZMTPe+Zvdh06ZNcHd3x5AhQ8QcP6vrmZkLFTzvyqeK/cykDWmZGV8rvYY33ngDr776Klzud8HxE8fBS/9e/OOLqCyvFDSMb4bTGZi8mDZQGT5pByppR7ntW7fj7NmzsNfsHCeke87QENdIS95IBCQCEgGJwM+OgL6YVjHRclsdrfCyecr4z14yKxnSseXFxaWotNORcbUjH2/yq3Qke9+mIyZadbMKt6puiU1yQsaGoJVTK2zbsk0w7Y3/uxEbNmxA6qZUbE7dLPxSU+n+i83i74sNX4iwwh8LaTvaL5GWloa0bWk49+M5fPP1N9iRtkMwcabHzHxn2k5k0PGoPD+f/X02Tp44ibMFZ1FaWophQ4fBf4g/2HiQmbvK9FmL0KSpRtKvQ5VzVs/EiBGh2FRA0xMat3tJOPmPwIzVWRpfOjg3a7XwX3ngR6ybMQIjZiZTg2ZXifwd67Al17AFrz4LMyj9yqxik/SN/VBakIHkHfnWyVZexI7kLShs6G50N7IwPTDwDtaj1DaO5jW74+Uxz/AOPP9MbeMOlLyBJEuxmr+V8JW4qKVQmYv59I2I7y9f+/1VQvn+ZiDrUuN8R9rvJDc5FoHUplfnNOSIJdPvvGDLfCo/lVOz87a2ir+W+/yNrxMm05FlgESLV8PqoMcO6kNHjxtHp3CGIGZDbg0ypnmY9wM1oqNgi2kZa8aouw+3AdP3dhGblhAvGDka4yZOxEQq80jiC/NX7zb073WnfTfEbDjDN0jawhCOmCtL0Wy5z1L2mjVrsPDthXSUaya+z/oeP/zwA44dPYa8vDwhfV+4eAFFV4pQcq0E1/XXYdfMDs/+4VlhqLdkyRKhgvfu440fjv2As4Vncf7CebH8r+hqkUh3qfgSiq8W48qVKyi+UgyeUli0aJFYu+9ElvM8j8/lEoyepgiEhE9z/EYNQx2Qd+vTj2KV4EC2tisqwJ7UQpE6b9s+aFl23r495O8Ejwdaw5HOlUEZjQbpUprzMabHJeAMb6NrcBasEtSgRrrSRzJ5DuKPaDaYN6Oc8++XERe/Bj9q+1OzOHV7bOiyu9qo29WKo2UKd6o8lnNrbN873zYau8S3Q88R7r1o58bCr3Bc8zHp8w8gXZAtwZ6DZzQZXKRNoOj7cxkMN1qxeftYmX4nZZeV7abLqT+przP/zi//dLa+JO7K+GVF2nqY4tWgApdmY5V4hwGIjJ6NKf7uZmSs5HGp1CqDvfoT77dZxyW8ZrmZP1aUXtZ4VWLL/IlYmpoNJw9fTKXyRkeGwcupBOmJC/D8sgxN3F/HbcM1E8TktU7Mp5NHUz6qjMLef/99hIaEYuv2rULyv1F+QwwGWL3Pm+HcuHFDXHkXPTby8/LyEtL5yn+txH+/8t/418p/weGWA27ob4i4nNfN62SKT7Q5DTtm5FdLruK5iOfw9NNPiw17unbtCocWDmIgIKYTBJunw3gMaUTCOvw49uhDK8wTkX7wGCqDXIUKpzL/O6SpaamT+v7iNPg5s0cxvvsqj/h9CLxp/1znuG0YZYhXadga2J5UQSbO8MwGhbz0sHEdMUsX6hftrdNt5tiesiyjMwkamHNzWo65fXsDE9clmQ6j6oKjSuqOl0fN6M5f1bZyZ9rGnS9/fXJ4+LFAIDUF354oxqD+bUTSY3urO9LMLQdQGtpd2Sm5MAe7KYbHCB+0EcNperit78j0O+nY82EimEdng9R/0Kgz+87Vz0r97O+Nd2mKl3hZ9f0haciV0nhNfwGhfsr7NiVhJY8W9mJHUN6wrJL6SxVfTusZsQTbI0ypNMpTZT4y0omSSzhWL48w7tY9io7qnj8yio4o34nCqEHinI9Gye9nIKLFrc7ZCSM4lvD5jx1dVeM7lqIFc6cXs3zlcrw+93WxRp/PQ7fkeKDA0vjAgQPxyiuvkOndTezcvRPvvPOOiXGfpbRsI8B02zu3R4/uPdD0vqZwbOkodv7jMompBhoh8EoCPkDHWhks0YbOA/5eQHbaIVyMGS5eav5BZnAeiI4djKVxidjzQyH8/Iizlp7AHhY8Qh6l41lLsWl+JDa2moy/jyrGM1EJgvz66NH4YepSxI1Vzrb/MXM9Fi5NQBqNEyglgqNj8cdRnoaGXIoDyf/Ce/GpYg95ER45E5Gh/UWjL9y9DFFLLuDN1fPgadjcp4D8/rTiBt7+1/M4+nYkEqg8SHoNoV8NJL8YdNeMNwq2LEFUQrYo16zRoZi6dDWGXlqHV5bsh2tP2tc/kwrV9XH0vfE9Wj71JuZNoHN4DS5300K8+tENvPmvZ/HJtPnw/vtyhHa/RXWehT0PjMaAko2ITxWVgldAJGJmh8LF0Mryd6/DwiUJyGPFg5MPwsd2xLZtNxAbH2Osh5oPgWobR02ZRJobuXh9Qv3KY8yrNAvzI18HrNVV4Hw77+Rl2hw5FwufXwQ81gvZKfxeXRC94l8Y5a55MVQg3i35h51rMP+1JKQzTi5eiJwZg9D+1M4EJoRzqwFofSRJtB3fyGWYF9rJanupzE1GZFQKQpbGI1Q0FsJ14Sx8lN8biz+Igju/m+IsxE55HV1epnb1GJD8/lKsSc0k/RY5Jy+Ev/QiIoZ35yc6AKkA695bhIQ0pf3AxRexc6MxvLvSeRfuXmnajjxCsIrycbXS0zj36kdIpGDvwdOIEgy/GPu2EW2fcMx+cA8WJX2Jo6UT0J/aeWHuIVGm8Y95UEGOCaxsf0dA7o7V+GBVIrL5eyDHktqM2dEI6m4n2pf2O1kwS4ze4dWVpcVK5G75NxYtT1LaK2nvfENm4MXpQXA2q4ueMB5t9p2Hi0MjTmPnx8sQl5givmMnwuqlBbMw3N3w0daCpSiw5scatm3yd2DxgjikG+roETAVc2ZOgNiJ2mY9DN9Yh+n4IMrP2Pdw/5WEMMTPG6XJXYmrxcu8X1EiW/9OlLYYr2CxYgrCUychPi7UyEjVb940j5cUsnlfIn5JBlJJ2mYn6jib6kjv4mLGSjz/dgHeWvM6PFvW/b0xHZuOND1iA/T29qbR7LojJDIYRd87oYQ0pC6mn7Bp3LvsqUEqfWbSzODZqVI0M2rV8fK6ixcvoqWDombhdfHNmzUXaZ4e+zTmzJ2Dh3vxaJr2dG8uTlMRO+GxdoDpijl4CuM0fBIfpx8zZgz8/f3Rrl07wdRb6FqA/9jd3/F+6Oxp+1VK7/qAK7q6dUXz5s3FEj9Oe/j7w8jNzYVjC8OHJlLV9uOIR/x9KNJu5F7kuNSQt1BjI3XiUP/h8CWftB05HIDSvGySC4Ahvg+K58vHC5H3bR5udRmIyGAaNZDr4BuCod1Z16+cH5OemIB97UIQHT2V9qsvROrSaHycwxN+PE8ZgRhi9td8QqgzjUWI1zWkElNUVUjnj1LHV3KW5PNqdzmf/Aq/xdVKO7gNGkHDEnJeAzE+ZCDamnVQdp084cv8gzqxgLBJ8O6ow/n8/SgsyRPM3sfHAy5desGVplzSEz5FfiXHZVeITUvTUNLSHZ0dy3C8pBCXyxT1Z+lZSpu0FPG7HRAeHY0wHxcaLMXjlRUHRMqLB1Zi+gJi9oRcNNUprHcBEhOJ8RXmU40tu9pwNE1Vv/KYpHXsjA4263q774RzK6O65iGNmD28fODi5IpObWv2FPw1ZKYk4VDvcMTGRsKrMBvxMeFYZ7ABETinErO/4AUvDye0amNvs704tu2Aa/TeUr7mg4/IlR7FRhplluSl4BvD/Hjht6nILCmBi2sb7H4/igZsR+E3dTbmEiP3bZmNxLgomtfmtlmAJc9OFszeNywac2OnUvnSERc1DpsMtGq0o85uNRikKIf606YXhlBbLPzqO/rCyNHBTqzx9Ro8nIx/B5NHHr45Wixi52ayfO+D/oZBEmNl/TsimjsWIooG5tntgzF77lzMDg8mculYFPU2tema34lLr2fw7tv/wIPNaVyT+xmiFiUBvcMQ+8ZcTA12I4luESa+nyHKov3RWfnOeUowhZj9Q2GRiA4PoAIRVtPfQq5o8LVjqc2D7y1h63QmGSFRzOw9ED6b6jg1AHlpCZj87DLBWGurx+Wz1FftPapgb8iQv7vC45fNvsuaeJn3K7X1Xbq2vRASFkC9Dm1F3i8YIUO7mkjqfPiF9b4rj5g9SK1OfQt1XlzH2H8rfcvZw/uM/WFt9TVUsW4XEvoCmQVkJyAkdAaWrabjvQ/koLBYD+/Ql/HevGkmglTdiP6yscxYQd0KI5i9QaXPEj2ry3l7XXaqap/n1Y0b3VDcGxU38MILLxBwIcg7nofVa1bjqZFP4cKFCyKdkL4pHp+Ux2p71ZVXlmPG9Bl4NfZVrE9ZjylTpgg7gOvXr4u5fmbovr6+2LhhozDMK6fT6Fq0bIEO7Tvgetl1MY3g6OSIrdu2isNzYl6NAc/z18W59+UOJxOZNMHo53gaXxJX9wj3gaOdKwYRx09P301d4HBc/W4PxfPCwB4s5ZTCnsch1+1J2+iKMaMGUQeajcHjpiDIkzp4/XmFUfvOxifzgkSDf9LLHiOnx6P0Kg0Xiw9jRSrJVj40AIgbJST64X4D0W5mCBJSViFryiDQGEg4reJReZEtaAtcHbyDnsWwNbQvQr8xmBDqqUTW/Lp4B+HZpzYTMy/DmP8KFUfK5giJBAiem4iXWWtBrmDLMWxclI7Nh4qF9FWasxX0zSFg8hOkUj0v4qg/5eLGB0s/jgNXE1Tv8tCJSNl7EMVRPbF9BXWe1Fkv/Zi0Ehzu9zhc5j+LpekiocUfmzhaTFHtabs8/an8WueMMTN8kWKtrsWZt/lOlLx4gOZE732V4b1rS6Dei0GcL717kq4YpuH9OmPGuDlI+GArxr73BGrUqzgD4XG22stijKfRX0LqN7g4rT/sDh8Ug1POL+NAHiaQZixzA01U0XQUT6dmnGe53gf+Q4egP4kufgN6Yt2aA/BsZ4fC3Z+Cm2ZA7FrEDHdmEvQaPTFj9CwsXbEdgdRe7Sy0IxHR6k8bDBzhgaTEPThdGYHOPyiahcA+rtC5+Ihpte27jlD7e4jm77meQxWtBDFNBSsr3xHJjWUVzUlBEoJ33osSGjpSx6FT+RHMSrqMy3od+lv4TpwN0woFZ06KEvf2C8DwQQTMID9067oaJ6lvoa9UvBtjlSx85zkGBYj2e/JAPqITL4M/88KDtWPJ71/ran2mzTUAACAASURBVGKrR/LM0RTFAwtTlgstCL0R9O/WHBPnpGBTVgSevHhSkLBWD6Urob5Kk5Hy3TH71bra+xXU8p3kTCFt4H8BW5LS0C9oEmmczGtoKQ8xDKSC+GLZxnkKg1X7lq+ob+E2rekPL9bnvWmrZ/GephXfWItm//4Ay5PSafC2lHRRinPyCMDLc2bCT1GjWEx9N3oqXLohJSOVOa97539NmzQVzJYZNTN5YTBH8+uqUwcBQU8Eif3xI6Mi0aFDB/Tp20ekV+PxwIEN+NT46vV3j/wOaz9eiz//+c/4atdXYsqAw/iP1+6n707Hrt27sPX/tuLD5R8iKYnUcCfycPr0aWz43w24dOkSfiz4Ee/8/R18+eWXana1Xu26eAlJ/tvDp1F8bJ/oKIcJdSIN/IeTtECmRd/lF+IISfPw8kcP8/ZLMfQ0AGFXTss6tM7rEQ/jB2XXqRd1bMDew7Ta4OwxMTIPmzhU06k44snwMIqRh+/y1A9AS838Xq8wBlrSaM1VlIvuEtXF4mcPBNDpc6pzHRIiypXyWbrw2vdpIl29MObx6jhqXPHNuTwMNyMGOrTrwKHUmVQW4yRB5BT8tMLsRSIdhoYwhtfFU20/1nC0ls5meSwkslXXxnknSqZumvduoRgCjbBxmnff5ncYRgwbBqMl83rVXjbg0dE0Oi3Zjh+KK3FiLw3ZqLNixVP2l0dQWkmDOmJOHmN9aTqKdpIcSpIoDXJjwkcLK/nY+L3oNiSQbFN0uJx/ShQ5LW46wsPDERoajvDnX1cGEEcLDUZVNduRpXpq/Tx8FEn+yJlifL9HDClB/J44DklYVM6S1G9RcPE4viWvgBFexu+GaVj7jjjMPehlvB/9KHauXIL5sTMRSpb/s5JYF0cnQYpf69+Jq/dgMUhIobqKVTkzF+JsBx+MCvLWfJeCiEKpxnduwGFAzW+F864bltX0lTtzbCtRyl70BmIi6F2EhiKU3sv0OYwh8NW+PNS3HiKh1R/reHGS2tsi9V001ctfvHl/WJ2llTxc3FG9e7mmb6lOKO4at75E0s4ZQdPmIXnbRqxNXIE3SOsWTNrLkrw0LJgcjQOK8smsFHfvo+kgro7lZEbL8+UGoV5I+Czpqwyaw3TNdUYJX5X0mRH37tNb7MZXWFiIzH2ZQjPA0wE8DcDxWBXfzGA0w1b/7FJSUpDwUQLCJoQJA75T1PE0a94M5TfKcfrUafTr30/k7erqiojnIgSD59UCrMp3d3fHhx9+KHYD/Ozzz9C/X/861pKi2XWBH/WV6fv3YUf5fvLwxaMGdaLLI49Rh5CKPWlbUUYdpk90X4sdgaXMuMEP6KU9Ya5SSCstRGRluGpub6faETnSgMiis2cFp/j6LQY3yFPnjd8HOyE7dTsOFLhhA/F9p+AJCtO+UR+KZKQpolfUJ9HPG9dGXUlnI8pyu++k5nu3UkXr4zQLCWovm/ug4XAiVcqe9F1ovrsEHpPGYGzLG0hdtAs7/49UukQ12q+HoO0eFIOU3mOwc+tO7NmznaTqRPHnQ7YC4YbcfcPG45F29uLMC1r7Ckfe86KFR53bv3klHD0USX7/7h1ovY/b2FBh2MVydJ+R9AFm78X/pV6lgbATpnu7GpPXxFP7HQEHVs5ETBKL2k7w8R2IsdEjUb59EYSXkYqVG2c/JG5eiwNp27FtZwbSMtMQn01/LuH4NDHCTENkhQZ71/IubxdL5e07IWxSCNqRbFFO/+xpbTv7t3DrCDh726hHqI2CNyRIKU29v5OGZGUtTb3eWzGydpPmq3VvDPeuOTArzd2EWVEbEbbqAwwnSd6ZBh38N2h4KMZtWYjJi9JwkIxN+xu0QtaKdDf5V4vh9SgVS/XE34XqnRk9/xd+BhrM+JlxqwMAtqZnl5efJ7a87dqtK37I/gGXiy+L4285jA3rWMLnU+3U9fOCDs3j79ixQ6j/o1+KxjD/YQh+KhhPBD4h5vVZW5CwKgEOLR3EHD7P1ef8kEMS5UkUXS4S6n/n9s5i2R/P7dfP2cHTj6TQvCTEp+TBKcBPUScyEZp7JE0kzVvTHCF1KEP7dLFM2vDBq5bXxkg00jV33IE5dnxAzHGt30A9n9FVYg/N67LxXlca5tIsB7kLuGIkUYnCk6eNsdWba+qNjatqRWwtyoBRkygoGzGTZ9EvMGlsH2tRrfvbdcIjQlL7XLMuWY+ta7lOyjDHemJDiDUca01Y9wjW6tqY74QlHFuO0UhNO1wdhebceSoJ7WmDkmpf411dygbnRzCW2mra0jihkh/2SA+49+aBbzaWLiUlpVMwfMRA9iLWzQ/Ha8llGBURhbjlydj26RtC0s3c9T2atmsr8u3QdxRJ96GYMGECTRn54/IBGhDmFRrLVO8bNpD1oW8pMR5ppLUP9u9lJOHa24++h0JS+dO0g0sgHnY2Bik3FvDk74hXzmR/RS2WpirWbktG3LwYRIwKQs+uNTt2S99JPhm2hk5eiXZBExAT9x4Zl27EbF+aCizchiOWpLp6tk+HRsWyJ4aMIel+gvJOxni1wIHte1BAQNRWj3IBlqKFFLCV5uGQogQRj5Z+LOHF8erUFi0RtOBnLQ8LUU28aquvSWQytn59wSLEpeQYva9eYkCu0zHl5Cou02A4D/9c838mNg4cVEZT1OzsHawIYCL07vtROHEDyyWYPaVVr0yGpXQ2nuvg3AGl10iFQ47n79mxxN6xQ0ehHeDpAHZqWBkdHs+76rVs0VIwag7j+Xs1nJn4F5u/wPmL5znIZIDB44/KikpBl9X3ly5cEuv27XXKiLNz585iIKEv14u09flx8fYRDJjT+A311iRtA59hxMWE84MXm4sanObzgY4MCtliYP2K95C8O7e2AT+NyAdgCnV+JWlxmLEkGVm5WUhe8iJoepkUDGHoSXaHbn1700MJFry6BDsOZGAdhcdxT2l0Su9TkvQBlqy2vLmOMmjIw4pFq5F10ToT0nX3R5jaR7qEkYWxJbbDkkVNp/pVEqsaPoXlw2zMCpmJ1ZuSsWzms4jPrJlG66OmZ7/64qhNq9JU/azV1mpdG+WdqKWwfWU9TUnqHMSu3oGcrB2YHxEjJPCp058QDF+tg5FKHcpGo1M8FkSNSjgyeutCbdWlD1h5z84jOEAwdeqy4VhUSKsIYmhjkS3Iyc3BFjoZklm5R79e6Bk4TpniiYnAkuTdyMnZTe9xIhLTs6Enycdiy7iRg9jAQLy8rrpTFZma/OjQd7BaPrKF6amxsHDxRqDB5MZlCK+CqauzU2xpSvYidUcW8vOzsG7hDCxgi0Dq0Pm4D1X8tvSdOLS4QUawaZgVuxIZOTk4sGM9ksWyiX7opimeWhrz9lmlBli5ujcEyxq0aKpvegj5ZiJqfCy2kEFZFm3yNXk6GfFlH4X7Qy60RNlWPXRwfYiSFybhrZVkkJaxBQsjZhE1cjTy1H4nim6uln6lTm2RidtyNfOo0eYpueqnLSNTtV1fs3wdewr7FqTHIXbZOqxbFos53D6c+uEB6mcdPYMRRgPlkrSlZHs2k4z2kmk32XVYEhuOKDY+ooHycHWZlBnpu/WRvvz6OxMGz6yXmLxqtc/UWDLnw2uee+452oawjZD2y8rKhDqdJXg2rPP1p+UxkTOEWl5fpsewYcPEfvyt2rRCr169EDY+TKRVl9KJzXOILg8aOD8uAy8P5M13ngp+CkeOHOGJAXA+fEIeq/zFBjxUPs5PlFFjV1DnWjs/jGBieElkBevfy7S78XjUn6yhsmk53mMGFaRC1ZF7bTFyplkBl94IpkaTxOrA084Y/vFAqzJte0d+HWwokojyRfPI2C8es1IVmh60xG1+zChlCUv/CMwOOIRFaamIi+EIXggL8yXbhbNKZOrg+zzlg8SETKQmroF/aBBoewAT5zFkNFzIqj47LRHvdO6DWIOxlUkk8eCMUZMDkBSXBt9JT5qoMrWyubbOKg3h56BIpjrvCHy6tB1ejV6KxKUkedHyKB+XdGRy/2vFaWnWwNGvu0lZmER9ymM5S2t1bYx3Ylo+y/krvi60J8XRxDgy8OJnUtfO/QcmGJbUaTFRYtehbBSx++Mj4EQjrBKaL+8iOLMrBlPDTqMObnRAD4UUt73XFyP31dep3SwiC3jF28M3Em8960mNGViY+Abee2UOrRpZIAw4OYZvOG2LPcrdQIMbv6lj3nqt1LxrNo3Tpc9QqimXLxA9TNqqM3zpA0qhufcRA2nNqMZp37fGm5Qh/B0RM3wxEl/OiqdVBrNoRw1yTrR5Srg7EkhbsDOrkFSzLla/Exe/lxAbdgVxSUmYk8maKE7vg7n/iDQMjhQv9de8fb45lnEwn2LT+Ok864ClSl291sS2Tf8orIi1x9y4JCyKEayaInsgcvFbyh4hNuthB+cXF2L3oRikU1/A1XTyCYBvQRrSaTWUaCaGDW2UmcTa+pW6tUVr702ppXkeg1GzzdNafYaC+haFgbWkB0UnUL/35ohn5r+BQ1FzaGVMgjLQob40drH6jp0x7YNEtKNlqvG0TDUlkfotg/PwCcPsv04z6ffVsLv52oQYoSJq16OUiYmJ6Nixo1g+d/XKVeha6HDu3DmaI9yDP/7xj1jznzX4KOEjofZja33BnIlZM4M+dfIUDmUdwtNjnhbW8szQeTqA1fhfp3+Nn87/JFT2vG6emTuvtef0HIefeWChThUwPf7H0wepm1Px2l9eE0Z/BQUFwo+t8Z07OItd/vgkv9+P+z06d+pcj5o2XlS9nnaOJv15ffbYqSwtJqMq6md1JHlZ2HC6krYTLtZXwpEGVRaCySBPT6N02q/aUqComh6l1BvzIEz5cCzX98DKGTQXCiwmS2Bvk87YcvyavrSd5rK3sc8xCLMjaJWBIcKBZeGIIY3ywpREg4VxzZTmPg3B0ZyGrefa6nq778RW3tqwSn0xaOtu6OjdCv6lDbRyX1vZrCSz6K2ntkdNy2rbKxV7oVP5HOtePosZ3XFP3gOdhxx2Yg90S9nZ/E709I2Jj8QRbRwVFmiJhurXkPbZOFhSPS9Sg6E+xtHSXu+11EMpAw0AKG1tziZehsS32xbrkofNctZSX9O0lYRdse198nkvfWoHPIPE+/9b6o9Nad6dT2rf26DSMRPmf+xuVpKFPS2p4/HDa6+9hoE+AwmcSrHZDTNqZuzM1DP2ZQh1+++f+T1aONBYj+b/mTHzPvsjnhiBkcEjcSSHrIdpOuDRRx8V2+ayEaDYKY+zYpMBGjwIxk8Mn6/89+zEZ9G9R3ckrUsSAwPe/MepCekCDcMZkV6U9Jf5YaZaX2dHnakF7aGRjB01PGcb36ddLYycmi41XCO5GjeFBzYhedcepNBGOk4BcxvI7JmsjjQMx5GWmI4rpdGYONQN575OwaKUQlJORIppihqZW/FoCI5WSJl417Wut/tOTDK18WCnawNnG+/GUtLaymYpjTU/ZuS2sudBpo2mZ43sL+DPh53YqgnzSBsDXhpst6G/urqGtM/GwZLqaavB1FKP+pTBJl4GoG63LdYlD5vvpJb6mqalwaCzqfbWNJyeaCBVWzuqkeYu9GgQw1eZPNdHkbHplw6nqbxZiS9Sv4DfYD+4dnEVBnksqfNWt7yZTvt27cVyPF4uFxgQiMPZh3Hl6hVBg9fTO7V2Etb9vD/+yZMn0bdPX1wtvWqU6FXmLZQSxPjFs4Ghc955p/Lw9ddfo9fDvYQdAe+0ZywjDRK05RYB8scmAucPJhGzJ6ZMqtA3X/KzGbe2QM/xcQjPj0ViCqkODYtZXUgtNvev2p22aqNy58Ibs653rpSSskRAIiARaDgCDVLpr05cLXa3YybOc+gODg7IPpyNBz0eRNNmTcX+9yyJs+V9cx0xckcnYYzn5emFLVu3ID4+HkmfJAlJniVxlv45Lhve8Rw87y3PTP3adZqXYYZOf/ws5vNZwmfi7OjCkjtL+OqVd+Lj+BnfZKBnz55wppHbsWPHxGl648LGobPLL6PSVwr8a/sllT+t3dZZUhE2sCqVpGorJV2xtWmKBpJthGSNX9dGKJQkIRGQCEgEGg2BBkn4au4sMTPz5Z3tnFo5Yd+BfYLJqhbzzPzLy8vR5GYTcYBORVWFmJPn9fOswudT83j+nZl9yZUSwbSZYRcXFQvm3vH+jmJzH9VQjxk8W/czc1elfM6f79moT13+d+rUKfAhOmKpH00nsONpAJFGLby81gEBng+sQ7R6RGFGX4uGtR7UGjNq49e1MUsnaUkEJAISgdtF4LYYvpo5r5Pv16+fmG/nc+lZwmZm2+SGcjRt2fUysbc+G7SwBT0v22vdurXYIIcHBGxNzxqClo4thTFgdnY2Cs8V4pnxz5B1L0n5LNCzlK/8iKtg3uQnHIWLeX1i+ny6XucHOosd+HKP5uKxxx8T8XlgwP+kkwhIBCQCEgGJwG8RgQYxfGEwR8yTd8fr2asnjh47KnbN43l4dsyYmamrTkjXVbdQ+FOhYOhsvFdwugA/Ff4kGP/10uvweNBDqObP/ngWbt3c0KVLF/C9YOyKkK6SU64q71aZPvkKjQMZ8vGGOzzwGDZ8mLKJD8X5pY32TAsvnyQCEgGJgERAIvDzItAghs/GcLwkjpfinTlzBn7+fkK6p2XwijSuqQNL1cxs+dja7777Dp999pkw1uN5/z9F/0nEZHpiZz6KW1FRIdT/dCvm83lwISR7DU3jLTN9DcMX/qofXZnmls1bxLa6NukYCcqbhiFAe3rzMi06gczaEsGG0ZWpfh0I8LKmUuicbVv2/zrqYq2UvOxND0eqY4M6TWtkpb9E4GdEoEFtl5fJsTTPRnhrPl6D48ePE38mDsv79mmk6Sa36HAbYuZiPT2l8Rvqh8OHD+PE8RN45plnhCSekZEhGHLWoSwxb+/u4Y5jR46Bd8UbNGgQyvW0pxLRFQMHGlEY1fLM6Jm5k1Pn5lmToHX8zFqIJ0c+KU7TY6b/m3Y3sjD9qT9j4LufYZp340zOl+bvwFuz4uh4VRVZJwRHv44/jvK20DGWYt0MOvUvT4kbsngN3NZOwtJMHzoGuDfi6Pjc8MWfIsKkbHpsmT8ai85ORcryCcpSMH0WZtIJbYhciheRiKj4y3XaI6A0ZzVCaDebqXTc6ck5C5DmRTTfY5ql2L3un1iSQEf/iqLRscHhL+OlCD8K02NT7OhayqjWXXO1UcbCA8lY+FY8skVmdM562EuYNW24qJulMi7yP4TZhm0JPcIXY3mEdsdHTZ6/0G3BlrdoX/FDeOPTZAxqnGbV6DW5Ru/+6T+txrT4/6UTAnmjltpcJfJ3fIZjXUYhqDstyys9hj9OnAXXaDrfflT32hLLcInAXYlAgxg+S/ebNm3C8BHDEUjbZjZr2kyswb/P7j7wMjw+8Y4Pz7FvZg/75vZiYxceILRu05rOf4/GnDlzMHbMWAHIY489Jg7LuVJ8BaOfHo2BAwZi7969+M9//iPsAnjDHHWDHWsIqhoAHgyo9yIuDQp4457M/Zlif32W+KVjBLQH694GIvocvEbbePJZAiHR/42hna7g0/cWIXUpMePW1cfs1sjByQvhkwLRp6MdvrushiqbZSq/qh9v2BOtbCvsUm6y1SfvYeZAkSvszXczU9Nav5aTFukKB9MJdLz/24GVkViQREezeAUjemR35G7+iHaaW4Ds0oVIpKN9L9sso/V8LJWxsmATwmPiKZEXps4eidKMNbRDYhyOl7egvAYZiWnL6PDQCERG9sau+ARcMgXIGP8Xu6E2sIj2fXahgcjdyuy12JSW1e0Ap9KcjzE9LhFhi59Ukjt6Y26kF22pughZQxu6AZW2JPJeIvDzI9Aghh8QEID27dsL6Zm3s1WXy7EEzdI/G+zx0jqeq2fVPf+1bNlSrMfnE+88vTzh2MpRHKbDafTX9Hj0sUfh6elJ1G7iYc+HMWbsGHEynr2Dsh++CTQa6V6o9FmwN/fjBOxPfy2btsTUqVPFSoKqW1XiOF8O/s255t5YsX17o1W7OHsnMXs6KTByMaIMW6vOe781wun89t17cvGyn4vlvFoOwvhQPu9dj9O0ihIXusC5hel71l/MQvxfZ4H2/FEc7dFkbKzMpcnR6QvKDf3WrRs3RG/RGh15f/aHukBXmYtEYvbEsfDRe4ZT0IJ84RQ+Dkkp21AQ5QVe6WmpjAZqli9Wynhow0cifvSqRRjF5y8EPQqEUl57D6OYGL6xjpoyunkPRw/vSpTT9p+fWM7tF/PNp9P0aHNpLBxzd2kdbhcQHQkr7LSHXnUPngKP+Fn4YFMOlk/wvN0sZHqJwM+OgLF/qU/OvK3uk0FPClW6uRrdnI6QuIkZczxmtm3plKhhQ4eJaL/z+p2JOp7DWf3fqlUrcRoeq+pro2+en/mzSoOvPJgw0QCYR67xXImsTSvwDp0oRiyBnAtJsq9gOqur9blY+PyryG7vh7kLX0Z3HQUXZ2Hhq+8gu91TeD9uFNLnz8L2DsPQ7+oXSExjCqS+JVXxLKEqpjMrdq/EK0v2w5W2CM/MJM7mEYJVH0ShDanJFy+gAzCUTOn48qmYM3MC6IRGcpXI3fJvLFqehDxVJRwyAy9OD4Izvc3S/N3459IVSMtWEruQ1Pri7EgM4sQ3cvH6hPnw/vtyhAq1ZikOJP8L78WnGusXHDkTkaH9xS5rhRlUvr8fxpCxXUniTRXqbifaA/+lmFl0iA4ru90Q4BuMof5duGCKc2wv9pfOpP0ZeENTUWQ1zHhVpXUdGWsS5810hAcNAtm1c1JSZCcqzN4nJAy67UlIN5xNICI5dkYvujnVth3aiTMAuuLBOmyGZkd7b7Pr49UTOXzToRMxfDv08HKBi/9jml0N21B5qFyF+fip1HoZmYRVZ7GMlSjqNwPh7ZrBV3vYEr9Hg5bZYhlFJso54daU0YUZq+ldfYvHgrtib5LyPl18wjF3lh/2Lqa95MWcC0+3vImXRylY6wsyEP/BKqRy22Pn5ILgSa/gj6E8HVOKTbW0XwII63mqwScavQ2qfKUce9Av0B27U5QpEhefEMS+Oh2ebZTupi757mk1AK2PJCGNiuZLR/PO6ltovV1z2fUFWEfapYQ0w37nLr40TRSN4d0tzTEoddvzwGgMKNlI+6Qr9fei8ypiZofS95eM0VEJTBXro0fjh6lLEccMnva/DwsA4hI+Rf4z8+jkTJpuWhiN5fm9sHjpy7ByrpSgI38kAncDAg1i+GyEx+fN1+YEszZI3mLunSVucrwrHjt17l080I/xgJw60lfTiatWwlcDtH6UN2sTtOVQo1m7Zq1+EbMSaVtZ7rCCeyEv9Z9IInX1ocvKPGrYpN6YvigVr77ng+SYvlj96izRQU19UTlkpvRsHrLT6Y8O3Zj9xoso2rmKDu5YgONCVdwf5/P3o7AkD4XcZ/p4oEDnBqczyQiJYpWvB8JnT8L9RXuwKCEBk/ddRmIyDQZyP0PUoiQ6eyYMsUE9cX7v50hIoYNObrTGtj+2x1vTFyCT8ouMnY42xd/jn/EpmDP5PFZsjoM7ycTHSwrhKtSaldi9JIJODysx1u/I5/9ESnwMvi18Q6iXzx9WypdEh0YEhEeiB44hng4eiZveHD22xcC1/yjE9NeiRx3pIsqfvHyH9rHC7LXxqZZj38biIW1pi18dFi9cCjdDr/ng2MVYOs4DnjRQWXfIjOETax63eCEldqUh2ItY2KlCmds3JV3jSef+BJYufAidaS8At7cXo39bOjSG8o16z3BKjJriYgaS+PRBl7Fi2187K2VUo1u+WiojbeE5KAjuQnNfitysw9ibugq88aDXiL7KgMNSGS1nYOJ7/vAe0ZZS6LD34KnRaH9uIxJJ+o6aSHUj5hcZ3RP7P0qg6ZZodPXaiNAO2Xh+8hxi2R4Ii45Fz2bF2JEcT4fizEL7XimIIP5WW/vV52wVB+gEjPAxaibUcqTScdLBkXPhY/89FtCAOXrcVWqDMXDXH6hTvpn03dAcC7w8TqOV00+227VdAZY8O1kc/esbRkzeoxSfxyUgLiod11dsxCgLnJjrlplOuz7y9BJNM5bvSaJ3Ho9XWnVF4pSBiAzeRQOBbHTwDcHQ7qziYWcH76F0Ol1aCtJySskOBijKz0NJnoPB9kOJJX8lAncrAg1i+MzIWXUvHDNxlbFqrxyohomIys+tJoaT7jhQtaHjdOwM8W3SV2Iqv1r66r161dBT6YpEHF4XV5mDj4jZs/TyCUnrDNRwP390mT8SixI/Qs749+AZ9FfMzhhPp9YtQGi2Ex2nyertZZggjM5oPwLhvLB4dZyyD/2gPnC8QgZgKSuQFbGcaCqnXwXPVee79UieOZpSedCBMssNB8r4oX+35pg4JwWbsiLw5MWTgmpvvwAMH+QODPJDt66rcZI2P9Lrr0JMN7d8GP60csLZzg/eXd2wPpe2NxapND/FmVhBzJ7r9zHVj+Xq4X4D0W4mGdWlrELWFFIv2yupQt5Yi6hByl7TD5XnY1YSS76AqyIwG4jSWep0VCodHkiKirn4a5CrJjPrtzrn7vBWSMO7vyJ5cuw27t4GiVvFUUvDDq7e6kjDFf3rrE12hKcaubtKX0uX7i8eQOzEOcqRtLGGbX+tlNEspdljLWW8uA9RZOyourbtWhlu61BGNZHmqr6r4LlraSqFAfXBpd3hxAR9sWzVPHSnBhzsUY7RZLR4uURP2peLaO/igZC5H5C2R+kGHqPwkSTZlosz5tWuwXr7dStT3k1fT8MLpFyry6G2aT8k0hGt4XGp2HzoBUxpV3u+ipmCD5Z+HAdPbpilBzCD62qlXRfu/lQw+4DYtYgZrpTF73FPzCDDzqUrtiOQ2re5q5HHKDIQDp2IlL0HaWplGsbQMzP8weOmIEgUQqHg3KsfDTJT8OO5YsDbFROWb8MEc+LyWSJwlyKgftX1L57K3LUpVWarMlXzOPQsXUk3LwAAD9VJREFUJGytv3p/00BITcuPHMbO3M+wGkAJtPCr0tQGWfLThpvd688cEfPTyFyK8eF0VNy1a2SHQEpMYupk7YUyoaSwQ9C8fyAnlKSLQpKUfWfjjdBqC95yVkP7jkT1kck6+AQHkwp7N67Q9rLtxfGZHgjoq8510/I2tvQidhMTEU4dyzVcq84UX+3Lw7Sxg8k/DSlx0+mPmKtXAIJ+Pwaj/DyJaV/EYC9KnZ2IiSNZsvNCyFNjMPJJf7jwm1aOFuAMSHo7JtT4YROHaiRxOlI0PAwJdDTed3mlxDK4MB7w7aPt0EXyGmZ/+ZsWC2bvM3UxqT/rzIEVYnfJb3HOJvyRju/lVxy+cK3hSNo7VDjn4di8eTipovPx8Vukcl8ahXU9N5IFOXO4hjjlXQX0Vd+VIzp1IDot3dHJ8JVrdXI69yCayvHCjq0fY+HHPyD/eCbyRNuuzru29ns6+xBF9kAnk+P8DOUYoLZpaoaPB1CsVOw9eBpR02rPV8yeuzwMNxUKx64223VO/ilR6DT6JrJXteRPlb7Va+I94mihmFoyN1OtkQd9Be0Yr+v2YnCvpyO12ZXTKWkmE1O6FuIY5uNneGhdt0Et05FOInA3INAwhs/MUyudqwxZZdBqzdhf9eN79VmbVo2rZeIqc1bjcxxzP22Yeq+9au8tpVXztXKtNEgvbLk9yb+r2CKYTHjg2Ja4fkULPKB2RsWXcZ4EZXYltNyQ+0yTbqBVa9GBiAj8U3HDeGvpRjEVojPQJ4WgHfU55fTPnk7FY/8Wbh0BZ28kbl6LA2nbsW1nBtIy0xCfTX9kdPZpYgQi3tsMv6xdSEvbjf2705FCIndKwj8xe9XHCOIOzeiUnOzNWoCdoWd0pJUWRqflFEZP05uS00fJIwDRv1Jmn7tpIVlgp1EdPDB7xWIEkY3CnXClFwtwrkSH7u7OylHJju54dsp4mmNPwNGj5+jgevc7kW0NmqW56tQRjwt90W9IJELbncIisucwcXVpv5qmYpLW7IHtMuucr0laZ5vtWrUg8Q0bj0fa2SvfKhkMO9IfWniIAa1mrGtCuf4Pdaxs/QnLFBKBO46Aynrrl5HKTFVGyqn5np02TA3X+pnfmz+b01DD1atKU2Rm+FHzboifNo3m3tGjD3X95Fx6IzQ0FBMmTKC/UHhcy8H2PUdRIphgsZi3zySZOzjEl8T/FPxp4Q6j7bh9C0qfugX5RrqVyNzGTKUDWls9o54j98SQMaEIpfw43zFeLXBg+x4UkMYgf8sShE5eiXZBExAT9x62bduI2b5sYLYNWTk7MJO0Dd/YPY5pL8/D8uRtSJxLc440w3jmJ5ZUqp1jxwfIhJCMkjbsq/akku9JIW0G1adrJ3VEowm2cesx5nW8sXgSpfz1ufxN8wWzdyIjt8Rty+8YsydxHlv/OhlR0/+KHM3ruHiGGD25Dp3a/mzgnT6wS+QVuYzm+t+bh5enheLRzs1N8q+t/bp5DaD4eTh+RlMZMXmUJzREKrGL+9PEFElvLzfUJV81nXrVF9hu1w5kCMyuQ99R1d9qqD8uH9iOA+ZqC5VobVfDIFdrpS+SFBfiAt14eajz+rURkuESgbsHgYYxfJXpqkyY68P3/Kd1arg5Q1bjqv7qs5rWnL5KR43H4do4ajr1yvG0Tk2n9avtXljk0rx8Gh3runAdDuTkYMvKWESTBJR92R4diB9mrX4dPM3vNTUWL0fNQ2yAEv/93Sznqy4d02csQ0ZOFpKXvEgbuBA7DZls5Wx5UqlPZwadiajxsdhyIAdZO9ZhMq11T88+CveHXGhp4w2yFUjDrNiVRDMHB3asR3I6qxj6oYcbnU9AhgQJ0TFI3p1FRmG7kfz5dlGQdu3MGLjzAEzxoaEA1W/GkmRk5SrloyXVNA0RVq8z6kluw841H2DOO2tMGJnIuBF/DH2wdYo3chBL+0K8vC7HehzzkOIMzF3KleZh0SV6R0uwkP6WLFmI+QtXIlfLy0zSliKZ5vhHhM63EcckAT3o8PikALrm0X4US7Cb3l9G8hJE0fw2z7mP8LJkUW5OQ3m+lrNO7IGxLof01w1wDo6KfcaubduRm5+PjE3LMI7sRNjt/+6shqL19qszrHq4XFYTpMToSKyjNnhgy0pMX8D1C8BIql9d8lWU6dVF0NH+HbbatXvgONrVAEiJicCS5N3IydmNZWRPkpieDb2Lu2bKqpqmeR4covpxO9N1vF8ZEK94j76lXOMgvvRygTDQ60w77nG7r38b4Jykkwj8Mgg0TD+lMmAus3qvXlU/8/pwuOq0jJ791Gc13JyWytzN42nTqmnUK4ep9+pV9eNrrc4Ow2NW4HrzN7E0NQExLJiTc/IKwdtvRsCxYBNZ8GeTBjiclrp5irDhM9/E7rRopC5YiKCNbwo/keYCWcpHK52pR0A03jJusKJ0usaIdNOmfxRWxNpjblwSFsXQ6EA4D0QufgvCFsvvJcSGXUFcUhLmZLI0To6s8uf+IxIujjq888ZUvDInAfELZilh1G0Fz16GULZUptkEVjooTodRbySifNE8Mk6Kxyzuk8l5BERifswog9U7l08YFYgw5ceSH3WWhTTyKXSAsPfSxG602/bK3KptepVivvZCaa1DAyOZ0tOKLQOBSOOsVGExbwwkJjxuZvWT+V15GQ20So7jat2zg4vfTCyMBGJo4Lgg2gg67bw3W1naaZ6J1WeFPZVWqjsQWH4vmhcOOwdFKrWnPTLcA6cgZPtRpKQsRZTSNBEcHo7z6xOR+e0RlEa4GXN2stJ+dR7egtEmpXyD//IOMpm6cnK5hgS1DdIqgTfema0sW6tDvmIsct2YPeDY33a7hicWJr6B916ZQ6sMFoiVA5zaN/wNvGrYH0KlRkp+4WrkQb7CjwYxPDS2I81eMKn4kni67LQzGbR2F0akh7dso1AP9PHgKZ9SNKQNUELpJAK/DAK0NK7ejpbV3aJlecqfes9X9U8N46u5nxpfDVOvlRp6ahxrfmq4elVpqFdr6Qzh9a1wRUnRrQsXLtwqKimrR9KSW2unB94KnL72VgmlKikqulVSVlGP9GW3ijjPopJbFlOVlSjhFstUQemKxF9dSsz14/j1K189qmKMWo1JXcplTFaPm73vjr0V8G5mPVI0PGrZsbW3AgPfvXWhISTo/XGbukDvt26u4tbaSYG3Qv51qG7R6xiL2yW/+5pNs/pd2Wq/2WujCYNJtzKLlAyz/2c6PUffyqYXrLQry/Wznq+tgtferpnuBW7LFj8aW7Qth5WVld2qUGmVHbo1PTDw1iTNO7itNmA5S+krEbhjCDRIwm/ahJbksdSsOu09+5k/m/tpw9V7wyo/lWSdaajptXnUhZYxo9pv7OhQeOcG2HAJGey6IonxoTL1czq0cTZTw2sJiHPlrRWK1nvXIz+uX31Lpy1Kfe4FGnlfYM06RwwcGghvFxt1rA9hiltZsAWvfVGC6H/1qmfKhkQvxSevJpCCZzFUu/h6UaH350x/dXGl+RnYevAYMgqN+/PUJVmd4thql3Vpv55jp8MrIRorNtDuc7x4X7gysYrFVruyla/1gtferplu3VC1nos2REf7NKguP3UtTcbQklmjYepttgGVsLxKBH4mBBrE8H+msv3qsxEqwl99LRqzAna0TS2pzsmQKilhKeDp26gM3841CNu3BzVmgW3QckQEGUVG2IjRWEF5aasQn0RTJuQ8VJ10YxG3QadO7ZdsXWJiA2id/WvIGJOMVoYdH8yXwdnI5tcRRIchzaVdBX1mrzDsj8HF/vnawK8DJFnKux2BJqw7uNsLKcsnEZAI3N0I8PHIOpKu710Jgo4ALtaT5qwx9Qd39zuVpbv3EJAM/957p7JGEgGJgERAIiARqIFAw5bl1SAjPSQCEgGJgERAIiARuJsRkAz/bn47smwSAYmAREAiIBFoJAQkw28kICUZiYBEQCIgEZAI3M0ISIZ/N78dWTaJgERAIiARkAg0EgL3rlFtIwHEZM5lfIqdNwZi4pCuClX9OXy1eTuOXqBd6JxcEfhUEDxamS7+L0inIztzikxLYdcTf5gyBHT8Tg136qtE7Go6DOG+rjXCpIdEQCIgEZAISARuFwHJ8G0geLUgG/uzcnCioAhOPdWIFfh240YcreyJ0eMfxrmvN2H7up2YMD2A1iBrHe23atcNo58ZjJY3K3CTg+7TWWT2aqqKxjvSSyUprxIBiYBEQCIgERAISIZvqyFUVaF5u27odqUIP6nxKs4imwT3QeOHoBNtT9dpxFB8t2onfqIzTFqZiO68wXordCRPU9m/Cqe+TcOWzJOCYoee/hg1pBeaNrVDZc6XWHuiTJzE59DBE8FjfNHeNLFaCnmVCEgEJAISAYlAvRCQDN8GXK26esOXtPjnKo+gwOQYewc63taQsJkzXOksrdMXr6F7Sy3HJ2grc/FF6mWxGUnljUp0G/wEDR52EbO/hsDxEehy30/YvG4Ltt3fGd6CXAV6PjEBnq2uYtdnqfg8zRnTn/g5tom1AYIMkghIBCQCEoF7AgHJ8OvwGsstqNqrvZqiOaF4o9pDQ9ERnVy7ojmqxClyrXVNUfQD6wrskH/oG5yhOx5HFB47A+/WpBHoNhT9XHlioBX8h3bDye2ncRW9zKYKKFg6iYBEQCIgEZAI1BMByfDrCRjxbnJlMA4CKi7iNPHqAc5a6Z7j8Bx+B/Tx9oJ2X/FTHNS2M1ycnVFFUwZtffrihp0jymmawK5ptf6+qbi/ocz9cxrpJAISAYmAREAicBsIyGV59QVP5wx3GiZ9nZEreP+57w8R+3dBB1OLPQPVKihn5VVn0tq5NVB0Be0e9IK3dw9UnjqI49ftYc9z+Cf24Ugxn29+Fd99e4IGBi58Srt0EgGJgERAIiARuG0EpIRfBwir5W6O3BKPPj0YZz7/EgknvqRnO3gHPYP2ZnSaNqdJfofmZgZ7QBuvJzDoxxRsXL1CSeFAlvz9O6HqG34swq5PVmEX39q5ICisd430HCSdREAiIBGQCEgE6ouAPDynvogZ41dBr69AUzovW6uyNwbXclNVoUdFVVPodOapK4guLPjXQlAGSwQkAhIBiYBEwAYCkuHbAEcGSQQkAhIBiYBE4F5BQM7h3ytvUtZDIiARkAhIBCQCNhCQDN8GODJIIiARkAhIBCQC9woCkuHfK29S1kMiIBGQCEgEJAI2EJAM3wY4MkgiIBGQCEgEJAL3CgKS4d8rb1LWQyIgEZAISAQkAjYQkAzfBjgySCIgEZAISAQkAvcKApLh3ytvUtZDIiARkAhIBCQCNhCQDN8GODJIIiARkAhIBCQC9woCkuHfK29S1kMiIBGQCEgEJAI2EJAM3wY4MkgiIBGQCEgEJAL3CgKS4d8rb1LWQyIgEZAISAQkAjYQ+H+IjOImOICzNAAAAABJRU5ErkJggg==" alt="" width="508" height="85" /><br />
This was obviously a trick question.  Presumably some programmer expected this expression to produce an array like <code>[1, 2, 3]</code> and it doesn’t. Why not? What does it actually produce?  I didn’t cheat by immediately typing it into a browser but I did quickly look up something in my copy of the <a title="http://www.ecma-international.org/publications/standards/Ecma-262.htm" href="http://www.ecma-international.org/publications/standards/Ecma-262.htm" target="_blank">ECMAScript 5 Specification</a>. From the spec. it appeared clear that the answer would be:</p>
<div style="padding-left: 30px;">
<pre class="brush: jscript; light: true; title: ; notranslate">[1, NaN, NaN]</pre>
</div>
<p>I then typed the expression into a browser and that was exactly what I got.  Before I explain why, you may want to stop here and see if you can figure it out.</p>
<p>OK, here is the explanation. <code>parseInt</code> is  the built-in function that attempts to parse a string as a numeric literal and return the resulting number value.  So, a function call like:</p>
<div style="padding-left: 30px;">
<pre class="brush: jscript; light: true; title: ; notranslate">var n = parseInt(&quot;123&quot;);</pre>
</div>
<p>should assign the numeric value <code>123</code> to the local variable <code>n</code>.</p>
<p>You might also know, that if the string can’t be parsed as numeric literal, <code>parseInt</code> will return as the result the value <code>NaN</code>.  <code>NaN</code>, which is an abbreviation for “Not a Number”, is a value that generally indicates that some sort of numeric computation error has occurred.  So, a statement like:</p>
<div style="padding-left: 30px;">
<pre class="brush: jscript; light: true; title: ; notranslate">var x = parseInt(&quot;xyz&quot;);</pre>
</div>
<p>assigns <code>NaN</code> to <code>x</code>.</p>
<p><code>map</code> is a built-in Array method that is in ECMAScript 5 and which has been available in many browsers for a while. <code>map</code> takes a function object as its argument.  It iterates, over each element of an array and calls the argument function once for each element, passing the element value as an argument. It accumulates the results of these function calls into a new array.  Consider this example,</p>
<div style="padding-left: 30px;">
<pre class="brush: jscript; light: true; title: ; notranslate">[1,2,3].map(function (value) {return value+1})</pre>
</div>
<p>it will return a new array <code>[2,3,4]</code>. It is probably most common to see a function expression such as this passed to <code>map</code> but it is perfectly valid to pass an already existing function object such <code>parseInt</code>.</p>
<p>So, knowing the basics of <code>parseInt</code> and <code>map</code> it is pretty clear that the original expression was intended to take an array of numeric strings and to return a corresponding array containing the numeric value of each string.  Why doesn’t it work?  To find the answer we will need to look more closely at the definition of both <code>parseInt</code> and <code>map</code>.</p>
<p>Looking at the <a title="http://es5.github.com/#x15.1.2.2" href="http://es5.github.com/#x15.1.2.2" target="_blank">specification of parseInt</a> you should notice that it is defined as accepting two arguments.  The first argument is the string to be parsed and the second specifics the radix of the number to be parsed.  So, <code>parseInt(“ffff”,16)</code> will return <code>65535</code> while <code>parseInt("ffff"”,8)</code> will return <code>NaN</code> because <code>"ffff"</code> doesn’t parse as an octal number.  If the second argument is missing or <code>0</code> it defaults to <code>10</code> so <code>parseInt("12",10)</code>,  <code>parseInt("12")</code>, and <code>parseInt("12",0)</code> all produce the number 12.</p>
<p>Now look carefully at the <a title="http://es5.github.com/#x15.4.4.19" href="http://es5.github.com/#x15.4.4.19" target="_blank">specification of the map method</a>.  It refers to the function that is passed as the first argument to <code>map</code> as the <em>callbackfn</em>.  The specification says, “the <em>callbackfn</em> is called with <em>three</em> arguments: the value of the element, the index of the element, and the object that is being traversed.” Read that carefully.  It means that rather than three calls to <code>parseInt</code> that look like:</p>
<div style="padding-left: 30px;">
<pre class="brush: jscript; light: true; title: ; notranslate">parseInt(&quot;1&quot;)
parseInt(&quot;2&quot;)
parseInt(&quot;3&quot;)
</pre>
</div>
<p>we are actually going to have three calls that look like:</p>
<div style="padding-left: 30px;">
<pre class="brush: jscript; light: true; title: ; notranslate">parseInt(&quot;1&quot;, 0, theArray)
parseInt(&quot;2&quot;, 1, theArray)
parseInt(&quot;3&quot;, 2, theArray)
</pre>
</div>
<p>where <code>theArray</code> is the original array <code>["1","2","3"]</code>.</p>
<p>JavaScript functions generally ignore extra arguments and <code>parseInt</code> only expects two arguments so we don’t have to worry about the effect of the <code>theArray</code> argument in these calls.  But what about the second argument?  In the first call the second argument is <code>0</code> which we know defaults the radix to 10 so <code>parseInt("1",0)</code> will return <code>1</code>.  The second call passes <code>1</code> as the radix argument.  The specification is quite clear what happens in that case.  If the radix is non-zero and less than 2 the function returns <code>NaN</code> without even looking at the string.</p>
<p>The third call passes <code>2</code> as the radix argument.  This means that the string to convert is supposed to be a binary number consisting only of the digit characters <code>"0"</code> and <code>"1"</code>. The <code>parseInt</code> specification (step 11) says it only tries to parse the substring to the left of the first character that is not a valid digit of the requested radix.  The first character of the string is <code>"3"</code> which is not a valid base 2 digit so the substring to parse is the empty string. Step 12 says that if the substring to parse is the empty string, the function returns <code>NaN</code>. So, the result of the three calls will be <code>1</code>, <code>NaN</code>, and <code>NaN</code>.</p>
<p>The programmer of the original expression made at least one of two possible mistakes that caused this bug. The first possibility is that they either forgot or never knew that <code>parseInt</code> accepts an optional second argument.  The second possibility is that they forgot or never knew that <code>map</code> calls its <em>callbackfn</em> with three arguments. Most likely, it was a combination of both mistakes. The most common usage of <code>parseInt</code> passes only a single argument and most functions passed to <code>map</code> only use the first argument so it would be easy to forget that additional arguments are possible in both cases.</p>
<p>There is a straight forward way to rewrite the original expression to avoid the problem. Use:</p>
<div style="padding-left: 30px;">
<pre class="brush: jscript; light: true; title: ; notranslate">[&quot;1&quot;,&quot;2&quot;,&quot;3&quot;].map(function(value) {return parseInt(value)})</pre>
</div>
<p>instead of:</p>
<div style="padding-left: 30px;">
<pre class="brush: jscript; light: true; title: ; notranslate">[&quot;1&quot;,&quot;2&quot;,&quot;3&quot;].map(parseInt)</pre>
</div>
<p>This makes it clear that the <em>callbackfn</em> only cares about a single argument and it explicitly calls <code>parseInt</code> with only one argument.  However, as you can see it is much more verbose and arguably less elegant.</p>
<p>After I tweeted about this, there was an exchange about how JavaScript might be extended to avoid this problem or to at least make the fix less verbose.  <a href="http://twitter.com/#!/angusTweets/status/35774944293953537">Angus Croll (@angusTweets)</a> suggested the problem could be avoided simply by using the <code>Number</code> constructor as the <em>callbackfn</em> instead of <code>parseInt</code>. <code>Number</code> called in this manner will also parse a string argument as a decimal number and it only looks at one argument.</p>
<p><a href="http://twitter.com/#!/__DavidFlanagan/status/35769732795736064">@__DavidFlanagan</a> suggested that perhaps a <code>mapValues</code> method should be added which only passes a single argument to the <em>callbackfn</em>.  However, ECMAScript 5 has seven distinct Array method that operate similarly to <code>map</code>, so we would really have to add seven such methods.</p>
<p>I suggest the possibility of adding a method that might be defined like:</p>
<pre class="brush: jscript; title: ; notranslate">
Function.prototype.only=function(numberOfArgs) {
   var self=this; //the original function
   return function() {
      return self.apply(this,[].slice.call(arguments,0,numberOfArgs))
   }
};
</pre>
<p>This is a <a title="http://en.wikipedia.org/wiki/Higher_order_functions" href="http://en.wikipedia.org/wiki/Higher_order_functions" target="_blank">higher order function</a> that takes a function as an argument and returns a new function that calls the original function but with an explicitly limited number of arguments.  Using <code>only</code>, the original expression could have been written as:</p>
<div style="padding-left: 30px;">
<pre class="brush: jscript; light: true; title: ; notranslate">[&quot;1&quot;,&quot;2&quot;,&quot;3&quot;].map(parseInt.only(1))</pre>
</div>
<p>which is only slight more verbose and arguably retains a degree of elegance.</p>
<p>This led to a further discussion of <em>curry functions</em> (really <a title="http://en.wikipedia.org/wiki/Partial_application" href="http://en.wikipedia.org/wiki/Partial_application" target="_blank">partial function application</a>) in JavaScript. Partial function application takes a function that requires a certain number of arguments and produces a new function that takes fewer arguments. My <code>only</code> method is an example of a function that performs partial function evaluation.  So is the <code>Function.prototype.bind</code> method that was added to ES5. Does JavaScript need such additional  methods?  For example, a <code>bindRight</code> method that fixes the rightmost arguments rather than the leftmost.  Perhaps, but what does rightmost even mean when a variable number of arguments are allowed?  Probably <code>bindStartingAt</code> that took an argument position would be a better match to JavaScript.</p>
<p>However, all this discussion of extensions really misses the key issue with the original problem. <em>In order to use any of them, you first have to be aware of the optional argument mismatch between </em><code>map</code><em> and </em><code>parseInt</code><em>.</em> If you are aware of the problem there are many way to work around it.  If you aren’t aware then none of the proposed solutions help at all.  This really seems to be mostly an API design problem and raises some fundamental questions about the appropriate use of optional arguments in JavaSript.</p>
<p>Supporting optional arguments can simplify the design of an API by reducing the total number of API functions and by allowing many users to only have to think about the details of the most common use cases.  But as we see above, this simplification can cause problems when the functions are naively combined in unexpected ways.  What we are seeing in this example is that there are really two quite different use cases for optional arguments.</p>
<p>One use case looks at optional arguments from the perspective of the caller. The other use case is from the perspective of the callee.  In the case of <code>parseInt</code>, its design assumes that the caller knows that it is calling <code>parseInt</code> and has chosen actual argument values appropriately.  The second argument is <em>optional from the perspective of the caller.</em> If it wants to use the default radix it can  ignore that argument.  However, the actual specification of <code>parseInt</code> carefully defines what it (the callee) will do when called with either one or two arguments and with various argument values.</p>
<p>The other use case is more from the perspective of a different kind of function caller. A caller that doesn’t know what function it is actually calling and that always passes a fixed sized set of arguments. The specification of <code>map</code> clearly defines that it will always pass three arguments to any <em>callbackfn</em> it is provided. Because the caller doesn’t actually know the identify of the callee or what actual information the callee will need, <code>map</code> passes all available information as arguments.  The assumption is that an actual callee will ignore any arguments that it doesn’t need.  In this use case the second and third arguments are <em>optional from the perspective of the callee.</em></p>
<p>Both of these are valid optional argument use cases, but when we combine them we get a software “impedance mismatch”.  Callee optional arguments will seldom match with caller optional arguments. Higher order functions such as the <code>bind</code> or <code>only</code> methods can be used to fix such mismatches but are only useful if the programmer is aware that the mismatch exists. JavaScript API designers need to keep this mind and every JavaScript programmer needs to take extra care to understand what exactly will be passed to a function used as a &#8220;call back&#8221;.</p>
<p><strong>Update 1</strong>: Correctly credit Angus Croll for map(Number) suggestion.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.wirfs-brock.com/allen/posts/166/feed</wfw:commentRss>
		<slash:comments>30</slash:comments>
		</item>
		<item>
		<title>Testing for -0 in JavaScript</title>
		<link>http://www.wirfs-brock.com/allen/posts/128</link>
		<comments>http://www.wirfs-brock.com/allen/posts/128#comments</comments>
		<pubDate>Wed, 16 Feb 2011 22:01:28 +0000</pubDate>
		<dc:creator>allen</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Web Standards]]></category>
		<category><![CDATA[ES5]]></category>
		<category><![CDATA[Firefox]]></category>
		<category><![CDATA[Internet Explorer]]></category>
		<category><![CDATA[JavaScript Hacks]]></category>
		<category><![CDATA[JavaScript Object Model]]></category>

		<guid isPermaLink="false">http://www.wirfs-brock.com/allen/?p=128</guid>
		<description><![CDATA[A few minutes ago I saw a tweet go by from my colleague, Dave Herman (@littlecalculist): For background you need to know that JavaScript Number values have distinct representations for +0 and -0.  This is a characteristic of IEEE floating point numbers upon which the specification for JavaScript numbers is based.  However, in most situations, [...]]]></description>
			<content:encoded><![CDATA[<p></p><p>A few minutes ago I saw a tweet go by from my colleague, Dave Herman (@littlecalculist):</p>
<p><img src="data:image/png;base64,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" alt="" /><br />
For background you need to know that JavaScript Number values have distinct representations for <code>+0</code> and <code>-0</code>.  This is a characteristic of IEEE floating point numbers upon which the specification for JavaScript numbers is  based.  However, in most situations, JavaScript treats <code>+0</code> and <code>-0</code> as equivalent values. Both <code>-0==+0</code> and<code>-0===+0</code> evaluate to <code> true</code>. So how, do you distinguish them if you really need to?  You have to find something in the language where they are treated differently.</p>
<p>Dave&#8217;s solution uses one such situation.  In JavaScript, division by zero of a non-zero finite number produces either <code>+Infinity</code> or <code>-Infinity</code>, depending upon the sign of the zero divisor. <code>+Infinity</code> and <code>-Infinity</code> can be distinguished using the <code>==</code> or <code>===</code> operators so that gives us a test for for <code>-0</code>.</p>
<p><strong>Update: </strong>As Jeff Walden points out in a <a title="http://www.wirfs-brock.com/allen/posts/128#comment-112" href="http://www.wirfs-brock.com/allen/posts/128#comment-112" target="_self">comment</a>, Dave&#8217;s solution isn&#8217;t adequate because <code>-0</code> isn&#8217;t the only divisor of <code>1</code> that produces <code>-Infinity</code> as its result. That problem can be solved changing the body of his function to:</p>
<p style="padding-left: 30px;"><code>return x===0 &amp;&amp; (1/x)===-Infinity;</code></p>
<p>Are there any other ways to test for <code>-0</code>. As far as I know, until recently there wasn&#8217;t. However, <a title="http://www.ecma-international.org/publications/standards/Ecma-262.htm" href="http://www.ecma-international.org/publications/standards/Ecma-262.htm" target="_blank">ECMAScript 5</a> added a new way to make this test.  Here is what I came up with as a solution:</p>
<pre class="brush: jscript; title: ; notranslate">
function isNegative0(x) {
   if (x!==0) return false;
   var obj=Object.freeze({z:-0});
   try {
      Object.defineProperty(obj,'z',{value:x});
   } catch (e) {return false};
   return true;
}
</pre>
<p>If you are a <a title="http://www.ecma-international.org/publications/standards/Ecma-262.htm" href="http://www.ecma-international.org/publications/standards/Ecma-262.htm" target="_blank">specification</a> junkie you may want to stop and take a few minutes to see if you can figure out how this works.  For the rest of you (that care) here is the explanation:</p>
<p>Line 2 is taking care of all the values that aren&#8217;t either <code>-0</code> or <code>+0</code>, for any other values <code>!==</code> will produce <code>false</code> and return.  The rest of the function is about distinguishing the zero values.  Line 3 creates an object with a single property whose value is <code>-0</code>.  The object is frozen, which means its properties cannot be modified. Line 5 tries to use <code>Object.defineProperty</code> to set the value of the frozen object&#8217;s sole property to the  value of <code>x</code>.  Because the object is frozen you would expect this to fail (and throw an error) as it is an attempt to change the value of a frozen property.  However, the specification of <code>defineProperty</code> says it only throws if the value (or other property attributes) that is being set is actually different from the current value. And &#8220;different&#8221; is defined in a manner that distinguishes <code>-0</code> and <code>+0</code>. So if <code>x</code> is <code>+0</code> this will be an attempt to change the value of the property and an exception is thrown. If <code>x</code> is <code>-0</code> it isn&#8217;t a change and no exception is thrown.  Line 6 catches the exception and returns <code>false</code> indicating that the argument isn&#8217;t <code>-0</code>. If <code>defineProperty</code> didn&#8217;t throw we we fall through to line 7 and return <code>true</code> indicating that the argument is <code>-0</code>.  For the spec junkies, the key places that make this happen are [[DefineOwnProperty]] (section 8.12.9) and the SameValue algorithm  (9.12).</p>
<p>I don&#8217;t think this satisfies Dave&#8217;s request for a more straight forward test, but it does illustrate a subtle feature of the ES5 specification.  But does it actually work?  This is  one of those obscure specification requirements that you could easily imagine being overlooked by a busy JavaScript engine developer.  I tested the above <code>isNegative0</code> on pre-production versions of both FireFox 4 and Internet Explorer  9 and I was pleasantly surprised to find that they both worked for this test exactly as specified in  ES5. So congratulations to both the FF and IE developers for paying attention to the details.</p>
<p>(And thanks to Peter van der Zee, @kuvos, for suggesting this is worth capturing in a blog post)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.wirfs-brock.com/allen/posts/128/feed</wfw:commentRss>
		<slash:comments>25</slash:comments>
		</item>
		<item>
		<title>The Browser is a Transitional Technology</title>
		<link>http://www.wirfs-brock.com/allen/posts/115</link>
		<comments>http://www.wirfs-brock.com/allen/posts/115#comments</comments>
		<pubDate>Tue, 01 Feb 2011 00:05:37 +0000</pubDate>
		<dc:creator>allen</dc:creator>
				<category><![CDATA[Browsers]]></category>
		<category><![CDATA[Post-PC/Ambient Computing]]></category>
		<category><![CDATA[ambient compting]]></category>
		<category><![CDATA[computing eras]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[transitional periods]]></category>
		<category><![CDATA[ubiquitous]]></category>

		<guid isPermaLink="false">http://www.wirfs-brock.com/allen/?p=115</guid>
		<description><![CDATA[In the graphic for my Third Era of Computing post I have two pairs of lines labeled “Transitional Technologies”. In my model, a transitional technology is a technology that emerges as a computing era settles into maturity and which is a precursor to the successor era. Transitional technologies are firmly rooted in the “old” era [...]]]></description>
			<content:encoded><![CDATA[<p></p><p>In the graphic for my <a href="http://www.wirfs-brock.com/allen/posts/74">Third Era of Computing</a> post I have two pairs of lines labeled “Transitional Technologies”. In my model, a transitional technology is a technology that emerges as a computing era settles into maturity and which is a precursor to the successor era. Transitional technologies are firmly rooted in the “old” era but also contain important elements of the “new” era. <a href="http://www.wirfs-brock.com/allen/wp-content/uploads/2011/01/3eras-medium.png"><img class="alignright size-medium wp-image-81" title="The Eras of Compuing" src="http://www.wirfs-brock.com/allen/wp-content/uploads/2011/01/3eras-medium-300x225.png" alt="" width="300" height="225" /></a></p>
<p>Transitional technologies that preceded the emergence of the Personal Computing Era included <a href="http://en.wikipedia.org/wiki/Time-sharing">time-sharing</a> and <a href="http://www.alanclements.co.uk/History/Minicomputer.htm">minicomputers</a>.  Both of these technologies emerged as corporate computing matured and both technologies personalized, to a degree, human interaction with corporate computing resources.  Time-sharing allowed individuals to directly access a fractional share of large mainframe computing resources.  Minicomputers reduced the cost and complexity of computers to the point where they could be applied to departmental level problems and could be programmed or administered by individuals.</p>
<p>Time-sharing and minicomputers were significant steps towards the Personal Computing Era as they offered the first glimpses of what is possible when a computer is used to empower individuals. Most of the <a href="http://www.digibarn.com/collections/systems/altair-8800/index.html">earliest personal computers</a> physically resembled <a href="http://www.simulogics.com/museum/N1200_1.JPG">minicomputers</a> and their <a href="http://johnkingworld.com/aplus/images/os-cpm.jpg">operating systems</a> were modeled after minicomputer OSes  and <a href="http://homepages.ipact.nl/%7Elokhorst/hcc2003.img.large/simh.pdp10.tops10.gif">time-sharing user interfaces</a>.  However, the true <a href="http://www.guidebookgallery.org/articles/microelectronicsandthepersonalcomputer">archetype</a> of the modern personal computer anticipated something very different than a scaled down corporate computer.</p>
<p>My diagram shows cellphones and the “www” as two of the transitional technologies from the Personal Computing Era to the Ambient Computing Era. By “www” I meant both the concept of ubiquitous information access via public websites and the personal computer hosted browser applications used to access such information.  Both cellphones and the web established themselves as mainstream technologies in the 1990’s, just as personal computing was reaching maturity.  Both are personal and task-centric.  The browser itself is the epitome of a Personal Computing Era application program.</p>
<p>Both the cellphone and the www give us glimpse of things to come in the Ambient Computing Era and both establish some of the foundation technologies for that era.  But we shouldn’t expect the Ambient Computing Era as it matures to be just a refinement of cellphones and web browser any more than the Personal Computing Era was a only a refinement of time-sharing and minicomputers.</p>
<p>Right now, we seem to be in second golden age of browser innovation but that doesn’t mean that the browser, as we know it, will continue into the Ambient Computing Era.  Recall that Digital Equipment Corporation, the minicomputer company that grew into the world’s second largest computer company, hit the all-time high for its <a href="http://books.google.com/books?id=_855mOqQNd4C&amp;pg=PA46&amp;lpg=PA46&amp;dq=digital+equipment+corp+stock+high&amp;source=bl&amp;ots=X4jTkCKjCU&amp;sig=nc-s9JkPZ0S0XJefwDdJwuJ1dEQ&amp;hl=en&amp;ei=_DFHTaDXBYX0tgOIuLDkAQ&amp;sa=X&amp;oi=book_result&amp;ct=result&amp;resnum=5&amp;ved=0CDgQ6AEwBA#v=onepage&amp;q&amp;f=false">stock in 1987</a>. That was the same year that Apple introduced the Mac II and Microsoft introduced Windows 2.0. Ten years latter the Personal Computing Era was firmly established but DEC and the minicomputer were no more.</p>
<p>For those of us to work on browser technologies that means it isn&#8217;t good enough to just create a great new PC-based web browser release every year or two (or even every 6 weeks, or every three months). We also have to aggressively work on the new technologies and user experiences that will make the web browser irrelevant.  Personally, I expect that many web technologies including HTML, CSS, and JavaScript are going to be foundational for the Ambient Computing Era but I don’t expect them to be packaged in a browser-like application running on running on a Windows or Mac PC. That transition has already started.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.wirfs-brock.com/allen/posts/115/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>

