tag:blogger.com,1999:blog-1074279646304163912024-03-08T19:48:32.591+01:00BitElmBitElmAnonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.comBlogger41125tag:blogger.com,1999:blog-107427964630416391.post-72448408432201307432014-04-03T12:17:00.001+02:002014-04-03T12:17:36.064+02:00Limit MongoDB disk usage on your machineWhen doing local development, you might want to limit how much space mongodb uses.<br />
<br />
<em>Note: Do not do this in production unless you know what you are doing. It will affect performance.</em>
<br />
<br />
If you are on Ubuntu perform these steps:<br />
<br />
<ol>
<li>Stop mongodb<br /><code>sudo service mongodb stop</code>
</li>
<li>Edit the mongodb configuration<br />
<code>sudo nano /etc/mongodb.conf</code>
</li>
<li>Add this to the file and save/close:<br />
<code>smallfiles=true</code>
</li>
<li>Remove old journal files:<br />
<code>sudo rm -rf /var/lib/mongodb/journal</code>
</li>
<li>Start mongodb<br />
<code>sudo nano service mongodb start</code>
</li>
</ol>
Anonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.comtag:blogger.com,1999:blog-107427964630416391.post-46363500611568448062014-04-03T11:22:00.000+02:002014-04-03T11:22:19.517+02:00Dropping a database after running rspec tests with MongoMapper or MongoidThe mongodb folder on my development computer was using 11 gb, and I needed to free up some space on /var, since that was on the / partition. (Yes, it is recommended to have it on separate partition, but that is another discussion).
<br />
<br />
In your rspec add this:
<br />
<br />
<pre>RSpec.configure do |config|
config.after(:suite) do
# for MongoMapper
db = MongoMapper.database
# for Mongoid
# db = Mongoid.master
# drop the database
db.command({dropDatabase:1})
end
end
</pre>
<br />
<br />
For using mongo directly, you need to access the db object and trigger the command as above.Anonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.comtag:blogger.com,1999:blog-107427964630416391.post-35516357842354094012014-03-10T12:12:00.000+01:002014-03-10T12:12:40.792+01:00Your development log files in Rails is probably hugeI always forget to truncate my development logs, and when I need to debug or dig into the logs, they are dead slow to work with.<br />
<br />
Use this simple gist to truncate your logs automagically. It will only touch your development.log, as I do note like when initializers pull production or staging logs into the mix, as that might be a security risk when you just needed to test a part of your app on your production environment.<br />
<br />
<br />
<br />
<script src="https://gist.github.com/leifcr/9462906.js"></script>
<br />
Anonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.comtag:blogger.com,1999:blog-107427964630416391.post-47247864122222058052014-01-15T15:33:00.000+01:002014-01-15T15:33:05.769+01:00SVGs in browsers (Firefox is the new IE)<h3>
Intro </h3>
SVGs in browsers are fun, and if you say goodbye to IE8 and older, it is almost working cross-browser.<br />
<br />
I'm working on a basic editor-like application that involves a lot of SVG hacking, but it's quite fun due to the great <a href="http://snapsvg.io/">Snap.svg</a> lib by Dmitry Baranovskiy (working for Adobe).<br />
<br />
My main development browser is Chrome, due to the powerful debugging features, and everything works quite well. I wrote a lot of tests, and I was going to fire them up i Firefox, and expected them to work. 5 out of 43 tests passsed, where 43 of 43 passwed in Chrome, Safari, IE10, IE11 and Opera.<br />
<br />
I told myself: keep calm, it's probably nothing.<br />
<br />
After digging further into SVG in Firefox, there are some issues specific to Firefox.<br />
<ol>
<li>Mouse coordinates relative to the SVG</li>
<li>getIntersectionList and getEnclosureList is not implemented</li>
<li>getBoundingClientRect</li>
</ol>
<br />
<h3>
Mouse coordinates</h3>
The usual hack for not having offsetX and offsetY in events in FF is something like this:<br />
<pre>offsetXY = function(event) {
var br;
var evt = event;
if (evt.offsetY !== void 0 && evt.offsetX !== void 0) {
return evt
} else {
var br = event.target.getBoundingClientRect();
evt.offsetX = evt.pageX - br.left;
evt.offsetY = evt.pageY - br.top;
}
return evt;
};
</pre>
<br />
However, when you are using SVG's, <span style="font-family: "Courier New",Courier,monospace;">getBoundingClientRect</span> does NOT provide the actual box around the SVG DOM element, but instead it gives a "close-bounding-box" around the elements inside the SVG. Basically the usual FF hack to get offsetX/Y does not work.<br />
<br />
Solution:<br />
Create a svg like this:<br />
<pre><svg id="testsvg" style="height: 500px; width: 500px;">
<defs>
<rect height="100%" id="mouse_calc" width="100%" x="0" y="0"></rect>
</defs>
</svg>
</pre>
<br />
You now have a rectangle where you can find the correct coordinates calling <span style="font-family: "Courier New",Courier,monospace;">getBoundingClientRect</span> on the rectangle By putting the element in the defs area, it's hidden from view, but not clickable, so you still need to do the click handler on the actual SVG. The new code will look like this:
<br />
<pre>offsetXYSVG = function(event) {
var br;
var evt = event;
if (evt.offsetY !== void 0 && evt.offsetX !== void 0) {
return evt
} else {
var br = document.getElementByID("mouse_calc").getBoundingClientRect();
evt.offsetX = evt.pageX - br.left;
evt.offsetY = evt.pageY - br.top;
}
return evt;
};
</pre>
<br />
Nice one FF, two different hacks to get correct offsets...<br />
<br />
<i>Note: layerX and layerY does NOT give you the correct values for the mouse click on SVG, due to getBoundingClientRect working differently than in the rest of the current browsers. </i><br />
<br />
<h3>
<i> </i><br />getIntersectionList and getEnclosureList</h3>
These to functions are essential to figure out if any objects are intersecting or if one object is within an enclosure when working with selections of gui elements. getIntersectionList can be implemented using the Separating Axis Theorem and getEnclosureList can be implemented with simple checking of x/y and x+width/y+height is within a given rectangle. However, performance for getIntersectionList in JS compared to Separating Axis Theorem implemented directly in the C++ core of the browser is a performance killer when working with many objects.<br />
<br />
<h3>
getBoundingClientRect</h3>
As mentioned, this basically fails for SVG elements.<br />
Here is how the bounding rect is calculated for a svg with elements going outside forced 500x500 px svg size: The dotted line shows what FF gives as getBoundingClientRect, and the red line shows the actual SVG. Chrome, Safari, IE 11 and Opera gives the red line for both.<br />
<img alt="" height="397" src="data:image/png;base64,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0UcfiYiGw6WiomLe7s+LL764x2OjRo0OPfTQQw45pE+fPr179y4pKQknErxAAAIQ+AGBrMrKymqhyF24Jk2aVHsq4oNZWUoTc8SBOede1Ev0csyYMa+//vpnn31mYX6FhYV9+/aVp/2DBw+WqaqFERISBCBgRMC+L3YTEURBjQY3UY22b98+duxYmfO99tprK1eujEvuHTt2PPnkk0877bSePXvGJWbihAAEviNgn4LK5KHGWlwUlAv4OwI7dux48803R48e/eqrr65bty6+XPbff3/R0SFDhnTv3j2+WRA5BJJFIJ4Kql3dzuF1cZN1XRpkK08Zr7vuuhYtWvz4xz9+8sknYy2fkq6UI912221ScySFAI899timTZsMGNAEAhCAQNoEtHNQkwls2t586WDfnyq+pBW+kV27dr300ksPP/zwhAkTdI/Dw4/Kd4+1atU688wzL774Yu7u+s4WgxDwjYB9X+wmz0G1c1DfuGDIPgKyysH999/fvn37008/ffz48Q7Lp7CXOeijjz7aq1evY4455q233nI7WfuuNSKCgMsEtHNQE/mNBox9f6pEwyEjr1IZdO+998q8c82aNRkZiH0nean06quvlllpTk5O7JMhAQ8EpGJOnl/sXfGjuLhYXjVmbSwPRL11te+L3UQEUVBvox6f3iKZd99993333cdzQRk0Kdy95ZZbpOBIVmyIzxgSqVcCopfyfOqdd96ZNGnSnDlzfrDYpMhnhw4d5P2oAQMGHH/88QUFLKztFXga/e1TUKNHmXJTq9rPsmXLqj0e/UElr4PySYPAxo0bRS1k2bw0ruZkNJX56Msvv5wGSprGlsDXX3990UUXyUNxw0u7tLT0yiuvlIVEYptx3AK374td3kqoEaL2D3BqcQ1/02xuJksiPP744/K8UxRU1syzOdRIYpNVCU866aSjjz562rRpkQSA0xAIyE0XuW8vk0t5HG5+A0bu2fzlL3+RZTpuuukmw7WdQ8gFF7YR0CqobYEST7oE3n333YMOOuiCCy5YunRpun0T1R5QDg/3e++917lz57vuukueemaQ5rZt237/+9/LmsxTp07NoDtdYk3AZF1cFDTWQ1x98CtWrJBKGaZW1dOp7ujeybqUKMu/q2vCsfgRkKf+/fv3X7RokcfQZduhI444YsSIER7t0N09AloFNZFf93A4kNETTzwhf3SPHDnSgVxCTkFu8V166aWyYL1sOBOya9z5TuDmm2++7LLLUmxMm5ZH2Urh5z//uazUkVYvGjtPQKugzmfuXoJSKzFo0KDzzjtPNuZ0L7vQMpoyZcqBBx4o37+h7T8TWmrJcfTQQw/97ne/8zdfKSr57W9/K5b9NYs1awmYFAOhoNYOX3qB/f3vf5d17GRF+PS60bo6AvLMTL5/ZQ+1GTNmVHeeY1YTkHUzhg0bFlCIMq81+WINyDtmbSOgVVCuEtuGShePzDhPPfXU888/X95a0bXheAYEPvnkEynFkiejMvnIoDtdIiEg75+cffbZsmJlQN5lA4ahQ4ea1/QGFAZmLSGgVVBL4iOM1ARkSVvZgWTv7tOpG6c+W6AK6qv6+6h99lf791Q9D1AHdFfd5d/NVLN6qp6cTd3dybPyGoM8GT3uuONitMubkwNhnpTMEVetWmXePoOW8rhEbvJn0JEu8SJgUgykXZPIaDmGSHjYt3RFJBjEqawxJHuqZFYrUUvV6qq6tlKtmqvmLVQL+SlVpakTWalWLlFLFqvF8t+FauF0NX2L2pK6izNnW7VqJavwy/NRZzJyMpGJEyceddRRIaQmS0LOmjVLVgEMwVdSXNj3xc6qfs5ee1u2bLnwwgvTLbjNV/mimr1UL5litlfts5WnOxC71K5ZatbH6mP5kX/sVDudxb07scLCwkceeeTcc891O81YZyd3C2SP23BSkF9A2TsvHF+J8IKChjTM9oEOKfH/ulm4cKFs5Pnpp58a+s1SWXI/dpAadKQ6skgVGfZKq9lGtXGCmvCOemeGcrz05pJLLrnnnntyc3PT4kPjEAgsWLCgTZs2oT20LioqkrVKWCzTt5G174vd5EYsXwS+XQDhGJIKrxNPPFFWLTZx10g1GqwGi3Y2Vo1N2mfcpraq/WP1Y/n5Rn0zVo19Xb2+Vq3N2JrNHR944AFZkXzUqFF8ddo2TM8//3xo8im5yzNy8Shr7drGgXjCJKC9j0ctbpjDYOhr9OjR/fr1M5FPebp5lbrqSfXk2ersoOXz+8FLIdJP1U+fVk8PU8OaqCaGecWr2dtvvy0vunz55ZfxCtv5aOUllpBzfOONN0L2iDvbCFBJZNuIaOO58847r7322hr/yt5X7XuOOqev6is3b7W2QjkhD0rl1q6oqVQeheIwVCcNGzZ85ZVXZAGjUL3iTENAXjKRuwIhLwFfv379xO6zqxkHD4ftu4trUkmknYN6IEFX/wnIYijXXHNNavksVIUXqgsfUY8cpY6KXD4FQY7KGagGPqYeO1ed697LMPLKhKwANW7cOP8HG4vpE5DK2JDlU2Jcu3btkiUO/nWYPv7k9tAqqMmrMMnFFmLmopqyT2GNC3Iepg4boUYMUUNEt0KMrmZXeSpP5sSPqkcPVK69CiIV0WVlZa+99lrNFGgRMAHZqC5gD9WbnzdvXvUnOJoMAloFTUb6tmcp8nnxxRdL8WeKQOXNzpvUTb9TvwvzeWeKeKo9Jc9H71B3XKuuDagYuFqnIRyU3a9OOeWUF154IQRfuEhBIKrtb2UamiIqTsWagEkxEApq7xCLfP7qV7+SdxBThCivdT6kHpLXVFK0seeU3NR9UD3YWrW2JyTvkcgiumeccYZsZ+/dFBZiR2Dz5s2xi5mAfSSgVVAT+fUxDkxVJSAPPh9++OGqx/ceOVGdeK+6t6lqmqKNbadaqpYPqAfkBRvbAvMSj6zCOmTIkPHjx3sxQt84EsjLy4tj2MTsFwGtgvrlADuZEZC9Qe666y5dX3nYebW6+hJ1iTxl1LWx9rhUFUnw8rqLx0WRrEpQbueedNJJH3zwgVVRJSeYqJSM14IdvsZMioFQUBsvANmDMMXS1aJAt6hb4j6Nk+UXfqt+G8e/AHRXzIYNG6SwKKqSFl1UCTnesWPHSDJt3bp1JH5xagkB7fugJq/CRJODfa8N+ctBnqidfPLJuu2ZZPWfP6g/dFad/XUalbVP1Cc3q5tdWqG+RYsWkydPlv9GhTSZfqWSSN7OTP26l+9kZOIrz0Gjmv76nk7EBu37YjcRQeagEV82P3AvtwGlLEUnn3VV3XvUPc7Ip+Qua9zfre4uUSV2DYOHaBYvXjx48OCoSkM9BB7vrnI3tWvXriHn0Lt3b+QzZOZhujMpBkJBwxyRGnzJu2VyG1DeMqy2ndy8ldmnLDlU7dn4Hmyn2slN6VzlzhLNciNX7iJIjW58ByWOkffv3z/ksAcMGBCyR9zZRkCroCbya1sysY5Hdr2Xr13dTs4iML9Xv++oonnYEzRYmYneqG50qbBI6nJ/85vfBM0N+98ncOqpp4YM5KyzzgrZI+5sI6BVUNsCdTseeX5z3nnn6YpQZIm+36jfyL6eDkM4XB0u1bkuJXj//fc/9dRTLmVkeS6yRnGYW17LVt4dOnSwnAnheSFALa4XeqH2/eMf//jiiy/qXJ6uTh+g3L9fdII64Xh1vA5CHI//8pe//OSTT+IYeRxjzsrKCnPeL69rx5ESMftLgFpcf3lmYk32SJIds3XVQ11UF6m1sW2120zyNOhTrsrlJdf5ar5B23g0adu27YcfflhaWhqPcGMeZXl5ebdu3ebOnRt0HkccccTEiRNFs4N2lCD71OImaLD9S1U2+5T7tzr5rKPqXK+uT4h8ClSplpJ8XdrI5auvvpKlGf27XrCUikBBQcG9996bqoUf53JycuQWPfLpB0urbZgUA/EcNMohlMef559/vq56SCKTtXtsXi8+CHZtVJtfqF8EYTkqm88//zwPREODL68SXXLJJYG6u/XWW3v27BmoC4zHhYBWQU3kNy5JWhvngw8++M9//lMXXj/V71B1qO6sw8flgWhXFfa7fYHylO/0+fPnB+oC43sJ3H333cG92SLvm1133XXQhsAeAloFBVDQBGRP4BTFCMWq2LGpmDlPqT2+XF3u0huisuDfOeecU1FRYQ6BlhkTyM/Pf+mll3r18r92/fDDD3/uuefkLm7GsdExRgSoxbV3sOT+7YUXXrh161ZdiLIrdUPVUHfW+eOycMRp6jSX0pw0aZIsd+xSRjbnIksUvfvuu0cffbSPQZ5wwgljx44tLi720Sam4k5AOwc1kd+4Jx9h/LJt2fvvv68LQHbQPEmdpDubkONnqbOaqWYuJTt8+HBZ88+ljGzOpU6dOm+99dYVV1zhveQnOzv7xhtvfPnll5FPm0c8kti0ChpJNAlxumTJEvkyTZHsT9VPk1N/q+MgFblnqjN1Z+N4fOPGjb/+9a/jGHlMY5ZFa//yl7+Iju6///4ZpyDL7U6YMEF2G8zNdWfhyYxpJKqjSTEQChrBJXHppZemWHlcilH7qD4RhGWfy4FqYCPVyL64Mo9INt4ZPXp05v3pmT6BY445Zvr06fKWS9Om6e1F36ZNm0ceeWTatGl9+/ZN3y09EkFAq6Am8psIQn4n+fbbb0uZQwqrMvGSUpoUDZJzSoqJHHsaKmN31VVX6TYPSM7Ihpyp1BZddtllUg4tK3/J8rkNGjRIEUDjxo2HDh36+uuvz5kz56KLLmLqmYIVp7RrEo0ZM0bqtm0EZN/SFeaUduzY0b17d6nC1XVpqVqOUCNQ0L18ZJUieSC6Xq3XEYvjcXmoJncF4xi5GzFLUfTMmTNlYiq/iWvWrNm2bVtRUZGsG9WpUye5ZyubdXt/dOoGqFCzsO+L3WR/UO7sh3qR3HfffSnkU0L5ifoJ8vn9IZGnoYPV4JFqZKjjFLCzO++882c/+5ncJAzYD+arJyCVQV12f6o/zVEIGBPQ3sWlFteYoWlD+Ysm9cxDbloepY4yNZeYdvI01LFcZdJz5ZVXOpYU6UAggQS0CppAFkGnfPvtt8ub9Sm8HKQOqqvqpmiQzFOtVCv3NkZ95ZVXKDVI5vVM1nEhYPIbioKGNJrffPPNo48+mtpZErYwS01Ad/YYdYzuVHyP33LLLfENnsghAAEhoFVQE/mFoDmBO+64I8UKRGKnSBUdpg4zN5iolkero11a5G/P2MmSyFOmTEnUOJIsBBwjoFVQx/KMNh1ZQuGxxx5LHUN31d2lXb1SJ5vu2dqqtns3cgUC09B0rwTaQyA0AibFQChoGMMhtZdSPJLaU0/FfkmpCDnJR5bL+eCDD1KlzTkIQMBiAloFNZFfi/OyKLR169aNGDGixoCcVIgaszZv0Ev5v9WGuffgWsqyc8EZxzIEIBAoAa2CBuo1UcZlEflNmzalTrmOqrOf2i91m4Sf7aQ6OXmXWxao+vLLLxM+uKQPAQsJmBQDoaDBDtz27dtlFYUafciG0iykkJpSnspz8lHozp07//rXv6bOnbMQgICdBLQKaiK/dqZkVVSyH+/SpUtrDEm2w6yxDQ1kyUMnIfz9739fu3atk6mRFATcJqBVULfTDi07uYVr4ksWDTBplvA2zVVzJwnITf4nnnjCydRICgLxJWBSDISCBji+X3zxxeTJk00ctFAtTJolvI2rCirDWuNqGwkfetKHgJ0EtApqIr92pmRPVCYluHuiZQ5qMmr7qH1MmsWxjWwV8t5778UxcmKGQJIJaBU0yVB8yV1qiJ566ikTU/VUvWJVbNIy4W0cnoPKyMpmzgkfX9KHgFUETIqBUNCghkx26F21apWJdVnPz6QZbWRhv3yV7yqH0aNHp954wNXEyQsC8SWgVVAT+Y1v2iFEPnKk6ZaWhaowhHjccFGiStxIpGoW5eXlsq191eMcgQAErCWgVVBrI45FYBs3bpQ5qGGoDquCIQHzZm6zkmmoOQpaQgACgRIwKQZCQQMZAplMbNmyxdA0c1BDUNLMbQV988035W8vcxq0hAAEoiWQq3NvIr+6vhyXhRTMIeSoHPPGCW/p8HNQGVnZfuC1114744wzEj7KsU5/4cKFskzjggULZJUMKScsKSkpLS1t165d+/bt69WrF+vUCL4qAa2CVm3KEUMCsg/ouHHjDBtLswr5Hx8zAtvVdrOGcW2FgsZx5CorK+VlJCl9kLsI8+fPrzaFnJwcmZYcf/zx55xzTqtWrKBSLSS7DkoxUFlZWeqYuIubmk8mZ8ePH596M+0fGN2qtmbiJpF9NqvNbuctX8G7du1yO0eXspNZpryG1KFDh6OOOkoWINPJp6Qswyrrq9xwww1t2rQ59thj33nnHZc4JDYXrYJSi5vxNSGbPqbVd4syfWKallknGzvPas2aNR9++KGTY+deUm+//XaXLl1++ctfzp071zy7iooK+YoYOHDgCSecwLY85tzsbKlVUDvDjUVUb7zxRlpxblM1bL6dljW3Gzs/B5XhS/f6cXvE7cxOXj0aNmzYj370o3nz5mUcoZTr9+zZ85lnnsnYAh0DJWBSDISC+jwE8huV7t+VKKjhGOxUO8tVuWHj+DaTG7nxDT4JkcvCF4MGDXrggQe8JyubCpx99tnDhw/3bgoLkRDQKqiJ/EYSseVOM1jddI1aw6NQk2FdqmreJ87EjuVtpk2btnmz4497LR+CFOGtX79+wIABEydOTNEm3VN33HHHZZddlm4v2ttAQKugNgQXxxjef//9DMJerBZn0CtpXZaoJUlIWfbc/uCDD5KQaexylLqhk08+eerUqb5Hft999915552+m8WgFwImxUAoqBfC1fSdNGlSNUdrOrRQLaypCedlBpqIOaiMdGZXEZdI0ARkpiiV9gF5kXu57777bkDGMRsQAa2CmshvQDHF1+zKlSvnzJmTQfyL1KIMeiWtS3Jm6pndyUja9RByvi+99JK8rxKcU3ndZejQoevWrQvOBZZ9J6BVUN89JcGgvIcg71ZnkClzUBNoyVHQKVOmZHYhmWCkTQYEZJHOK664IoOOaXVZvnz5TTfdlFYXGgdHwKQYCAX1k//nn3+embnP1GeVKhPpzcxdHHtJIe4MNSOOkWcQs1R7png3PwODdPFI4MEHH5Tl+jwaMen+0EMPMfQmoCxpo1VQE/m1JAd7wpgxI8Ov+PVq/dfqa3sSsTCSmWpmol77yfivMQvHLu4hSQHRPffcE04WUkd29913h+MLL94JaBXUu+kEWvjiiy8yznqampZx3yR0TBofFNSeq1qWPli6NLwqtieeeELeE7Un/cRGYlIMhIL6dnnIYl2zZs3K2Nwn6pOM+yahY9L4oKD2XNXPP/98mMHIDncvv/xymB7xlTEBrYKayG/GXp3suGjRIi8vwuFeuJAAACAASURBVH+qPk3CgjuZDf1GtVHu4mbWN6a9pk+fHtPIHQtbSrqCe4NFx4p153VkbDuuVVDbArU/nq+/9vQgU5Ylmqwm259mJBFOUBOkkigS11E5pZwkKvI/8Cvvp8lbaiEHw+tMIQOv1p1JMRAKWi26TA56VFBx+Y78j091BMaqsdUddvmY3M9YsWKFyxnGJLdPPong8Yp8mUj5UkwIJTpMrYKayG+iyVVJ/quvvqpyLL0DU9VUKcpNr08CWsvLsrNU5g+Y40vI+xUV39ztiXzmzAgeH8jqCoy+PddAiki0CpqiD6eqJeD9tpvcqPyX+le1xpN8cJwal8z0vV9RyeTmb9aykZm/Bg2tyTvBhi1pFhABk2IgFNQ3+FJJ5N3WK+oVllb4PkaprnpDpbffqvdRsMSCL1eUJbkQRroEolLudONMeHutgprIb8LZ/SB9WZHLOxBZIJdp6Pcxinwm9s726tWrvV9RWIgpgaKiophGnqiwtQqaKAq+JOvX992z6lmmoXtGRG5rv6Be8GV04mhkzZo1cQzbsZhLSkoiyahevXqR+MXpXgImxUAoqD8XjDz5X7VqlS+2ZHm/91Umm4z64t0qI/IEdKUK+0UCewigoDaMRZcuXcIPIy8vr3Xr1uH7xWO6BLQKaiK/6TpzuL1MQH3cTOMJ9cQutcthXCapbVfbZTpu0tLVNmvXrnU1tRjl1atXr/Cjbd++fW5ubvh+8ZguAa2Cpmso4e39uoW7B+N8Nf9V9WrCkYp8JmdL7WrHmjlotVhCPihzwZYtW4bs9MgjjwzZI+6qEjApBkJBq3LL5IjsqJBJN30fmYauUcl9DCZbgY5So/R4EnHG94sqEdQCSHLgwIEBWE1lMnyPqaLhnJ6AVkFN5FdvNnFn5DmovzlvVptHqBH+2oyRtfvV/TvUjhgFHESovBEYBNUMbJ5xxhkZ9Mq4S6NGjcrKyjLuTscwCWgVNMwgHPAVxG5EspRd0rb02nMlSOIfq48duCo8prBjR9L/hvAI0K/uAwYMaNeunV/WarRz0UUX5efn19iMBkETMCkGQkGDHoXM7cs7Lber25N2L1feiJUJaObUHOq5ZcsWh7KJcSrZ2dnXXnttOAnIa6CXXXZZOL7w4p2AVkFN5Ne7eyykJrBWrf2j+mOFqkjdzJmzUn/7B/UH2abGmYy8JOJjdbeXMOgrBM4777yuXbuGgGL48OFyFzcER7jwhYBWQX2xjhHvBD5Tn/1D/cO7nVhYeEg99JXyukB/LDI1CTIrK8ukGW1CICDvljz44IM5OTmB+pKXWK6++upAXWDcnIBJMRAKas4zVctAv+yeU88lYeOz19Rr8pOKcsLO1alTJ2EZW51u3759b7755uBCLC4uHj16dGFhYXAusOw7Aa2Cmsiv79HE12CgX3byQPQudZfbVUX/Vv/m8ecPrn/eqbftC+GGG24466yzgohK/gR/7LHHunXrFoRxbAZHQKugwbnEcgYEZIXYG9WNrm6T+Yn65Pfq98l53Gt4AdSqVcuwJc3CISA69/jjj/v+qomY/ctf/nLmmWeGkwVeDAmYFAOhoIYwa2gWwr0X2efrBnWDbDddQyhxOz1PzbtF3SJ/IsQt8MDjLSgoCNwHDtIkIO+ZvPjiiz6qndxpePjhhy+//PI0A6G5FQS0Cmoiv1ZkYEcQDRs2DCEQ2efrCnXFbDU7BF/huJA6qavUVbJ8RDju4uWltLQ0XgEnJFpZ9v3pp5+WWaP3tzabN28+fvx4eQE0IejcS1OroO6lGmhG9evXl9+rQF3sMb5BbbhGXSO3PUPwFbSLKWrK9er6LYq3HqsnLRdV9Sc4GjUBue96xRVXTJs2rV+/fpnFImW9w4YNmz59OkvgZgYwhF4mxUAoqG8D0aBBA99spTQkkiPCE/cd0KS6WG7eyq3plLkm+mRoV1SiKXtIvnPnzhMmTHj77bcHDRokqy4YWqpbt+7FF188a9as++67j01ADaFZ20w76ibya21WkQQW5nvQsmbsrepWWX0+jtU38sjzYfXwn9Sf2MEt9YXKXdzUfCw5e8wxx7z11ltff/31n//85/79++ueXsv3g9TxPvvss0uXLv3b3/4W5jKBloByMgy2oPNtWBs3buybLQND8orL0+rp6Wr6cDW8VMXmgdkKtUJWHZqpZhqkmPQm3MWN0RXQqlUrWQxBPrLJxOLFixcsWCDbu27fvr2kpET+EhK9DKdUIkbE7A/VpBgIBfVtHCPZU14eiF6sLr5OXddLRbAPcLrs5MHnnepOeZSbbsdktm/Tpk0yE4911vKAc9/dn1hnQfCGBLQKaiK/hj4S0iwSBRW2svT8tera/qr/ReoiayejMvWUO7fvqfcScjH4kmZUV5QvwWMEAkkgoH0OmoTk/c2xbdu2/ho0tyZ3dKUw53x1/kvqJdseLspTz5Fq5AXqAuTTfED3tIzwiko3VNpDwD0CJsVA2jmoeziCzijyGYO8VSkrs7+h3jhPnddH9clSEa9LLlVO76p3n1RPLlFLgobvnn15CCpFm+7lRUYQcImAdg5qIr8ugfCeiyUzhgVqgZTpyh3dCWqCzE2955WBBZkHv63elnmn7G+KfGYAULpYcjllFjy9IJAQAsxBfRvopk2byrxBCvB8s+jB0Hw1/zZ121PqqRPVifKItLaq7cFYGl3XqXXj1fiX1cvL1LI0utG0CgEWGa+ChAMQCJWASTEQCurnkHTp0mXSpEl+WvRma5Fa9IB64BH1yMHq4EFqkPw3VwUy4rIwgqzwIM9ip6qpcXxF1RvmQHqjoIFgxSgEfCWg/T41kV9fI3HBmG0KuoepLL8g8iY/MhPtrrr3UD3kp41q4/FBqSjll+rLT9Wn8iPL27I4n79XMArqL0+sQSAIAloFDcKZ8zZllS+bc9yoNu6RUgmyjqrTWXVupVo1V81bqBbyU+ObMCvVSnmouVgtlv/KFjEz1IxNapPN+cY6NhQ01sNH8A4QkGKgGneyQ0H9HOgYfevJsgayvoH87M2/QBUUq+IiVVSiSuQnW2XLiyhb1VaRSfmv/LCGrVKyYWcLpZorJVvxFCtVpFShUiW7GW5Taqv6dpV82WdmvVKLlFq8+x+ZfJo0aSKP1TPpSR8IQCBEAloFNZHfEOOMh6vevXvLbn87d8Zyq0sRSPlZq6yohLJmvEUjuyt1gFIddmtnuq+XyBxddHSeUh+rb7fT2WiYV58+fQxb0gwCEIiQgFZBI4wpvq5r1arVo0ePjz76KL4pEPluAi2VOmq3cHZSnmqvZM7acffPCUpVKDVXqWlKTdz9j1QfFDQVHc5BIBQCJsVAKKjPQ3HEEUegoD4zDc9cPaX6KTVw94zT94+8ey1m5WeoUguUGqfUeKVWVOtGrqJqj3MQAhCwikBWZWX1L90vX75cHsZYFet3wWRlKU3MNkT7wgsvnH766TZEQgzpEBBhG6LUYd5mnOk4/Lat/OrJ3d3nd09M//dTXFy8bt26cDZsTzdi2kMgKAL2fbGbiKB2TaKgMLluV3acl/3rXc/Spfx6KvUnpR5Q6shw5VMYynVyoFJ/Vup+peTB53eXzaGHHop8unSFkUtMCZgszIeC+jy4UkLZq1cMNhrzOe1Ymmuv1L1K3bn7eWe0H3lQeotSj+6JZPDgwdFGg3cIQMCQgFZBTeTX0EfSmh133HFJSzlu+UqBz693zzu72BR5692z4esPOmiQTVERCwQgoCWgVVBtD07URODYY4+tqQnnIyRwtFIjlPrJ3rumEYZSneujf/KT1Q88sKiiovoCheq6cAwCEPCfgEktLgrqP/fDDjustLTUf7tY9EpAXu68TiZ5Slk9OuvW7Rw2bPZxx32ycuV2rxnTHwIQCJKAVkFN5DfIwGJsOycn54QT5P0/PlYRaK3Ug0oNsCqmFMG8/fbqXr0+eO+9dSnacAoCEIiWgFZBow0r7t6HDpV3/vjYQ+CY3U89ZZ2EOH2WLCnv3/+jP/1pvsVvb8WJJ7FCIC0CJsVAKGhaSE0bDxw4sGFDWTqVjw0EzlbqGqUKbAgl3Rh27qy87rp5P//5jF27eCyaLjzaQyBwAloFNZHfwKOLrQN5n++UU06JbfjOBC6X9zClfhr3fEaM+Obkkz/bulXWBeQDAQhYRECroBbFGM9QzjjjjHgG7kzUeUr9Vqkfu5HPq6+uHDTo47Vrd7iRDllAwH4CJsVAKGhQ49i3b9+2bdsGZR27NRCQC/tGpfrW0CpWpydNWicFulu27IpV1AQLAZcJaBXURH5dBuM5N1nb78ILL/RsBgMZEJDl8S7bvcita58PPlg/ZMjn27dzO9e1kSWfmBLQKmhM87Eq7HPPPVe2C7UqpGQEc75Szi6M99prq84/fwbVucm4kskySgImxUAoaIAj1Lx5c9YnCpBv9ablTVzHXyV65pllN9zwZfXZcxQCEAiRgFZBTeQ3xDjj6uqSSy6Ja+ixjHv/3Qveuv+5/fav//nP1e7nSYYQsJuAVkHtDjs20Q0aNKhz586xCTfegZbsrh5KxG1zuYt73nlfLFvGsn/xvmSJ3mYCJsVAKGiwIyj1RFdffXWwPrD+HYErlWqWHBgrVmw/55zpLECfnBEnUwsJaBXURH4tzMfCkM4888xmzRL0zR7REPzIsXdXTDCOG7fm3nsXmbSkDQQgEAQBrYIG4SyZNvPz8y+99NJk5h5W1nWU+nlYvuzyc/PNXy5atM2umIgGAk4QMCkGQkHDGGqpJ2rUqFEYnhLqQ15fqZvM1Ddt2nXJJbOTmTtZQyByAloFNZHfyKOPSwC1atW65hpZ3JxPEAQ6Ovz2pwkvWfDvlVdWmrSkDQQg4C8BrYL66wZrv/rVr5o2bQqHAAjI6yuyCFGiP5deOpuFihJ9BZB8AARMioFQ0ADAV2eyuLh4+PDh1Z3hmBcCByslc9Ckf+RR6OOPf5N0CuQPgdAJZFVq1gdbvnx5kyZNQo/HwGFWltLEbNA5yibl5eXdunWbO3dulEG45vuvSvG67beD2rp10Zw5h+flJX067toFnpx87PtiNxFB5qDhXaEFBQV33nlneP7c93QA8rl3kOfP3/rUU0vdH3MyhEBYBEyKgVDQsEZjt58TTzxx4MCBobp02dmZLieXfm633fY1Cyykj40eEMicgFZBTeQ3c7cJ7nnPPfewYYsf47+PUj38sOOOjS+/3Pruu2vdyYdMIGA9Aa2CWh95XAPs2rUrCyz4MXj9/TDimo1nn13mWkrkA4GICFCLGxH4mtzeeuutrVu3rqkV51MTOCb16WSefeGFFdu2sf92MgefrCMgoJ2DmshvBPE64VIWWHjooYecSCWqJOQNFrmLy+eHBDZs2ClbcP/wKP8fAhAIhoBWQYNxh9XvCMjO27LiPDgyJdA3047u9xs1arn7SZIhBIInYFIMhIIGPw4aD/fddx97tmjY1Hi4V40tEttgwoQ1VOQmdvRJPGQCWgU1kd+QY3XMXYMGDZ588knZQNSxvIJPR3Zi2S94L3H1sGrVjs8/3xTX6IkbArEioFXQWGUR12Dl3dArr5R9ofmkRaA7C+Gm5jV+PO+0pCbEWQjUTMCkGAgFrZljoC1uu+22Xr24J5kWY3DVgGvCBBS0BkSchoAvBLQKaiK/vkSQcCOy//aoUaPq1k3o9pYZjT5LydeAbcqU9TW04DQEIOAHAa2C+mEcG0YE2rVr9+yzz/JA1AjWt41aGLdMaMOVK7evXbsjocmTNgR8ImBSDISC+gTbm5nBgwfffPPN3mwkpHcjpYoTkqqXNGfO3OKlO30hAAETAloFNZFfEwe0MSRw0003HX/88YaNE9yMCajR4M+evdmoHY0gAAEPBLQK6sEmXTMhIHdxn3nmGdlANJPOCerTMkG5ekh19mzmoB7w0RUCSpkUA6GgFl0pUk/0xhtvtGjBNCvFoDRMcY5TewksWrQNGhCAQNAEtApqIr9BB5dA+yKfIqKU5uqHvlB/ijP/S2Dz5l3ggAAEgiagVdCgHWNfR0Bu5L700kvylouuQbKPo6BG44+CGmGiEQT0BEyKgVBQPb/ozvTv3/+5557LycmJLgRrPaOgRkODghphohEEvBHQKqiJ/HpzTe9UBE466aR//OMf2dnaAUrV2eVzvMpiNLqbNnEX1wgUjSDghQBf0F7oBdv37LPP/tvf/sZKC8FSdtR6ZaWjiZEWBMIiYFIMhIKGNRoZ+fnFL37x2GOPcTv3e/DKMwKZuE7FxfxqJ27QSTh8AtpfMxP5DT/cBHq84IIL5D1RCov+O/S8pGH0S1BSwkN0I1A0goAXAloF9WKUvv4SGDJkiFTnFhUV+Ws2ntZQUKNxQ0GNMNEIAnoCJsVAKKien01nZMG/sWPHlpaW2hRUJLGw1I4R9jp1co3a0QgCEPBAQKugJvLrwS9d0ybQp0+fyZMn77fffmn3dKrDRqeyCSyZ+vXzArONYQhA4DsCWgWFkIUE2rdv/+9///uQQw6xMLawQloYlqN4++nUidd+4j2CRB85AZNiIBQ08mFKL4DGjRtPmDBh6NCh6XVzp/Vid1IJMpMOHUqCNI9tCEDgWwJaBTWRXxBGQkBKikaOHHnXXXcl8i2XpUrtjAR7vJx26MAcNF4jRrSxJKBV0Fhmk6Sgr7rqqrfeeqthw6TtVSJL7SxJ0jhnkmutWjktW7L8YSbo6AOBvQRMioFQ0BhfMAMGDPjoo4+OPPLIGOeQSehfZtIpSX169KidpHTJFQKREdAqqIn8RhY1jv9LoFWrVvJY9LbbbsvLS07t5SeMf2oCAwbw1lNqQpyFgD8EtArqj3msBE9AnoYOHz5cXnTp2LFj8N5s8PCxDUHYHMOAAfVtDo/YIBALAibFQChoLIay5iAPPPDAjz/++LrrrsvNdf5V+uVKST0Rn+oJFBfnHHpo3erPcRQCEPCVgFZBTeTX10gw5pWA1OjefvvtH3744UEHHeTVlu39mYZqR+jII+vl52t/r7XdOAEBCKRPgN+09JnZ3aNnz55yR1fedalVq5bdkXqJ7n0vnd3ue/LJjd1OkOwgEA4Bk2IgFDScsQjVi9zIlXdd5syZc/755zu6R7fMQdeFyjQmzmT2eeqpKGhMRosw409Aq6Am8hv/9F3OoFmzZiNGjJg6dWq/fv2cy1PeCp3gXFI+JDR4cIPS0uRUZftADBMQ8EJAq6BejNLXHgK9evWS113GjBkj/7AnKj8iGe+HEddsnHVWM9dSIh8IRETApBgIBY1ocMJ1e8IJJ8jaCy+++GLXrl3D9Ryct1lKLQjOehwtN2iQd8IJSVujKo4DRczuENAqqIn8uoMhAZlkZWWdfPLJn376qejo4Ycf7kTGzzmRhW9JXHFFq8JC7W+0b24wBAEI/JcAv2/JuhaksEh09P3dH/lHzOuM5FHoN8kaP3229erlXnJJS/15zkAAAukRMCkGQkHTY+pMa5mGymR09uzZV199teyYFs+8pJ5oZDwj9z/qyy5rVbeu84tp+M8NixDwQkCroCby68UxfW0g0K5duz//+c+LFi0aNWrUwIEDYzglHaeULFGU9E/t2jmXXcYENOmXAfmHT0CroOGHgseoCOTn55922mljx45dsGDBX//6V9nsJT5SKnuFPhoVN3v83nrrfvXr8xKLPQNCJC4QMCkGQkFdGGm/cmjRosWll146ceJEmZU++OCDgwcPLi62f6PmiUolercW2cts2DAmoH79EmAHAmkQyKqsrKy2+fLly5s0aVLtqYgPZmUpTcwRB+ai+23btomgvrn7M3PmTKtSLCgoOOSQQ4499tiOHY8ZMmT9jh3VX8lWxex7MPLbMGlS78MPZyl539FiMFwC9n2xm4ggChruVRJnbytXrpy0+yN1vLIPzI4dO8LPpn79+lIDJfeZ+/Tp07t3bxHRPTFcffXcu+5K4uuhF1ywz//8T+fwBwKPEPCZgH0KKgvRlJWVpU4TBU3Nh7PVE9i+ffuMGTM+//zz6dOnyzums2bNWrx48a5dUhzr56ewsFBqnbp06dKjRw9ZC6Jbt26tW7eu1sGWLbsOO+zDzz7bVO1ZVw+2aVM0bdohlOC6Or7JyiueCqotf5da3BrlN1kDTLbfIyDFR7IJjHz2HhNNlUKkr3Z/RE3lBsjq1atX7f7IP3bu/mzcuHFveylWqlv323uPsilbw90fealG/tugQYO2//3ss88+htRlU8xRo7r37v3Bxo0+q7hhAOE3k0Xkn3++G/IZPnk8QmAvAa2CwggCaREQTd1/9yetXj427tCh+NFHO59xxuc+2rTZ1B13tOvdu47NERIbBGJNgFrcWA8fwadNYOjQJgmpSv3JTxpdfnmrtAHRAQIQ8JWA9m0WE/n1NRKMQcAHAvfc0/7HP27kgyGLTRxySN1nnukqj434QAAC0RLQKmi0YeEdApkRyMnJGjmya9++9TPrbn+vLl1KxozpIc997Q+VCCEQawImC/OhoLEeYoKvhoCoy6uv9ujWrVY152J+qEWLgjffPKBRo/yY50H4EHCEgFZBTeTXEQak4RwBqVB9661eXbs6JaItWxa+/fYBIqLODRcJQSCuBLQKGteEiBsCuwk0a1bwr38deOihjizWI5XG7713UKdOJQwvBCAQDgGTYiAUNJyxwEsEBEpL88aNO+CYY0oj8O2rywMOqD1x4kH77lvoq1WMQQACXgloFdREfr06pz8EAiZQUpLz2ms9zzmnWcB+AjQ/aFCDCRMObNyYZ58BQsY0BDIjoFXQzMzRCwK2EZC1e558sssjj3QqLIzZ1S51xb/73X7//GfPOnVY+cS2y4p43CdgUgwUs+8U9weNDIMhcNFFzSdP7r3//vZv1vZd/k2b5o8de8CNN7bJzubFz2CuCaxCwDMBrYKayK9n7xiAQHgEevasPXXqwRde2Nx+TTrppMbTph169NHOvtUa3qjjCQJBEtAqaJBOsQ2BaAjI7dDHHuv0738fdOCBlq4oK7PkN9/s9dJL3WUOGg0jvEIAArsJmBQDoaBcLIkjIKvi/ec/vR96qGPDhnn2JC9FT3/8437Tpx/6ox81sCcqIoEABFIQYH/QFHA45TiBDRt2PvDAonvvXbRy5fYIU61VK+fXv2555ZWtKLiNcBRwHTEB+/YHlS0amzRpkhoLc9DUfDjrMgG5qXv99W2++qrPnXfu36RJBHdNZe2kG25oM3/+EbJVGfLp8qVGbjEkYFIMhILGcGAJ2VcCMgX8zW/2XbDgiFGjusm+Lnl5gde+SinTgAGlf/9750WLjvz97/dr0MCim8m+osUYBBwnoH3PTOS3rKzM8exJDwL/JVBQkH3aaU3kZ9WqHc89t2z06BVTpqwvL6/wkZC83ymrC51ySuOzzmrG8rY+gsUUBKIioH0OOmbMGEsV1L7b5VENHn4DJbB1a8XkyevefXfthAlrP/pog/zfDNzJeg6yS0y/fvXlp2/feqyNkAFDuiSCgH1f7CbPQbVz0ESMGUlCQE+gqCi7f/9S+ZEmlZVq4cJtc+dukZ85c7YsWVK+cePOTZt2yY+UI0kDqaSVu8G1a+fKo81GjfI6dChp375YXk1p3bpQpp56J5yBAARiTEA7BzWR32jytu9PlWg44BUCEICAMwTs+2I3EUEqiZy5AEkEAhCAAAR8I0Atrm8oMQQBCEAAAhD4AQHtHNREfqEJAQhAAAIQSCwBKokSO/QkDoG0CVRsrajctku65dTnHda06dEhXgRM1sVFQeM1pkQLgfAIiF5unrxu47/Wbf1807YvNpV/tbVyZ+Ve9yKiRZ2KCzvXqtWnbu1+9fNbF4UXGZ4gYAcBanHtGAeigIA1BCq3V6wbs2rNs8s2vLGqYpvpW7CFHYpLz2haenazgv2QUmvGMkaBxLMWFwWN0SVGqBAIloBo56rHliz784LtC7dl5ikrJ6v+kCbNftumsHNJZhbolVAC9imoybJC2kqihI4iaUMgqQQ2jFvzRdcpCy+ZnbF8CrnKXZUyeZ3RY8qiK+ZUbPr2iSkfCDhMQKug1OI6POqkBoHvE6jYsmvBBTPmHvNx+dwtvpCRx6Ur7l34Rad/b3pvnS8GMQIBOwloFdTOcIkKAhDwl0D5vC2zDvlw1ePf+GtWrG1fXD6n/0dyT9h3yxiEQAgETGpxUdAQBgIXELCUwJaPNsw+YurW6ZsCik8mo0uunbvw17Pk7m5ALjALgQgJaBXURH4jjBvXEICARwKbP1g/5+iPdizf7tFOjd1X/m3xgp/NUGhojaRoEDcCWgWNWyLECwEIpEFAbt7OK/t018aQin1WP7V0yfB5acRHUwhETcCkGAgFjXqU8A+B0AlUbN4l8rlzZeCzz+9ntuxP89c8syz0XHEIgQAJaBXURH4DjAvTEIBAYAQWDpu9bdbmwMxrDS+8eKZf5b5aH5yAQIgEtAoaYgy4ggAEwiOw4c3Vq//uf+WtSQJy03jBz2fyQNSEFW0iJ2BSDISCRj5MBACB8AjIKn0LL5kVnr8qnjb+a+3qJ5dWOcwBCMSSgFZBTeQ3lhkTNAQSTGDVI4vLv9waLYBvbvxSlg+MNga8Q8AXAloF9cU6RiAAAXsIiG4tvyv69Q22L9q2+ilKiuy5LoikegImxUAoaPXsOAoB9wisfXGFrBNkQ14r/rrQhjCIAQIeCWgV1ER+PfqmOwQgECaBNU/Z8gBSNhzd8snGMHPHFwSCIKBV0CCcYRMCEIiKwM41OzaMXROV96p+1z6/vOpBjkDAHgImxUAoqD3jRSQQCJDAxnFrZJXaAB2kaXrDW6vT7EFzCFhHQKugJvJrXTYEBAEIaAhseMeiCajEuOXTTSEviqQBw2EIZE5Aq6CZm6QnBCBgHwF59GhXUBWVW2dEsC6SXRCIxmICJsVAKKjFA0hoEPCPQPlsf3bP9i8iZWFIPmaHqSQQ0CqoifwmARA5QsABArKUvFQS2ZbIr2Y8JgAAGANJREFU9oXbbAuJeCCQFgGtgqZlhcYQgIDNBERBLQyvYouNUVkIipAiIWBSDJQbSWQ4hYBtBLapGRUq4uXuqjIpVgdWPZjBEVkON4NeQXep2GpjVEFnjX2XCGgV1ER+XQJBLgkn8JUaulV9bhuEA93exyTLNt7EA4H0CHAXNz1etIZAHAnk1NH+rRxhOjm1cyL0jmsIpCZgUgyEgqZmyFkIuEAguyQnK8e6GV+2lbruwniTQ1gEtApqIr9hBYkfCEDAE4GsvKz8VoWeTATQubBdcQBWMQmB8AhoFTS8EPAEAQgET6Cgg3VyVdDeupCCHwc8xIaASTEQChqb4SRQCHghUOvwel66+943p25uUddavpvFIATCJKBVUBP5DTNQfEEAAl4I1BlU6qW7731rH11f7i37bhaDEAiTgFZBwwwCXxCAQNAESnrXydunIGgv5vbrntDIvDEtIRA+AZNiIBQ0/HHBIwSiIJCdVTq0SRSOq/GZVZBd/5TG1ZzgEARiRUCroCbyG6tMCRYCSSfQ4NxmliCod2KjnHo2vqJqCR/CiAsBrYLGJQHihAAEDAkU9ahd9/iGho0DbJalml3fOkD7mIaAHwRMioFQUD9IYwMCMSHQ7Oa2KurynXonNRYtjwkwwoRAKgJaBTWR31SGOQcBCNhHQOqJor2Xm12Y3eKu/e0DQ0QQyISAVkEzMUYfCEDAegLN79g/p35eVGE2ubZ1QZuiqLzjFwLmBEyKgVBQc560hIALBPKa5u/7aKdIMik5tG6z37aJxDVOIRAEAa2CmshvEAFhEwIQCJpA/VMbNx7WMmgvP7Cf2zCv7ciurKIQMnbcBUpAq6CBesU4BCAQLYEW97Sv9+Pw1jTILs5p91rP/Nbcv4122PGeBgGTYiAUNA2gNIWAMwRks7M2I7vWGdQghIxEPtuO7l5ySN0QfOECAmES0CqoifyGGSi+IAABfwl8Oy8c06P0zKb+mv2BtdzSvPbjDqh7XBhSHWgiGIdAVQJaBa3alCMQgIBjBLLys9s83bX5He2ycgN5S7T4oDodPzy45DBmn45dOIlIx6QYCAVNxKVAkhDQEshSTa9t3f7dAws7lmjbpH9CJLnpNft2nHRQQVuefaaPjx4xIaBVUBP5jUmOhAkBCNRAoFafep0/PaT5H/fLqZ1TQ1OD07X71e/08SHN/7S/rCBv0JwmEIgrAa7vuI4ccUPAXwJyR7fp9W26zj+i2U1tcxvnZ2I8O0tKk2Q6237CgUXd2D07E4T0sYeASTEQ2yPYM15EAoHoCUjhzz63tm12U5uN49asfXHFxglry+dtSR1WdkmOTGHr/KhB6ZAmec0t2oI0ddichYB3AloFNZFf7+6xAAEIWEhA3nURRZQfiW3HkvKtn2/a+sWm8q+2VmzcVbGtQg7m1M/NrZ9X2KmksHNJcc/arJNg4SASUggEtAoagm9cQAAC9hOQaaX81DmW11HsHysi9JOAFAOVlZWltshz0NR8OAsBCEAAAhConoBWQanFrR4YRyEAAQhAAAK7CWgVFD4QgAAEIACBxBIwKQZCQRN7eZA4BCAAAQh4IqBVUBP59eSZzhCAAAQgAIE4E9AqaJyTInYIQAACEICAJwImxUAoqCfEdIYABCAAgcQS0CqoifwmlhqJQwACEIAABLQKChoIQAACEIBAYgmYFAOhoIm9PEgcAhCAAAQ8EdAqqIn8evJMZwhAAAIQgECcCWgVNM5JETsEIAABCEDAEwGTYiAU1BNiOkMAAhCAQGIJaBXURH4TS43EIQABCEAAAloFBQ0EIAABCEAgsQRMioFQ0MReHiQOAQhAAAKeCGgV1ER+PXmmMwQgAAEIQCDOBLQKGuekiB0CEIAABCDgiYBJMRAK6gkxnSEAAQhAILEEtApqIr+JpUbiEIAABCAAAa2CggYCEIAABCCQWAImxUAoaGIvDxKHAAQgAAFPBLQKaiK/njzTGQIQgAAEIBBnArlxDp7YIeAbgfrq1BJ1mG/mMAQBCMScgBQDlZWVpU4CBU3Nh7NJIdBM3ZSUVMkTAhDwiYD2Li61uD4RxgwEIAABCLhJQKugbqZLVhCAAAQgAAEDAibFQCioAUiaQAACEIAABKoQ0CqoifxWscYBCEAAAhCAQFIIaBU0KQDIEwIQgAAEIFCFgEkxEApaBRsHIAABCEAAAgYEtApqIr8G9mkCAQhAAAIQcJOAVkHdTJesIAABCEAAAgYETIqBUFADkDSBAAQgAAEIVCGgVVAT+a1ijQMQgAAEIACBpBDQKmhSAJAnBCAAAQhAoAoBk2IgFLQKNg5AAAIQgAAEDAhoFdREfg3s0wQCEIAABCDgJgGtgrqZLllBAAIQgAAEDAiYFAOhoAYgaQIBCEAAAhCoQkCroCbyW8UaByAAAQhAAAJJIaBV0KQAIE8IQAACEIBAFQImxUAoaBVsHIAABCAAAQgYENAqqIn8GtinCQQgAAEIQMBNAloFdTNdsoIABCAAAQgYEDApBkJBDUDSBAIQgAAEIFCFgFZBTeS3ijUOQAACEIAABJJCQKugSQFAnhCAAAQgAIEqBEyKgVDQKtg4AAEIQAACEDAgoFVQE/k1sE8TCEAAAhCAgJsEtArqZrpkBQEIQAACEDAgYFIMhIIagKQJBCAAAQhAoAoBrYKayG8VaxyAAAQgAAEIJIWAVkGTAoA8IQABCEAAAlUImBQDoaBVsHEAAhCAAAQgYEBAq6Am8mtgnyYQgAAEIAABNwloFdTNdMkKAhCAAAQgYEDApBgIBTUASRMIQAACEIBAFQJaBTWR3yrWOAABCEAAAhBICgGtgiYFAHlCAAIQgAAEqhAwKQZCQatg4wAEIAABCEDAgIBWQU3k18A+TSAAAQhAAAJuEtAqqJvpkhUEIAABCEDAgIBJMRAKagCSJhCAAAQgAIEqBLQKaiK/VaxxAAIQgAAEIJAUAloFTQoA8oQABCAAAQhUIWBSDISCVsHGAQhAAAIQgIABAa2CmsivgX2aQAACEIAABNwkoFVQN9MlKwhAAAIQgIABAZNiIBTUACRNIAABCEAAAlUIaBXURH6rWOMABCAAAQhAICkEtAqaFADkCQEIQAACEKhCwKQYCAWtgo0DEIAABCAAAQMCubo2Ir9lZWW6sxyPPYFN09W691T5YrVtsarY6ls62cWqaF9V0FyVDlRFbX0ziyFrCCwp3/G7r76xJpzvAjmqfu0zm5baFhXxOE9Aq6DOZ57QBEUsFz+kFj+qtswOnEDtXqrFr9Q+56ksLrPAYYfmYM2OnY8uWRWaO0NHOVlZKKghK5oZEjApBuIuriFMJ5qteFm9307NuSoM+RRgG6epmT9Xkzurtf9yAh9JQAACEPg/BLQKaiK/sIwTga//oD47RZWHfv9ty1z18TFqyf/EiRWxQgACEDAgoFVQg740iQ+B+XeoL29UqjKaiCt3qJkXqeXPReMdrxCAAATSJ0AtbvrMnOwhN2/nXR91ZpVqxgVq46dRh4F/CEAAAr4R0M5BTeTXtygwFBwBmf/N/U1ks8/v57Vry+5I+EAAAhBwhIBWQR3JjzSWPqm2fmULhjXjlPzwgQAEIGA9AZNiIBTU+mH0GODSZzwa8Ln70qd8Nog5CEAAAhER0CqoifxGFDNujQnsWKPWTTRuHUrDVa+pyp2heMIJBCAAgWAJaBU0WLdYD4fAxk9U5a5wXJl6EVHf+rVpY9pBAAIQiIiASTEQChrR4ITjdvuycPyk56V8aXrtaQ0BCEDASgJaBTWRXyszIqjvEdi53kYc25fbGBUxQQACEEiTgFZB07RDcwgYE5AXbPhAAAIQsJuASTEQCmr3GBIdBCAAAQjYSkCroCbya2tSxAUBCEAAAhAInIBWQQP3jAMIQAACEICArQRMioFQUFtHj7ggAAEIQMBuAloFNZFfu1MjOghAAAIQgECABLQKGqBPTEMAAhCAAATsJmBSDISC2j2GRAcBCEAAArYS0CqoifzamhRxQQACEIAABAInoFXQwD3jAAIQgAAEIGArAZNiIBTU1tEjLghAAAIQsJuAVkFN5Nfu1IgOAhCAAAQgECABrYIG6BPTEIAABCAAAbsJmBQDoaB2jyHRQQACEICArQS0Cmoiv7YmRVwQgAAEIACBwAloFTRwzziAAAQgAAEI2ErApBgIBbV19IgLAhCAAATsJqBVUBP5tTs1ooMABCAAAQgESECroAH6xDQEIAABCEDAbgImxUAoqN1jSHQQgAAEIGArAa2CmsivrUkRFwQgAAEIQCBwAloFDdwzDiAAAQhAAAK2EjApBkJBbR094oIABCAAAbsJaBXURH7tTo3oIAABCEAAAgES0CpogD4xDQEIQAACELCbgEkxEApq9xh6jC4rx6OBQLrn1A7ELEYhAAEIhEtAq6Am8htuqHhLn0Beo/T7BN+joFnwPvAAAQhAIHACWgUN3DMOQiBQ2CoEJ2m7KGyZdhc6QAACEAiXgEkxEAoa7piE7K12T5XXMGSfNbir1V3lN6mhDachAAEIxIGAVkFN5DcOCSY7RnkO2ugEuxA0OtGueIgGAhCAQKYEtAqaqUH6WUag5eUqy5pRzqmlWv7KMkCEAwEIQKAaAibFQNZ8t1YTP4f8IFC7h2oy1A9DfthofY3Kb+qHIWxAAAIQiJ6AVkFN5Df68InAhED7e1VRG5OGwbape7ja99pgXWAdAhCAQIgEtAoaYgy4CphAfiPV4/+pvPoBu0lpXupvu49S2fkpG3ESAhCAgC0ETIqBUFBbRivYOGp1U70nq+L9g/Wis16nt+o9RRU0153nOAQgAIE4EtAqqIn8xjHh5MZc3EEd/KFqfa3KKQkPQl6pane7OuhfqmCf8JziCQIQgEAoBHJD8YITOwjk1lXt7lCtrlDLnlHLX1QbpqjKikAik7do6h+tGp+img5VufUCcYFRCEAAAkESMCkGQkGDHAE7bcuCBq2u/PZH5HPHKrVrk89h5taxbhkHnzNMtLl2xQVTD+5kG4JG+XyV2TYm1cRTXl45a+62WXPLZ8zetmz5jvUbdm3aXLHnZ2o1zWNwSHvZmchvDPIjxBQE5D3R/MZKyQ8fCJgSKMrOPrBOsWlr2iWegEjmu5M2yc9Hn279esH2XbsqXUKiVVCXkiQXCEAAAhAIjYDMNV99c/3Lr60X4Vy6fEdofv11JMVAZWVlqW2ioKn5cBYCEIAABIwIVFaq9yZvenrU2lGvrJM7tEZ9Yt5Iq6Am8hvz3AkfAhCAAAR8ILBjR6UI5x33Lp/zZbkP5uJjQqug8UmBSCEAAQhAIBoC27dX/mPkGtFOecYZTQSBeTUpBkJBA8OPYQhAAAJOE3jupbXX3rJ04WLXtNN80LQKaiK/5m5oCQEIQAACzhCQ11EuuWbxhPf8fhcuboC0axLFLRHihQAEIACBwAls2Vpx9U3f9Ow723n5NFmYTzsHDXwccAABCEAAArEiIC93nnbe/Okzt8Uq6gCD1c5BTeQ3wLgwDQEIQAACNhF45oW1vfvPQT6/PybMQW26QokFAhCAgH0E5GWVYdcufuQfq+0LLcCITIqBUNAABwDTEIAABOJOYPOWCrlz+89xG+KeSBDxaxXURH6DCAibEIAABCBgCYHVa3YeP+SrDz7aYkk8toWhfQ5qW6DEAwEIQAACYRJYsGj7EcfNS6x8mhQDaeegYY4TviAAAQhAwCoCK1ftHHjSl/O+StYqfekOgXYOaiK/6TqjPQQgAAEI2E9A1oWXm7fIZ40jpVXQGnvSAAIQgAAE3CMgS92efO7XH05L+rNPk2IgFNS965+MIAABCGRO4OeXLxo/MenL9Rni0yqoifwa+qAZBCAAAQjEgsBjT65+8rk1sQjVhiC1CmpDcMQAAQhAAAKhEfh8xrbLhy8JzZ3ljkyKgVBQyweR8CAAAQiEQUBWTjj9Z/Nl4fgwnLniQ6ugJvLrCgTygAAEIJB0Atfc/I0sHJ90Cmnmr1XQNO3QHAIQgAAE4kpg6rQtSVv2tsahMikGQkFrxEgDCEAAAi4TqKhQv/rN4l27Kl1OMpjctApqIr/BhIRVCEAAAhAIj8CjT6zi7c/McGsVNDNz9IIABCAAgRgR2LS54oY/LotRwKGFalIMhIKGNhw4ggAEIGAdgb+NWCUbsFgXVkwC0iqoifzGJEfChAAEIACBaghs3VZxz99WVnOCQ2YEtApq1p1WEIAABCAQVwKPP71m2YodcY0+4LhNioFQ0IAHAfMQgAAErCSwc2fln+9bYWVosQlKq6Am8hubLAkUAhCAAAT+L4Fx/9q4cPF2qHghoFVQL0bpCwEIQAAClhN46vm1lkcYbXgmxUAoaLRjhHcIQAACERDYuKnildfXR+DYLZdaBTWRX7dQkA0EIACBpBB4acw6FpH3PthaBfVuGgsQgAAEIGAngdGvrrMzMHuiMikGQkHtGS8igQAEIBAGAVkCd+K/N4fhyXUfWgU1kV/X4ZAfBCAAAQcJyCq4GzbucjCx0FPSKmjokeAQAhCAAATCIDDhvU1huIm5D5NiIBQ05oNM+BCAAATSJICCpglM21yroCbyq7XKCQhAAAIQsJUAe5n5NTJaBfXLAXYgAAEIQMAeAstX7ly3noegNQ+ISTEQClozR1pAAAIQcIbA7LnbnMkl8kS0Cmoiv5FHTwAQgAAEIJAWgZlzytNqT+MUBLQKmqIPpyAAAQhAIKYE5sxjDmo0dCbFQCioEUoaQQACEHCDwLIVO91IxIYstApqIr82JEAMEIAABCBgTmDT5grzxrRMTUCroKm7cRYCEIAABOJIYPMWFNRo3EyKgVBQI5Q0ggAEIOAGgc3MQf0bSK2Cmsivf2FgCQIQgAAEwiCweQsvg/rGOdc3S2EaysoK0xu+IAABCDhD4DNnMgk4ESkGKisrS+0khgpaWZk6Jc5CAAIQgAAEQiCgvYtLLW4I9HEBAQhAAALxJaBV0PimROQQgAAEIAABjwRMioFQUI+Q6Q4BCEAAAgkloFVQE/lNKDPShgAEIAABCCilVVDgQAACEIAABBJLwKQYCAVN7OVB4hCAAAQg4ImAVkFN5NeTZzpDAAIQgAAE4kxAq6BxTorYIQABCEAAAp4ImBQDoaCeENMZAhCAAAQSS0CroCbym1hqJA4BCEAAAhDQKihoIAABCEAAAoklYFIMhIIm9vIgcQhAAAIQ8ERAq6Am8uvJM50hAAEIQAACcSagVdA4J0XsEIAABCAAAU8ETIqBUFBPiOkMAQhAAAKJJaBVUBP5TSw1EocABCAAAQhoFRQ0EIAABCAAgcQSMCkGQkETe3mQOAQgAAEIeCKgVVAT+fXkmc4QgAAEIACBOBPQKmickyJ2CEAAAhCAgCcCJsVAKKgnxHSGAAQgAIHEEtAqqIn8JpYaiUMAAhCAAAS0CgoaCEAAAhCAQGIJmBQDoaCJvTxIHAIQgAAEPBHQKqiJ/HryTGcIQAACEIBAnAloFTTOSRE7BCAAAQhAwBMBk2IgFNQTYjpDAAIQgEBiCWgV1ER+E0uNxCEAAQhAAAJaBQUNBCAAAQhAILEETIqBUNDEXh4kDgEIQAACnghoFdREfj15pjMEIAABCEAgzgS0ChrnpIgdAhCAAAQg4ImASTEQCuoJMZ0hAAEIQCCxBLQKaiK/iaVG4hCAAAQgAAGtgoIGAhCAAAQgkFgCJsVAKGhiLw8ShwAEIAABTwS0Cmoiv5480xkCEIAABCAQZwJaBY1zUsQOAQhAAAIQ8ETApBgIBfWEmM4QgAAEIJBYAloFNZHfxFIjcQhAAAIQgIBWQUEDAQhAAAIQSCwBk2IgFDSxlweJQwACEICAJwJaBTWRX0+e6QwBCEAAAhCIMwGtgsY5KWKHAAQgAAEIeCJgUgyEgnpCTGcIQAACEEgsAa2CmshvYqmROAQgAAEIQECroKCBAAQgAAEIJJaASTFQbgo6Y8aM2TsT3WuLI0IMGgKBK4ErYc+3B1cCV4KTV0IKcdx7KquystKkHW0gAAEIQAACEPg+Ae7icj1AAAIQgAAEMiGAgmZCjT4QgAAEIAABFJRrAAIQgAAEIJAJARQ0E2r0gQAEIAABCKCgXAMQgAAEIACBTAigoJlQow8EIAABCEAABeUagAAEIAABCGRCAAXNhBp9IAABCEAAAigo1wAEIAABCEAgEwIoaCbU6AMBCEAAAhBAQbkGIAABCEAAApkQ+P/BHetI5+UopAAAAABJRU5ErkJggg==" width="400" /><br />
Same as above, but without the two circles on the outside:<img alt="" height="396" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAggAAAIECAIAAADzXEFPAAAAA3NCSVQICAjb4U/gAAAgAElEQVR4Xu2dB5gVRdaGz+TEAEPOgpIzEkRRRBBcCQZcl+zyq2taETEhZt01rXFdXRTEXSNGdAUzAioIBkAESQqSc54hDsz8R8ZFdqjbt++91d11ur7rPDxjdXXVOe/pqe921+mqpOLiYsIHBEAABEAABP5LIBkoQAAEQAAEQOBoAhAGXA8gAAIgAAL/QwDCgAsCBEAABEAAwoBrAARAAARAIDKB1MiHiJKSnI7iGAiAAAiAgEQC0XKOHIWBHY52vkQmsBkEQAAE7CXg4hs/5hjsvTzgOQiAAAgoCUAYlFhQCAIgAAL2EoAw2Bt7eA4CIAACSgIQBiUWFIIACICAvQQgDPbGHp6DAAiAgJIAhEGJBYUgAAIgYC8BCIO9sYfnIAACIKAkAGFQYkEhCIAACNhLAMJgb+zhOQiAAAgoCUAYlFhQCAIgAAL2EoAw2Bt7eA4CIAACSgIQBiUWFIIACICAvQQgDPbGHp6DAAiAgJIAhEGJBYUgAAIgYC8BCIO9sYfnIAACIKAkAGFQYkEhCIAACNhLAMJgb+zhOQiAAAgoCUAYlFhQCAIgAAL2EoAw2Bt7eA4CIAACSgIQBiUWFIIACICAvQQgDPbGHp6DAAiAgJIAhEGJBYUgAAIgYC8BCIO9sYfnIAACIKAkAGFQYkEhCIAACNhLAMJgb+zhOQiAAAgoCUAYlFhQCAIgAAL2EoAw2Bt7eA4CIAACSgIQBiUWFIIACICAvQQgDPbGHp6DAAiAgJIAhEGJBYUgAAIgYC8BCIO9sYfnIAACIKAkAGFQYkEhCIAACNhLAMJgb+zhOQiAAAgoCUAYlFhQCAIgAAL2EoAw2Bt7eA4CIAACSgIQBiUWFIIACICAvQQgDPbGHp6DAAiAgJIAhEGJBYUgAAIgYC8BCIO9sYfnIAACIKAkAGFQYkEhCIAACNhLAMJgb+zhOQiAAAgoCUAYlFhQCAIgAAL2EoAw2Bt7eA4CIAACSgIQBiUWFIIACICAvQQgDPbGHp6DAAiAgJIAhEGJBYUgAAIgYC8BCIO9sYfnIAACIKAkkKosNbZw48aNJbZ9/fXXJb906NABJaDB1wCuBIaAKyGOK6FPnz7GjnhBGZZUXFwcse+kJHI4GvE0Dw+wMFStWtXDDiQ3vXv37q1btx48eDCSE1lZWZUqVUpLS4tUAeUgYBsBG4cUFwO7sDsG267aY/3dsmXL8qM+a9as4Sub9YDL9+7de2z9Y0vKlStXpUoVVoiKFSse/78fVo5j66MEBEDANgIQBqMjvn///vnz58+bN4//XbBgAf/CApCgxTsPf3788cdS7SQnJ7NMtGrVqnnz5i1atOBf6tevn2BfOB0EQEAiAWGPkiZOnBj6B4Lr1q2bMWPGzJkzv/rqq9mzZ7M2BHVhVa5cuWPHjieddFKnTp3at2+fk5MTlCXoFwQ8ImDDkFIaHR4llSZi6v8XFRWxDPA1+t57733//feGmLl582Y2iT9sT2ZmZufOnVmVe/bsyTcWhlgIM0AABLwggEdJXlB12+aBAwc++eSTt956a9KkSTwKuz0tiHr79u37+PBn2LBhjRs37tu374UXXti6desgbEGfIAAC3hIQ9igpHCkEhYWFH3744Ztvvvnuu+/u2LHD2wh72XqDBg1YHvr169eyZUsv+0HbIOAVgXAMKbHRcfEoCcIQG9IEa//000/PPvvsv/71r02bNiXYlFGn8wzEn/70pwEDBpQpU8Yow2AMCDgTgDAo+UAYlFg0Fx46dGjChAlPP/301KlTnV4c0dyt382xKgwcOPDKK6/EIya/0aO/eAlAGJTkhC2JceTFTqUzBhbyS2f/+Mc/GjZs+Ic//GHKlCkhVgWGX1BQMGbMmDZt2nTv3v2jjz4Kt7MGXmwwKQ4C4oaUOHyM4xRMPscBzdUpPJn8+OOP813Ctm3bXJ0QokqTD3/4ZYgbb7yR7yFSUlJC5BxciZkAJ1nwQ9QjL2BmZ2fzKzJ4Az9mjj6eAGHQD5uV4JFHHnniiSf4G7T+1uW0yC/lXXTRRffdd99dd93Fc9T8Ap0c22FpogRYBjjR+dNPP50+ffrSpUtLrdTCqtCoUSNOgO7WrVuvXr0yMjIS7Q/n6yXA9/sRP8RLJZn12bBhg1kG/a81+fn5PAjymhN6YxSC1vju4e233zY5drBNF4Gff/75sssuc5+GUKFCheuuu47f69RlQEztGD6kxOSL28ouBnbHod/F+W5N0VTP2Cjy9PK4ceOqV68egkHcOxe6dOkyZ84cTdcCmjGOAH8xuuGGG9LT0+O4hPgNyttvv33Pnj0+e2XskOIhBxcDO4RBA3/ONeIZ1zj+GCw8hR8oXXzxxUF9PdQQbDQRgcDnn39eu3btBC9pfqn+m2++idCDJ8UQBiVWYY99TUsh4NcReHL1jDPOmDt3boJ/Epaczot/PPfcc5ymxcla/LslXofeTZ5R69q16+rVqxP0lBcOPvXUU/nmO8F23J9u2pDi3nJPawoTBk9ZxNr4888/37Rp0/Hjx8d6IurztPw111zDa/PxkrGgIZ3AnXfeOXz4cIeNQGJykFeN5JclOWchprNQWS8BCEM8PHl6rUePHkOHDuWNEOI5H+ccJjBr1qy2bdvysBLgCrIIRYIERo8efc899yTYSKnT+eHGrbfeyi3rbRatxUBA+YDp10IXcxROp3twzIQHgvwkJDc3NwbEqBqNAL8p/cMPP3hwvaBJbwnwkl/evaTCKa285LC3DhQXmzCkeO1j6fZdDOy4Y4g2aB11nO8Pfv/73/PcKWdfxHAaqkYj8N1337Vr145nHfgKjlYXx00hwBkEgwcP5nw8jwzitSb79+9v+ctAHrGN2iyEISqiXytw6hGvIcpLZLs9IXK9DMrIo7waVKMBNWhNrU+kE1tSS/69OlUvT+X5aORTQ3uE34fiWYezzz7b8OXHQxuA2B3jeYXE9xN07paf2fKTRuc6OOoFAWGL6AW13RK/yXzzzTfHN71Whso0p+Z1qE5NqlmLavFPBargHMvNtHktrV1Da/jfVbRqAS3YQ3ucTwnN0Tp16vCCgzz3EBqPQukIJ6eefvrpPrjGj6oWL17s3S6zQQ0pPqCL2IWLZbexJEZEeiUH+I2bSy+9NNbUo3RKZzFoQ234hqAhNUym2O7MKlNl/uFzS2w4RIcW0+I5NId/+JeDdDCK0ZIPr1q1ihMWn3nmGV5OQ7IfIbf9/vvv98dDflT14IMPjh071p/u0EsJAdwxOF0JPEidc8458+bNc6p01LEkSuKHQj2ox2l0WhZluTwrpmr5lD+Vpn5Kny6khTGdKK7y1Vdf/dhjj6Wm4ruLcaFbuXJlvXr1fJsQysrKWr9+vUcrzeCOQXl5CRMGPxdP5zdfzj33XE5aUIIrVchf8HtST5aEKlTFTf3E66yjdZ/QJ+/Re9tpe+KtmdkC5wS//vrrHo0IZroswqq//e1vI0eO9NNUvoPk9Ze86NHPIcUL++Np08WjpNgeccRjhMxzeN9NXtjHjSrwzMH1dP0L9MJgGuybKjBUnrv+I/3xJXppGA2rSlVlYo5iNW8x3bFjx2XLlkWph8P+EuDNNvztkN5//32fe7S8O9wxKC6Ahx56iL8QRb1TPo6OG0JDOlNnfoKkaMXHIp6E4OdLLBI8We1jtz51ValSpXfeeYdfk/apP3TjSICzSPke7sjmCo51tR3My8vzaF8T3DEogyTsjsGHhU34lcubbrrJWRUyKfNSuvQZeuZ0Oj1wVeC4plDKmXTmWBp7EV0UvmxXzonkZ0q884/yCkahzwQ4R8hnVWAHt2/fvnatJ196fBhSfA6Qlu6ECYMWnyM1wmLA68JHXaTlZDp5HI3rR/14OI7UVCDlaZTGdzBjaExbCluuJ+eG9enTZ9KkSYGARadHE+D9lwIBwnvABdKvnZ1CGH6NO6sC72LPaTAO1wG/kXAH3XEP3ePnXIKDPcpDPPfwAD0wkkZ6lBal7NSHwn379l1wwQVvvPGGD32hCwcCO3fudDjq3SG+afCucbRcioAwYejQoYMXIWRVuOqqqzjzwaFxfh1hNI3mPFSHOuYc4idLT9FTdamuOSYlbglvHTxgwIB333038abQgjgCu3fv9sJmj4YUL0z1s01hwuARGp5UePrppx0aP5fOfZwer0bVHOqYdqg21X6SnuQMWtMMS8Qeft2pX79+U6ZMSaQRnCuRAK+pJ9FsoTZDGIgXDX744YcjxY8nEm6kG6+mq/kJfqQ6xpbzRDQbz/mssb56baxHbBg/Uzr//PN53U2TjQyxbUEN0Hidxc+LSpgwaE8h4DXfHVbp4oH1LrpL+pfuc+icW+lWicIW6S9h165dPBcd1CxoJKssKW/cuHEgntatW9eLfrUPKV4Y6X+bwoRBLyB+Wj1s2LBIbeZS7t/obx2pY6QKgsr5ZYv76L5syhZks7OpvAhrz54916xZ41wNR7UTaNGiRRK/Ouvvh29TeDtof/u0ujd7hYGfRfBMZqTV5MtRucfosabUNDRXBy/J9wg9kkM5ofGIVYG1IagkmdBgjNURfqTTvHnzWM9KsH779u2DeoSVoOVCTxcmDLpSCDgnmp9FcHa8Mmz8BOmv9Fd+sVl5VG5hfarPT8ZSKTzL0vHTpL59+3K2ktygSLS8a9euPpvdrVs3j3rUNaR4ZF5QzQoTBi2YeE8oHk0ibQjD4+Zf6C+NKZgHqVocdGiE7xtup9vDNBfNGUo33HCDg8s4pJ0A72OovU3nBgcNGuRcAUf1ErBOGPiVhaFDh0aat+T1LW6gG3gfBb2UjWrtFDqF85SMMilBY3hP0BdffDHBRnC6ewK8bpV3O+ccawbvCNSoUaNjy1HiHQFhwpB4CsG9997rsD3nH+gP3cirm1bvohhry72pdy/qFetZJte/4ooreONoky0Mk208+eznXRq/ZuQdvcSHFO9sC7BlYcKQIClevPeuu+6K1EgzavZ/9H+Rjoas/Eq6MkzvRfN0ES+Y4dECnCELvRZ3+La7QYMGWppyboS38+OdwJ3r4Kh2AhYJA2+uwFdzpDSkslT2FrrFtHXxtMf7SIM8wc7+hmkp1uXLl/O6Jt4RQ8tHE8jIyHj88ce9ZsIbPvNzQv+zY732y/z2hQlD3CkEPLVw8cUXR5pw5jjxG8ImL43nxZVUj+pdTpd70XJQbb722muYbPANPucK8/arnnZ39913t279687nHnUU95DikT2GNCtMGOKm9tRTT33wwQeRTu9CXcLxIlskByOV82RDc/I7Jz2SMVrKeahasWKFlqbQSFQCjzzyiHepq5xQfvPNN0e1ARW8IGCFMPDWIg7zV/w+cMi+OLu/UDgL61q6NkxvNvBqGUOGDCkqKnIPATXjJpCenj5hwoQ2bfRn8Z1yyimvvvoqP0qK2zacmAgBYcIQRwoBP0S69NJLHfac4s1tKlGlRCCKPpff47uQLhTtQinjp0+fzktghckjk33hF6GnTZt2xhlnaDSyd+/en3zySXa2Hyu4xDGkaPTU2KaECUMcHHk97RkzZkQ6kTNzzqfzIx21pHwQDapO1cPk7KhRo7CMkm8BLVu27EcffTRixIjEZ4mTk5Nvv/32t99+2x9V8A2RuI5CLgy8TyyPEQ5R+SP90Z5MpEgcODdpIA2MdFRieX5+/p///GeJlgu1mRcyevTRR1keEslh5SWYpk6dysvgp6aGZ9UWoQEVJgyxphBcc801DouscVpOJ+okNHJ6zeYd3ypTZb1tBtsaL5375ptvBmuDbb137959wYIFnMZarVpsW1rVq1eP90+cO3du586dfYYW65Dis3lBdSdMGGLC9PHHH/PMmMMp/DWZZ18dKthziOefQzbTwLG7/vrrI62TaE9kffaUp6OHDx/OiWG8vgAvqVSxYkUHA6pUqdK/f//33ntv6dKll112GW4UHFj5fCiJ52YjdsmrrjscjXiahwc2btxYtWpVNx0UFha2bNmS85EiVeadL8fROAjDET77aT9PNuykYLZ6jxSmBMv5gTU/mkiwEZweNwFOD1u0aBHfRvBfIr+XzrvvZWVlVahQoUmTJvzgiPf8SXxaIm7bSk50P6Qk2JFBp7sY2IUJw8SJEzm72Q1izrB2Xs6FF5Ljrc3cNGVPnefoufE0Pkz+ZmZmLly4kJ9UhMkp+KKRgPshRWOnATflQhjC+SiJvwU4f0/kJyen0+kBh8e87nmmwTyjErKIv6Jed911CTWBk0HAPgLhFIb777+fX3RyiGY7asd7tDlUsPNQHaoTvo0o3nnnHeSq23k9w+u4CQgTBjcpBOvWrRszZowzERvW1nYmEOlod+oe6ZDccocldeU6Bcu1EHAzpGjpSFYjwoTBDdwHHnjA4T1nbiGLsk6mk900ZWGdM+iMMK2QURJBXiZr1qxZFkYTLoNAfATCJgz8RtvYsWOdWbSklmFabtrZ2ViP5lJu+J4mMQTcNMR6JaC+zQSECUPUh8UPPfQQzzc6R5T3PXauYPnRUPLhl3K/+uoryyML948lEHVIOfYUG0qECYNzSHbs2DFu3DjnOnw0lANfVK/dVwjrlte8ZoN7CKgJAjYTCJUw8Hp5BQUFzuHkndpOoBOc61h+tAk1CeWjNn4NftmyZZYHF+6DgBsCwoTBIYXgwIEDTzzxRFSfeV8avO3sTCmN0kI5zXDw4MG///3vzr7jqG0EHIYU21Ac7a8wYXAIFW/rsX79eocKJYd4+4GodVCB1wsJJYR//etf27dvD6VrcAoENBIIjzDwcyQ3XPgdLjfVLK9Tk2qGkgA/aXz++edD6RqcAgGNBIQJQ6QUgh9++GHmzJluuNSiWm6qWV4nrMLAYY368qPlobfN/UhDim0cSvkrTBgiRctNMlLJubhjiMTw6PIaVMNNNYl1eLHPL774QqLlsBkEfCMQBmHgaecXX3zRDbLyVD6b/NhI1o0xJtcJ8R0DY+c9YUyGD9tAIHACwoRBmULAG31s2bLFDUpeDMNNNdThVTHSKT2sHHhnN+c1FsPqOPw6loBySDm2mm0lwoRBGZ7x491uIZBJmcoWUHgsgRzKObYwHCX79+/nVfjD4Qu8AAEvCIgXBt72ne8YXKIJ8WDnkoD7auFmhe2g3V8JqGkhAWHCcGwKAX/1c7+vL+4Y3F/i4RaGDz/8kL9SuKeBmmElcOyQElZPY/IrNabaBlbm99rcW5VCKe4rW14zxHMMHFleaXHSpEkDBgywPMqi3V+1ahWvcbJy5Up+aZEzUHJycng36fr16zds2LB8+fKiXQvceNnCwPsuTJ482T3EIipyX9nymgfoQLgJQBgkxre4uJizjXlake/5VqxYoXQhJSWFp5R79eo1ZMiQOnXwQqsSUpRCYY+SSqUQTJkyxXlPnlLe76W9UXjg8H8J7Kbd4YbBI8uhQ4fC7WOYvON7As4zbtSo0emnn87LHERSBXaZw8qvu95222316tX73e9+9+mnnzpwQFaSEo4wYSjlAy+yr/QqUuEe2hPpEMpLEQg9q23btn3zzTeIuwgCH3/8cbNmza644ooff/zRvcFFRUU8RJx55pm9e/fGwrruuXFN2cLw/vvvx+TtPoqyh09MrYW7cujvGDh8sV4/4Y64md5xbvGwYcPOOuusn376KW4LOXGxdevWL7/8ctwt2HaiMGE4OoWAL5RYvwVAGFxe3wfp4H7a77Ky3Gr8NEmu8TZYzu8h9ujR48knn0zcWV4/cfDgwaNGjSrVFLKSlGyFCcPRPsSx4s022oZpBuV1UKpwPUVfwNxNO4bXmTt37u7dIZ9KMTwEDubt3LmzW7dun3/+uUOdWA898MADw4cPj/UsC+sLFoYZM2bEEbA1tCaOs2w7ZS2ttcFl3roHG0GbGWieau7bt++3336r3Tzezot3htfebMgaFCYMR6cQTJ8+PY5grKJVcZxl2ymW3DFwWOO7imy7Hvz3l7/Xc86hR/3yA6Vp06aVNI6sJCVkYcJwxIfNmzcvXbpU6ZJz4Wpa7VwBR5mAPfdV8d134iLxlADvzu1y3634zOB81v79++/YsSO+0204S6owcKIhv+oSR4Rwx+AGmj3CMGvWrPguJDcYUScOArzCzYgRI+I4MaZTNm7ceMcdd8R0ilWVhQnDkRSC+fPnxxen7+n7YopHUeLrTuJZnJK0kBZKtDwOmznvxeFVqTgaxCkJEnjqqad4rYsEG3Fz+ujRozn0yEpSshImDEd8WLgwzpFrJ+38mX5WskBhCYFFtMiqvN64v2TggtFOgOecH3vsMe3NKhvk1INHHnlEeQiFUoWBN3mOO3hzaW7c59pwom18IAzmXNX8Jtr69f6lSj///PO8nKI57ptjiTBhKEkh4DfdFy9eHDfE7+i7uM+14UTb+EAYzLmqX3vtNT+N4aXXeaUNP3uU0pcwYSjBunr16kTeS5pH82x4rTe+SzCf8vlRUnznCj1rwYIFQi0PmdmcBeBdimokVtjkVUlGpDD8/HNCkwT88vNMmqnEgcKpNJUnn63igMlnQ8LNCeichu6zMdivSQlcmDCUpBAkKAwM4lP+Dx8VgU/oE1VxmMv47nPTpk1h9lCIb999F8AzXl6Xm2e8hRDyz0xhwlACZvny5QkS+pa+5fSkBBsJ3+n8ksdiin/yRi6QxK8oub6bY/miRQE8w+QJS0T/2GtApDAkfu/PT0s+o8+OxWF5yWSKYTu8MLFK/IoKE42gfOEVtgPpGtMMx2IXJgwlWUk8+XysJ7GWvEPv4E23o6HxhPz7FNv+FrEyN7a+livKWO9gmAOBiRMnBiVIDlYFfkiYMJTw4tfZEwfHiybhpuFojKwK1j5e27p1a+JXFFoQSiArK0uo5d6ZLVIYdP0Zv0Kv4Kah5NriZ2tv0BveXWeGt8zbfBpuoQ3m5eTkBOJm+fLlA+nX5E6FCQNnJfHKiFu2bNHClNfGmEHxbOqgpXejGuHZhc3kd6agOQQgDCbEIpB3zc4777y6deua4L5RNggTBmbHtwsal8N8np4/RIeMCon/xhygA3zz5H+/5vS4fft2c4yx1pI2bdr473vNmjVTU1P979fwHkUKg0amK2jFu/SuxgYlNsWqYM/OPMoA4Y5BicXnQv7mXrt2bZ87DeQ2xWcf4+hOmDBwVhKviRiHnw6n8E0D7wXtUCHch3jrhdfp9XD7GNU77RdV1B5RQUngzDPPVJZ7V/joo49617jcloUJA4PmOQa9uHfT7nE0Tm+bglr7B/2jkAoFGeyFqchk94JqHG0OGDAgjrPiPqVy5cqYeVbSkycMBQUFSk8SKeR1IGxba7oEFzs+h+Ykgi4c5xYW2i6NhsSxW7du9evX982Yyy67LCkpybfuBHUkTBg82m6Jk1bvp/tte6DEb3Lw7YKgi9U7U3k7Se8aR8vuCSQnJ48cOdJ9/URq8usLw4cP92hIScQwE84VJgzeIdtO2++le4uoyLsujGqZM5H+Sn/lhWaNsiooYzTmuQXlQmj6HTp0aPPmzX1wZ9SoUfwoyYeOJHYBYfgtarwd9L/p3xKjGIfNo2n0ckp0LcI4+jXzFDxPMCcunDzK2z6npKR4alLDhg1vvPFGT7sQ3bgwYeCsJE//hl+lV21YkXsSTeIf0ReuXuPLli2rt0G0lgiBzp0733nnnYm04Hxudnb2m2++mZmZydVKll9zrm/hUWHCwBHy9G+YJxsepofDPRH9JX2JqYVSf+p4xcm0se+2224bNGiQF1bxN8uxY8e2aNHCi8ZD06Y8YfAaPa8adDvdHtZtCXg/57/QX+yZSnF5tZQpU8ZlTVTzhwAP388991yfPn30dsfN8osLAwcO1Nts+FoTJgycQlByA+hpJHgB6tvoNt61xtNe/G/8J/rpLrrLtp073XDOyMhwUw11/CSQnp7+1ltvaRzE+b7w6aefvvbaa4/2AllJypgKEwb2oVKlSkpP9BbyAtQjaMQSWqK32QBb46n16+l6fpsvQBuM7bpChQrG2mazYWlpaS+99BJ/x2eRSJADr4k0ZcoUfnEhwXYsOV2eMOTl5fHl4kN4dtGum+gmfvbiQ19edzGLZt1Ct+whZOurSfNFpT6A0qAJ8MOfESNGzJ07t0uXLvHZwglOw4YNW7BgwWmnnRZfCxaeJUwYSlIIKlas6E+oeCTl8VT60tycZ8VPkPj5mD/QJPbi2xUlEY4JNjdt2nTq1Kkff/xxjx49+CU4lyaVK1fuyiuvXLx48RNPPBFp6QtkJSlhilxvll9L2bBhg9If7YW8jtDddPcgGjSEhiST2ytSuxnxNcjTCc/SsxNoAvYjcgaIR0nOfAw52v3wZ9WqVa+99tqHH344Y8YM5a6cPD6wfvTq1Yv3WsDubPHFTqQwVKlSJT5v4zuLR9WX6KUFtGAUjapAYh5Gb6JN/G7zIloUn9dWnYVHSYLCXadOHX43jT+8nuaaNWtWrlzJ22kcOHCAN4BjgeellvyZhhRELA5ThQkDpxBwBlsgOy7xZMOVdOXNdHMbCmA7kVhDy5MKD9FDPE0S64l21q9Xr56djov2micPjjv8ScSLkiElkRZCea4wYSiJQSDCwF3zKnsjaWRX6noZXWbsrQPfKDxNT39BX4TyevXIqaCuKI/cQbMgkCABkcJw/PHHJ+h23KfzYyWey+Xv4xfRRefSuSnk7YouMdnJMwpv0Bu8Hds+2hfTiagc4BUF+CBgIIEkp3UleaXy4mKjjN64cWPVqlW//PLLTp06BW7YcXTcUBraiTolUcBLuvObzNNo2gv0wlpaGzgWcQbwBAO29hQXNV0GlwwpulqT0Y6LgR13DPGHciWt5ISlulR3IA3sQl0CkYdDdIjvYMbTeN6hM35P7D4Ttwt2xx/eKwiIFIZq1arxtzxORVA45HvRClpxH933Ir3IT5Z4+iGXcv0xYQftmEJT3qa3N5BPmbv++OV/L1hPzX/m6NFwAsIS848sbHliWhsAACAASURBVNKsWTOjyPJuaE/Sk/2oH79KxsuXerceEb+nxnpwK93KffGeClCFxC8DCEPiDOW2gLWSlLETecfAnrAwTJ8+XelSgIX8Nhy/Js0/fN/Qklq2olb8U4/qJfiUiacQltGyeTSPf3jJI6xsoTfEEAa9PNFaCAhIFQZ+Rd5k+vmUX6IQbGRZKtuUmtahOjWpZi2qxT9RU10302aeRuZpA/6XF3ldSAsLqMBkf0XbBmEQHT4Y7wUBkVlJDIIXTunatasXRHxoM4Mysik7i7JyKId/eKUNfvTE2y/z6M//8g/WNSLiDRJqEdXk5XSJsomyiHi/rZzD0eFkXN6qmhcE5JVidxKtpl8m3vmXeD6c5Obb8irx2IdzPCaArCQlYKl3DO3bt+fV1Q8ePKj0yvBCHvf5ZzsZMXluDCse+lsSnUjU6LAklIvRML6jYnn4iWgO/bIgbr7L003Ie3ZpKqqBgG8EpAoDb7nVqlWr2bNn+0YKHXlDoDbR6Yf1oAlRIlcj32E0PvzTm6iI6EeiuUSfH/7F6QNhcKKDY7YSkJqVxPE69dRTbY1aCPwuT3Qe0ZNEzxH9kYg34E1EFUoB4auabzv6E/2T6NnDv0RcdRFXUQgupkRcQFaSkp4wYTjaB3zXU0bU+EIesu8gGk/058PDt9cfXmHtEqKXiB6gY1Y/zM7ObtNGwJKIXjNC+yBQioDGr2l+s+X9mHh3J6clPfy2CP05E2hNNODwUyP/P7xmSdvDP4uJXiX6kuiXtV46duzoz26A/juMHkEgEQLC7hiO3m6J33/G171EYu/juQ2JHid6KCBVONpRnoS4i2hMiSU9e/b0EQK6MpEAdnBTRkWYMJTy4eyzz1Z6hUJjCPCcMD8y4rkEo95Ur0v0INEt7dr1MAYUDAEBgwjIFobf/e53BrGEKaUJnEE07vAkc8Crz5a269f/P+O887Y++eTqoiKzlhCOYC2KQcA/AsKEoVQKwcknn4zdev27WGLoiV9KuJm/kpPZO6Hu2HFw2LAlZ5/93ebNB2JwDlVDRABZScpgChOGUj7w3n69e3PeOj5GEahL9BRRN6NscjDm44+3tmnz1Rdf7HCog0MgYBUB2cLAoerfn7PU8TGHQPfDMwr82pqkz9q1+7t2nf3ggysM25hKEkPYGiYCjumeLjb68ZnFsQubFBYW1qhRY8uWLT5bgu5UBAYffltN8OeSS2o880yTlBQzJ0UEgzXW9GOHFGNN1WaYi4Fd/B0D56FfcMEF2pChoTgJ8IU0TLoqsOvjxq3r2/f7vXt5UQ18QMBeAuKFgUM3YAC/NoVPgATSiG4lOidACzR2/e67m3v0mLN9e6HGNtEUCMgiIEwYlCkEnTt3xra9wV12fAndTtQ5OAP09zx9+g5OVdqz55D+ptGiYQSUQ4phNgZgjjBhUBLihTEuvfRS5SEUekyAn8UPJzrZ414CaP6rr3b26zf/wAE8UwoAProMnEAYhIEhXnTRRbw9Q+A07TPgYqLQrioxadKWiy9eiDwl+65qeEzChCHSwiY1a9bEW9C+X878BknIc4VffnnDbbct8x0sOvSPQKQhxT8LjOxJmDA4MLz66qsdjuKQbgINDi+CFP7P/ff//MEHW8PvJzwEgaMIhEcYevTo0bRpUwTXFwK89zJPOFvx7I4fJQ0d+sOGDVgzw5crC52YQUCYMDikEPAU9I033mgG1dBbcR1R9dA7ecTBTZsODBmyAGvthTLiDkNKKP116ZQwYXD2auDAgdWrWzRgOdPw7OhZIUtOdQNq8uRtjz++2k1N1AGBEBAIlTCkp6dfc801IYiKwS6UJfqTweZ5aNqddy5bvXqfhx2gaRAwhoD4tZJKkSwoKOCX3TZv3mwM4ZAZci1Rr5C55N6dc86p/J//tHJfHzXNJ4C1kpQxCtUdA3tYpkyZm266SekqChMmwPtihvatBTdweLWMd97Bdw43qFBHNoGwCQNH46qrruLtoGWHxVDrOT/V9mVHr7lmCV6HNvTyhFn6CAgTBjcpBNnZ2aNGjdKHCC2VEOhAxHcMtn94muG559bZTiFE/rsZUkLkrltXhAmDS7cuv/zyBg34DSx8NBIYpLEt0U09+ODKwkJsEy06hjA+CoFwCkNGRsZDDz0UxXUcjoHAiUR4efBXXitW7H3xxfUxwENVEJBGIGxZSUfz7969++TJk6VFxEx7HyZCNs5voTnhhKylS09JTrZ9xsXMizUmq5CVpMQVzjuGElcfe+wxLLmqjHqMhTWgCqWILVu2d9q07TFiRHUQEEMgzMLQvHlzvO+m40rsqqORsLXxyisbwuYS/AGB/xIQJgyxphDcfffddevWRbgTI9A9sdPDefYbb2zatw/b+IgPbqxDiniH3TkgTBjcOfVbLX7fbfTo0bGehfpHEeAUVX6UhE9pArt2HeSdfEqX4v9BIBQEQi4MHCPewIcX1wtFsAJxIlSbOesl+PrrG/U2iNZAwBACYc5KOoJ469atLVq0WL8eKYZxXHV8v1U/jtNsOKVSpbSNGzsjN0l0rJGVpAxf+O8Y2O2KFSu+8MILvGGDEgEKIxPgtVRPiHzU9iNbthTOn19gOwX4H0YCVggDB+7MM8+87jreXgafmAi0xOJIzrymTEHSqjMhHBVJQJgwJJJCcN9997Vp00ZklAIzGriioJ86FcIQBZHhhxMZUgx3LRHzhAlDIq7yNj6vv/56uXLlEmnEsnOxal6UgM+atTNKDRwGAYEELBIGjk79+vVfeeUVTDa4vlBrua5pacXNmw9s315oqfNwO7wEhAlDhw68+HNCn549e955550JNWHLyZWJsm3xNQE/Fy3ak8DZODVgAokPKQE74E33woRBC4Q77rijVy9796d0zRC3C65QLVmy21U9VAIBOQRsFAZ+lPTyyy/zmw1ywhSIpbUD6VVcp0uW4I5BXNBgcBQCwoRBVwoBT0G///77tWrhS7HD9VHJ4RgOHSHAe7qBhlwCuoYUuQSUlgsTBqUP8RWyKrA2IEkpMr3MyIdw5DcCu3cfAg4QCBkBe4WBA8lPkyZMmMBprCELqiZ3IAyuQEIYXGFCJVEEhAmD9hSCrl27vvrqqykpKaKi5o+xEAZXnCEMrjCZWkn7kGKqo7HZJUwYYnPOXe3zzz//3//+d3IyUJTihVxVVxdQQQEeJbkChUqCCGA0/CVYgwcP/uc//4kX3wRduOaYWlxsji2wBAT0EBAmDN6lEFx++eVjx47FM6WjLqv9ei6xsLeSnS3sjyjsAYnNP++GlNjsMKw2runfAnLJJZfw+w2Yi/4vEWRhuvpjzcnBBJUrUKgkiACE4X+C1a9fP85TysrKEhRCz0yFMLhCC2FwhQmVRBEQJgw+pBDwahmffPJJhQoVRMXRC2PxQq8rqmXLprqqh0pGEvBhSDHS7yhGCROGKN5oOtypU6eZM2eecILlm5fla8IZ8mby8tJC7iHcs48AhEEd84YNG3755ZcnnXSS+rAVpaus8DJhJ5s0QV5vwhDRgGEEhAmDnykEVapUmTp1av/+/Q0LmW/mrPGtJ9EdNWqUI9p+y433c0gRhFqYMPhMlmehx48f//DDD1uZxrqe6KDPwCV216gR7hgkxg02OxGAMDjRKTl2/fXXf/TRR5Uq2bbaKL/QuzY6HbtrlCmTUrs21g6x+yIIo/fChCGoFIJu3brNnj37tNNOC+M14ODTModjOMQEWrXKBQfRBIIaUgyHJkwYAqRZp04dnnK477770tLsyUL5LkDgIrru1g1pzSICBSNjIwBhiIEXzzSMGjWKM1kbN24cw2mCq84RbLsvpnfrludLP+gEBHwlIEwYTEghaNu27Zw5c26++ebU1NC/2bSRiKeg8VETyM5O6dixnPoYSoUQMGFIMRCVMGEwhCBnK91///3ffPNNu3btDDHJMzNw0xAR7WmnlU9Px19QRD44IJcALuv4Y9e6dWt+rMTJrGXKlIm/FdPPnGG6gcHZ17dvleA6R88g4CGBpGKH5eSTksjhqIdWRWx648aNVatWjXg4oAPr16+/7bbbeLefoqKigEzwrlteOvRVovLedSC0Zb5XWL/+tAoV7MlEEBqoKGabOaREMTrBwy4GdtwxJMj4l9OrV68+bty4b7/9tkuXLhqaM6sJfpthqlkWmWFNz54VoQpmhAJW6CcAYdDGtE2bNpzPOnHiRP5FW6NGNDTFCCsMM2LQoOqGWQRzQEAbAWHCYH4KQe/evflVuLfeeqt58+baohRwQ4uJVgZsgmHdV6yY1ru3bW/CGxYDTeaYP6RocjS2ZoQJQ2zOBVSb947u27fvvHnzWB5OOeWUgKzQ2y1PM+DzG4ERI+pkZuJvB5dEaAng4vYqtMnJySwPMw5/+Bf+X6968qNdnmZY50c/EvooXz716qtrS7AUNoJAnASEjVYSFzbhmwa+dViyZMmNN97IS3nHGaiAT+Mp6PEBm2BM98OH1ylXLvTvNhqD22NDJA4pHiP5pXmkq/oA+bcuDhw48J///GfMmDFTpkyRltvKQ+G/iYzLFfY1fkS5uSkrV56KXdt8xu5dd0hXVbIVdseg9EFQYXp6+oUXXsh7Sq9cufLvf/87L9cq5xET780wRhBqj0y9++4ToAoesUWz5hAQJgyhSSGoVavWNddc8/nnn69evfqpp57q2bNndrb5+718TmT1equ8yPawYZhdMGf40mBJaIYUDSyOakKYMOh13oTWatSocdVVV7333ntbt27l7YBGjBjRpEkTEww72oaMjIzOnTvzkuMTJnRJS0syzTx/7OHXRf/5z8apqZa67w9k9GIIAcyhGRIIyszM7HH48+ijj27evHn64Q9nNPFKroWFhf5bmZeXx9Pm/LCrU6dO7du3Z20osWH48B8fftjG1xouvrjGKadgLVX/r0T0GAABTD4HAD2mLnm+euHChfPnz1+wYAG/G7F48eI1a9YcOsRpQjo/LEv169dv1qxZq1at+NW8Fi1a1K1bV9nBnj2HTj75m++/L1AeDWthvXpZc+eehGSk8MUXk8/KmEIYlFiMLmSp4Lnr5Yc/LBJ8ZfNjqC2HP/zLwcOf/Pz8Iz7w/Ha5cr981eXVwnnnav5w1iz/W7FixeP/++EnWu59XrJkT/v2X+XnaxYn9wb4XJPXy5s+vV379mV97hfd+UAAwqCEjEdJSixGF3JqU4PDn6CsbNQoe8yYpgMGzA/KAJ/7feCB+lAFn5mju2AJCJt8RgpBsJfLkd77969qSX7OeedVvvbaOoZghxnaCWBIUSIVJgxKH1AYCIHHHmt4zjmVA+nat05POqncyy8353wkfEDAKgIQBqvCrdPZlJSk8eObd+6cp7NRk9pq1ixn4sRWvLGzSUbBFhDwgwAmn/2gHOI+du48eNpp386fH7YkpVq1MmbO7MD/hjh2cI0JYPJZeRngjkGJBYVuCXAG50cftWnePFS7XteunfnxxydCFdxeBKgXOgIQhtCF1HeHqlfP+Oyzth07huTlL865+uKLdk2a5PgOEh2CgCkEhAkDUghMuXD+1w7e/Xjy5BO7d69gpnnurTrxxNzPP2933HGZ7k9BTdEEMKQowydMGJQ+oNAEAjk5KZMmtR4yRPBOyD16VJw6tW2VKukm8IQNIBAgAQhDgPDD1jW/IfzCC82eeaaJuG0vOcPqnntO+OCD1mXL4pXPsF2W8CcOAshKigMaTolC4Lvv8v/wh/k//rgnSj0zDlerlv7KKy3OOCO0ebdmYDbUCmQlKQODOwYlFhQmRKB169xvv+1w6aU1k5NNfzfs/POrzJ3bEaqQULxxcugIQBhCF1IzHOJnMmPHNvnyy3Zt2xq69lyDBtkffthmwoSWfMdgBjNYAQKmEBAmDEghMOXCcWcHLynx9dftR49uXKlSmrsz/KjF8+T33nvCggUdzzqroh/9oQ+DCWBIUQZHmDAofUChyQT4adIVV9RatqwTj8WVKwf83bxMmZSRI+suX97pllvq8VS5ydxgGwgESAB/GwHCt6hrfrLEYzGPyA891KBq1QDkgd/Qvu22eitWnMpraCMh1aIrD67GRQBZSXFhw0kJENi/v+jddze/9NKGDz7YUlhYnEBL0U/l+xWeWB48uNoFF1TNzcVyeNGJ2VYDWUnKiEMYlFhQ6AeBLVsKX311w5tvbpo1ayerhcYu+b0Efof5gguqDBpUHUseaQQbvqYgDMqYQhiUWFDoK4G9e4tmztwxbdr2qVO3z569i/83ju55zqBFizJduuTxT+fO5fGqWhwMLTwFwqAMujBhmDhxYp8+fZSeoDAcBIqLadWqffxyHP8sXbpn7dr9+fkHCwoO8c+uXQfZR84p4jnk3NxUnjaoXDmtUaOchg2zOfe0bt1MvlEIBwR44RsBG4cU3nmK/8wcP1gAwBEPDvpOgC9aXsOOf848U/ySfL7DQ4cgoIcAspL0cEQrIAACIBAaAsIeJdn4QDA01xocAQHzCNg4pLh4lIQ7BvMuVVgEAiAAAoESwBxDoPjROQhEIFC0t6h43yE+mJJn0GoiEYxFcdgICBMGXtgEWUlhuwbhDxHLwO6ZO/I/27F3fsG+Hwr2L99bfPC3vBHWhqwm2ZlNy5TpVC63S1563Sww00UAQ4qSpDBhUPqAQhAQSqD4QNGOiVu2vbJh1/tbivZFfHvj0PbCgi938s+WZ9eyp5mNsisMqFZhcPWME6AQQiNvutkQBtMjBPtCSYAlYcvYtRv+tvLAqn2xOrhvyZ51dy1f/5ef8/pVrX5rvcymObG2gPog4EwAWUnOfHAUBPQT2DV526qrFu/XscNdUmpS5atr1/zLCcllsBJUPJFCVpKSGrKSlFhQCAKeECjac2jlJQt/7D5HiyqwiTwVsenxVT80+bLgix2eWIxGrSQAYbAy7HA6CAL7f9qz+KRvtjy3TnvnB9bsX9p1Nj+Y0t4yGrSTgDBhwHZLdl6mIfB6z+xdS079du+CAo984VuHtSN/XPXnxcWHoiyD45EBQpvFkKIMnDBhUPqAQhAwnMDur3YuPWN24cYDXtu5+Z9rVv7fQoI0eA067O1DGMIeYfgXNAF+gvRTn3mH8n95W82Hz9YX168d9ZMPHaGLEBNAVlKIgwvXgidQtPvQonZf71u822dT6r3UvMKgaj53KrE7ZCUpo4Y7BiUWFIKAHgKrhi3xXxXY9FVXLtKV+KQHBFoRRQDCICpcMFYUgV0fbt36L/05SG4Y8JOrlX9ahMkGN6xQ51gCwoQBKQTHhhAlZhLgJS5WXb04QNvyP9u+9YX1ARogomsMKcowCRMGpQ8oBAEDCWx5Zs3+ZXuDNWzd7ct47Y1gbUDvEglAGCRGDTabToCH440PB/+62YHV+7a+uMF0WLDPPALChKFDhw7mMYRFIFCawPa3NvHbyKVLg/j/TX9fFUS3YvrEkKIMlTBhUPqAQhAwjcC2F015uM8bPOz5Lt80PrDHcAIQBsMDBPPkETi4rXDXJ9vMsXv7axvNMQaWiCAgTBiQQiDiqrLcyPzJ247efy1wGrs+2hq4DcYagCFFGRphwqD0AYUgYBSBXZ8adLvAZPbMKzi42fNlmowKAYxJkACEIUGAOB0EShPgx/qli4L9/6LivQv9XpMjWI/Re4IEhAkDUggSjDdO94HA/iV7fOglpi4MNCkm+72rjCFFyVaYMCh9QCEImEOAV83jyWdz7CmxJI6dpU1zAfb4SQDC4Cdt9BV+AiwMBjrJW4oaaBVMMpZAqrGWKQ3jFII+ffooD6FQCoF9tLCIAl4r4lhW2dT22MI4SniJpDjO8vqUor0mWuW1127ax5CipCRMGJQ+oFAWgeXUfy/NN83mtuFeiTTJNN6wx2gCeJRkdHhgnDgCKWVN/LKVkpsijiQMDpCAMGFACkGA1wq6dkMgOSclKcW47+fJRsqVG55e18GQoiQsTBiUPqAQBMwhkJSWlF4n0xx7SizJrJ9tmkmwx2QCEAaTowPbRBLIaGTcKJzR0DiTRIbWGqOFCQMWNrHmyhTsaJlTyhtlfUq51KzmZYwyyRxjMKQoYyFMGJQ+oBAEjCJQtkcFo+zJPSOPH3AZZRKMMZwAhMHwAME8eQRy2pdNq5Fhjt3lelc2xxhYIoKAMGFACoGIq8p2I5OTKvSvagiEpIzkvAuqGGKMgWZgSFEGRZgwKH1AIQiYRqDiRdUNMan8uZVTypv4aoUhfGCGkgCEQYkFhSCQEIGsVrnlelVKqAktJydR9VvqamkJjVhFQJgwIIXAqqtTtLPV7zyegp7xLX9+FZYo0Ri9Nh5DipKwMGFQ+oBCEDCQAE9BB/tAKTkzudbDDQwkA5PMJwBhMD9GsFAqgZoPNEjJSwvK+qoj62bUywqqd/QrmoAwYUAKgeirzTbj06qlHzemSSBe53QsV/3WeoF0LatTDCnKeAkTBqUPKAQBYwnk/b5KlWG1fTYvtVLa8eOb46U2n7GHqTsIQ5iiCV9MJFDrsYblz/HvFbPk7JT6k1qn18VDJBMvBik2CRMGpBBIubBg5xECvAp3vfHNy/ao6AMTVoXj32yZc1I5H/oKRxcYUpRxFCYMSh9QCAKGE/jlW/zEVhUGVvPUztQKaQ0nn1jubD8UyFNH0HjgBCAMgYcABlhBICk9ud5LzWs+UD8p1ZO3G7LblW38TYeck3GvYMXl5LWTwoQBKQReXxBo30MCSVRtZN2G09pmNs7R2AsrTbWbjms8vV3G8ZhXiJkrhhQlMmHCoPQBhSAgiECZTuWbzjup5r0naNmHObdLXpM5J9V8sAEvlicIAkw1nAAuJsMDBPNCSIAfK1W7pV7zFadWv+P41Crp8XiYnMSz2Xzz0XBq26wW2IQnHoQ4x4GAMGFACoFDLHFIFgGeK65x9/Et153W4MM2lf5UM8PFtszJOSmsB7Ueadhy1akNPmqTe3qeLJcNtBZDijIoWI9XiQWFIOATAU5mLXtWRf7h/grX7t87v2DvDwX7l+8tyj9UtK+IC1PyUlPz0jKb5GQ2zclunYvX1nwKjN3dQBjsjj+8N4lAWs0M/in7O+SbmhQVK20R9igJKQRWXqVwGgS8IoAhRUlWmDAofUAhCIAACICARgIQBo0w0RQIgAAIhIGAMGFACkEYLjr4AALGEMCQogyFMGFQ+oBCEAABEAABjQQgDBphoikQAAEQCAMBYcKAFIIwXHTwAQSMIYAhRRkKYcKg9AGFIAACIAACGglAGDTCRFMgAAIgEAYCwoQBKQRhuOjgAwgYQwBDijIUwoRB6QMKQQAEQAAENBKAMGiEiaZAAARAIAwEhAkDUgjCcNHBBxAwhgCGFGUohAmD0gcUggAIgAAIaCQAYdAIE02BAAiAQBgICBMGpBCE4aKDDyBgDAEMKcpQCBMGpQ8oBAEQAAEQ0EgAwqARJpoCARAAgTAQECYMSCEIw0UHH0DAGAIYUpShECYMSh9QCAIgAAIgoJEAhEEjTDQFAiAAAmEgIEwYkEIQhosOPoCAMQQwpChDIUwYlD6gEARAAARAQCOBVI1toSkQcEMgj36fQye7qYk6IAACgRBIKi4ujthxUhI5HI14mocHNm7cWLVqVQ87QNMgAAI2EbBxSHExsONRkk1/BPAVBEAABFwQgDC4gIQqIAACIGATAWHCgBQCmy5O+AoCnhPAkKJELEwYlD6gEARAAARAQCMBCINGmGgKBEAABMJAAFlJYYgifAABEIiPALKSlNxwx6DEgkIQAAEQsJcAhMHe2MNzEAABEFASECYMSCFQRhGFIAAC8RHAkKLkJkwYlD6gEARAAARAQCMBCINGmGgKBEAABMJAAFlJYYgifAABEIiPALKSlNxwx6DEgkIQAAEQsJcAhMHe2MNzEAABEFASECYMSCFQRhGFIAAC8RHAkKLkJkwYlD6gEARAAARAQCMBCINGmGgKBEAABMJAAFlJYYgifAABEIiPALKSlNxwx6DEgkIQAAEQsJcAhMHe2MNzEAABEFASECYMSCFQRhGFIAAC8RHAkKLkJkwYlD6gEARAAARAQCMBCINGmGgKBEAABMJAAFlJYYgifAABEIiPALKSlNxwx6DEgkIQAAEQsJcAhMHe2MNzEAABEFASECYMSCFQRhGFIAAC8RHAkKLkJkwYlD6gEARAAARAQCMBCINGmGgKBEAABMJAAFlJYYgifAABEIiPALKSlNxwx6DEgkIQAAEQsJcAhMHe2MNzEAABEFASECYMSCFQRhGFIAAC8RHAkKLkJkwYlD6gEARAAARAQCMBCINGmGgKBEAABMJAAFlJYYgifAABEIiPALKSlNxwx6DEgkIQAAEQsJcAhMHe2MNzEAABEFASECYMSCFQRhGFIAAC8RHAkKLkJkwYlD6gEARAAARAQCMBCINGmGgKBEAABMJAAFlJYYgifAABEIiPALKSlNxwx6DEgkIQAAEQsJcAhMHe2MNzEAABEFASECYMSCFQRhGFIAAC8RHAkKLkJkwYlD6gEARAAARAQCMBCINGmGgKBEAABMJAAFlJYYgifAABEIiPALKSlNxSlaUo1EmgYAHt+IL2r6F9a6hor7aWk7Mp6zjKqEkVzqSs47U1i4aMIbB2f+E9y9cZY86vhpyelzuwWgXTrII9eglAGPTyPKo11oA1o2nNGNqzxLM+/ttwbhuqdRXVGEpJCKjnsH3rYFvhwTFrt/jWncuOUpKSIAwuWcmtJmyOQUwKwaa3aUZ9Wnq9H6rAV1/+XFr0J5rZlLZ/JvdahOUg4D8BMUOKv2iECYO/cOLt7ee/0vcX0H7fHwLs+ZHmdKe1z8ZrN84DARAAgV8IQBh0XwcrHqBltxMV627XXXvFhbToMtr4qrvaqAUCIAACCgLChKFDhw4KJ8wp4idIP90StDnFtPASyp8XtBnoHwQEEDB9SAkIoTBhCIiSu2752/qPNwR2r3C0jYf2HLYEHxAAARCIhwCEIR5qK9pn4gAAB0JJREFU6nPWv0B7l6sP+V+6bTLxDz4gAAIgEDsBYcJgdArB+pdj5+/lGetf9LJ1tA0CYSBg9JASHGBhwhAcqGg9F26jHZ9Hq+Tv8S2TqPigv12iNxAAgTAQgDBoimL+d1R8SFNbmpphrdr7s6a20AwIgIBFBIQJg7kpBAc2mHjV7F9volWwCQSMIWDukBIoImHCECgrx84P7nQ8HNDBAxsD6hjdggAICCYAYRAcvOimcwYtPiAAAiAQIwFhwoAUghjji+ogAAJOBDCkKOkIEwalDygEARAAARDQSADCoBEmmgIBEACBMBAQJgxIIQjDRQcfQMAYAhhSlKEQJgxKH1AIAiAAAiCgkQCEQSNMNAUCIAACYSAgTBiQQhCGiw4+gIAxBDCkKEMhTBiUPqAQBEAABEBAIwEIg0aYaAoEQAAEwkBAmDAghSAMFx18AAFjCGBIUYZCmDAofUAhCIAACICARgIQBo0w0RQIgAAIhIGAMGFACkEYLjr4AALGEMCQogyFMGFQ+oBCEAABEAABjQQgDBphoikQAAEQCAMBYcKAFIIwXHTwAQSMIYAhRRkKYcKg9AGFIAACIAACGglAGDTCRFMgAAIgEAYCwoQBKQRhuOjgAwgYQwBDijIUwoRB6QMKQQAEQAAENBKAMGiEiaZAAARAIAwEhAkDUgjCcNHBBxAwhgCGFGUohAmD0gcUggAIgAAIaCQAYdAIE02BAAiAQBgICBMGc1MIklJMvBxSck20CjaBgDEEzB1SAkUkTBgCZeXYeVplx8MBHcyoHlDH6BYEQEAwAQiDpuBl1tHUkNZmMmtrbQ6NgQAIWEFAmDCYm0KQ25rSKpl1yZRpSelVzTIJ1oCAYQTMHVICBSVMGAJl5dg5zzFU7u1Yw/eDlc/1vUt0CAIgEAYCEAZ9Uax9LSUZwzOlDNW+Sp9vaAkEQMAiAsYMZO6YG51CkNuKqvZ354f3tereROnVvO8GPYCAbAJGDynBoRUmDMGBctdzw8cpq567ql7WKncKHTfSyw7QNgiAQJgJQBi0Rje9MrX6D6XlaW00xsY4E6nl65ScHuNpqA4CIAACvxIQJgwCUgjKtKD2Mym7QTCXWNn21H4WZdQMpnf0CgLSCAgYUoJAKkwYgkAUe5/ZjajDN1R3JKXkxH5yvGekVaD691O7zyijRrxN4DwQAAEQ+IVAqjgMGzduZJuPTBkdEXzjShZ1ooPNaPu0Dl2G0KGCX2x+rU8J7Q79Jpb8oqMkiVJzv572KuV1pl05HbJ2EfGPBD5ff/0rjQ4dfqWBEqKjr+eV+w7QklUT+/2adtzntf+UgAq2ZOiQ34v5G3RxRZUgxb+lCCQVFxdHhJKURA5HI56GA8cQKC6iwi0l8nDMsQQKUssa91ZdAt7g1FIE9hYVLSzYZxqWyumpdTIxg2VaWGKxx8XADmGIBSjqggAIgIB0Ai6EAXMM0oMM+0EABEBAMwEIg2agaA4EQAAEpBOAMEiPIOwHARAAAc0EIAyagaI5EAABEJBOAMIgPYKwHwRAAAQ0E4AwaAaK5kAABEBAOgEIg/QIwn4QAAEQ0EwAwqAZKJoDARAAAekEIAzSIwj7QQAEQEAzAQiDZqBoDgRAAASkE4AwSI8g7AcBEAABzQQgDJqBojkQAAEQkE4AwiA9grAfBEAABDQTgDBoBormQAAEQEA6AQiD9AjCfhAAARDQTADCoBkomgMBEAAB6QQgDNIjCPtBAARAQDMBCINmoGgOBEAABKQTgDBIjyDsBwEQAAHNBCAMmoGiORAAARCQTgDCID2CsB8EQAAENBOAMGgGiuZAAARAQDoBCIP0CMJ+EAABENBMAMKgGSiaAwEQAAHpBCAM0iMI+0EABEBAMwEIg2agaA4EQAAEpBOAMEiPIOwHARAAAc0EIAyagaI5EAABEJBOAMIgPYKwHwRAAAQ0E4AwaAaK5kAABEBAOgEIg/QIwn4QAAEQ0EwAwqAZKJoDARAAAekEIAzSIwj7QQAEQEAzAQiDZqBoDgRAAASkE4AwSI8g7AcBEAABzQQgDJqBojkQAAEQkE4AwiA9grAfBEAABDQTgDBoBormQAAEQEA6AQiD9AjCfhAAARDQTADCoBkomgMBEAAB6QQgDNIjCPtBAARAQDMBCINmoGgOBEAABKQTgDBIjyDsBwEQAAHNBCAMmoGiORAAARCQTgDCID2CsB8EQAAENBOAMGgGiuZAAARAQDoBCIP0CMJ+EAABENBMAMKgGSiaAwEQAAHpBFKjOJCUFKUCDoMACIAACISLgKMwFBeHy1l4AwIgAAIgEJ0AHiVFZ4QaIAACIGAVAQiDVeGGsyAAAiAQnQCEIToj1AABEAABqwhAGKwKN5wFARAAgegEIAzRGaEGCIAACFhFAMJgVbjhLAiAAAhEJwBhiM4INUAABEDAKgIQBqvCDWdBAARAIDoBCEN0RqgBAiAAAlYRgDBYFW44CwIgAALRCfw/AC+tc/9p/AAAAAAASUVORK5CYII=" width="400" /><br />
As you can see, the problem for calculating your SVG size and therefore finding mouse coords in FF is currently a bit "hackish".<br />
<br />
Even though this might be correct when reading the W3C standard for SVG, I think it is better to do the same as all the other browsers to make it easier to develop SVG based applications for the browser. <br />
<br />
<h4>
<b>Conclusion</b></h4>
For SVG, Firefox is the new IE, and that is not a good thing.Anonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.comtag:blogger.com,1999:blog-107427964630416391.post-37574631395813507972014-01-08T14:44:00.001+01:002014-01-08T15:03:34.724+01:00iTunes connect screenshot and icon detailsSome simple guidelines on the sizes/resolution for screenshots and icons for iTunes connect, when you submit your app.<br />
<div>
<br /></div>
<h3>
For all images</h3>
<div>
<ul>
<li>Minimum 72 DPI</li>
<li>RGB color space</li>
</ul>
</div>
<h4>
</h4>
<h4>
Large app icon</h4>
<div>
<ul>
<li>1024x1024 (cannot be scaled up, but can be scaled down)</li>
<li>File format: jpg tif or png </li>
<li>Keep it "flat" as in the new guidelines. Avoid rounded corners.</li>
</ul>
<h4>
</h4>
<h4>
3.5" Retina Screenshots</h4>
</div>
<div>
<ul>
<li>Portrait: 960x640 or 960x600</li>
<li>Landscape: 640x960 or 640x920</li>
<li>File format: jpg or png</li>
</ul>
</div>
<h4>
</h4>
<h4>
4" Retina Sreenshots </h4>
<div>
iPhone 5/iPod touch (5th gen)</div>
<div>
<ul>
<li>Portrait: 1136x640 or 1136x600</li>
<li>Landscape: 640x1136 or 640x1096</li>
<li>File format: jpg or png</li>
</ul>
</div>
<div>
<br /></div>
<div>
<h4>
iPad Screenshots</h4>
<div>
<ul>
<li>Portrait: 1024x768, 1024x748, 2048x1536 or 2048x1496 </li>
<li>Landscape: 768x1024, 768x1004, 1536x2048, or 1536x2008</li>
<li>File format: jpg or png</li>
</ul>
</div>
</div>
<div>
Do the screenshots on a device and you should be all good to go.</div>
<div>
<br />
<br />
For more details on the content of the screenshots and icons read this document: <a href="https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/iTunesConnect_Guide.pdf">https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/iTunesConnect_Guide.pdf</a></div>
Anonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.comtag:blogger.com,1999:blog-107427964630416391.post-51487860563027932562013-10-24T13:44:00.001+02:002013-10-24T13:44:05.409+02:00Remember to have a "catch-all" in your nginx configuration when using virtual domainsI got a bit stressed when I saw that a beta for a service I am working on was out in the open. I checked every config file and firewalls, and could not see why the site would respond to a different domain and the ip, when the <i>server_name</i> in nginx was set.<br />
<br />
I cannot stress this enough: <b><span style="color: #990000;">You need a catch-all in your nginx config!</span></b><br />
<br />
If you do not have a server config block to catch all "none-virtual" domains, nginx just picks the first available configuration. Quite different from what I was used to with apache. And if you have private and public sites on a mixed box, that might be mission-critical if sensitive data gets out... <br />
<br />
So do yourself a favor and throw this into your nginx config before the virtual hosts are loaded:<br />
<br />
<pre> server {
root /usr/share/nginx/www;
index index.html index.htm;
location / {
try_files $uri $uri/ /404.htm =404;
}
}
##
# Virtual Host Configs
##
# ....
</pre>
<br />
Without the above settings, your site will be accessible on any domain pointing to the server, and any IP the server hosts.Anonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.comtag:blogger.com,1999:blog-107427964630416391.post-11570192576382768732013-10-14T22:41:00.000+02:002013-10-14T22:41:28.700+02:00Get the dot notation of a embedded Mongoid elementI needed to get the dot notation for embedded documents to perform queries using the given dot notation.<br />
<br />
I could not find a "standard" way of doing just that, so here are some helper methods I use in a class. (Part of soon-to-come BoxCMS)<br />
<br />
This Gist uses Mongoid metadata and reflect_on_all_associations, so it might break for newer versions of mongoid at some point.<br />
<br />
Using dot notation queries in mongoid, makes it easy to dig for even the deepest "weird" embedded structures you might have.<br />
<br />
Example of query:<br />
» Box::Page.where("rows.boxes.box_items.is_template_object" => false).first<br />
<br />
Dot notations in mongodb are a powerful tool, but use it wisely unless you are indexing "more than you should".
<br />
Basic usage: Call dot_notation_for_element(embedded_element) to get the dot notation all the way up to the parent document, excluding the parent documents name/embedded name.<br />
<script src="https://gist.github.com/leifcr/6981688.js"></script>Anonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.comtag:blogger.com,1999:blog-107427964630416391.post-76367041017083156612013-08-14T11:54:00.001+02:002013-08-15T10:28:48.017+02:00Setting up your own S3 storage on VPS serversAfter using Amazon S3 for a while, and seeing that the performance in Northern Europe is basically crap (even with CloudFront), it was time to try setting up an alternative on VPS servers that were closer to "home". I chose only to look at S3 API compatible storage as I already had apps running using S3 storage as a backend, and I wanted to move the storage for the apps to servers running in Northern Europe.<br />
<br />
<h3>
The requirements</h3>
<br />
Easy to scale<br />
Replication<br />
S3 Compatible API<br />
Must run as an application on top of Ubuntu 12.04 LTS<br />
Must use the storage available on a VPS<br />
Can limit disk usage per node. (This is not an absolute requirement, but a nice-to-have requirement)<br />
Can work on low to medium latency connections<br />
<br />
<h3>
The contenders</h3>
<h4>
</h4>
<h4>
Riak CS</h4>
<br />
Runs on top of Riak<br />
Riak is a database (Key/value store) and therefore runs on top of the majority of *nix based distributions<br />
Easy to add nodes using command line tools to a Riak cluster<br />
Has S3 compatible API<br />
Replication is a must for a Riak cluster<br />
No node is master/slave, all are equal (good for HA)<br />
Has a nice web administration tool<br />
<br />
<h4>
</h4>
<h4>
Eucalyptus Walrus</h4>
<br />
Medium difficult to add nodes<br />
Has addons for S3 compatible apis<br />
Can use a normal folder for storage, but not if replication is used<br />
Must have a block device to replicate<br />
Easy to limit usage on a node<br />
Difficult to configure replication<br />
Must have a Cloud Controller, and for HA, a secondary controller<br />
<br />
<h4>
</h4>
<h4>
OpenStack Swift</h4>
<br />
Medium difficult to scale<br />
Can have problems with medium latency connections, due to writing on a majority of nodes<br />
S3 compatible API as an addon<br />
Must have block devices<br />
Easy to limit usage on a node<br />
Syncs through rsync. (I never liked rsync...)<br />
<br />
<h4>
</h4>
<h4>
Cloudian</h4>
<br />
Has a community Edition, but documentation is sign-up only (Vmware/citrix anyone?)<br />
Claims to be OSS, but in reality: no.<br />
Read up on the docs, but the documentation is sparse and it does not feel "production-ready"<br />
<br />
<h4>
</h4>
<h4>
Apache Cloudstack</h4>
<br />
Has S3 API<br />
Not usable as it requires a management server and a host/hypervisor system<br />
<br />
<h4>
</h4>
<h4>
Ceph</h4>
<br />
Is a distributed file system.<br />
Easy to add nodes<br />
Replicates across nodes<br />
Does have a S3 compatible API, although some limitations (http://ceph.com/docs/next/radosgw/s3/)<br />
Has a nice deploy-tool<br />
Requires block devices for storage<br />
<br />
<h3>
</h3>
<h3>
Wrap up</h3>
<br />
Basically this gives two different directions.<br />
Setting up Riak CS directly on the system or choosing Ceph, Walrus or Swift and setting up a file as a block device.<br />
<br />
After reading up on the docs, I am considering both Ceph and Riak CS, and will start by testing Riak CS. The<br />
<br />
Both provide good chef cookbooks, so for large scale deployments, use time to setup chef properly. It will save you time when you need that next node if you plan to grow.<br />
<br />
However, this is most likely going to be more expensive than using cloud storage, so do consider if you want to use your time on this or just pay for cloud storage.<br />
<br />
Other openstack options would work fine as well, since the client library I am using supports both.Anonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.comtag:blogger.com,1999:blog-107427964630416391.post-21052113072486941272013-08-08T10:14:00.002+02:002013-08-15T10:29:08.676+02:00wicked_pdf and stylesheetsThere are several articles showing how to use wicked_pdf on a Ruby on Rails application, so I am not going to exlain that here. Setup is quite simple, and the gem is just awesome.<br />
<br />
There are however a few things to notice:<br />
<h4>
</h4>
<h4>
Outputs on osx and ubuntu can be inconsistent</h4>
If you are using wkhtmltopdf-binary-11 on osx in development and the same binary in production on a ubuntu system the output of the pdf is most likely to be quite different. Solution: Develop on your local computer, fine-tune using an Ubuntu machine in Virtualbox.<br />
<h4>
</h4>
<h4>
Stylesheets don't 'behave'</h4>
You might get mixed results when using linked stylesheets. The simple solution is to inline your pdf or print stylesheet. Put this in your pdf layout file:<br />
<pre><style type="text/css">
<%= Rails.application.assets.find_asset('pdf_print.css').to_s.html_safe %>
</style></pre>
This way your stylesheet will be inline, and all linking issues with wkhtmltopdf is resolved. If you have full paths for your assets, that will sometimes work as well. (e.g. if they are hosted on a cdn), but to my experience, inline CSS is more consistant when using wkhtmltopdf.<br />
<br />
After writing this blog post, I found this repository:<br />
<a href="https://github.com/jwo/railsdotpdf">https://github.com/jwo/railsdotpdf</a><br />
It is a fully working example of generating pdf with linking to the assets instead of inline css. It is a fairly basic example, but it shows how to setup wicked_pdf with linked stylesheets.Anonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.comtag:blogger.com,1999:blog-107427964630416391.post-83230250051753891172013-08-02T12:32:00.000+02:002013-08-15T10:29:22.490+02:00Nginx max length of server_names / server_names_hash_bucket_sizeI have deployed quite a lot of sites lately, and since some of them have more than one alias, I tend to use the server_names attribute with more than one name<br />
<br />
e.g. in <em>/etc/nginx/sites-enables/mysite.conf</em><br />
<pre class="prettyprint">servernames somesite.example.com othersite.on.example2.com</pre>
<br />
This will throw the following error: <br />
<pre>Starting nginx: nginx: [emerg] could not build the server_names_hash, you should increase server_names_hash_bucket_size: 32</pre>
<br />
To fix this, just increase the number in nginx.conf<br />
<pre class="prettyprint">server_names_hash_bucket_size 128;</pre>
Depending on how many sites you have as aliases, you might need the value to be higher or lower than 128<br />
<br />
Anonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.comtag:blogger.com,1999:blog-107427964630416391.post-77080839747206958012013-07-01T15:40:00.001+02:002013-08-15T10:29:35.639+02:00Not possible to see Microsoft support answersAnyone experienced creating a support ticket with Microsoft and not being allowed to view the reply?<br />
<br />
It is quite impressive that you are allowed to file a ticket, but not see the answer! I did send a ticket on the original ticket. Guess what happened then? Exactly the same. Catch-22 anyone?<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-1TMJ3inVU0Y/UdGF7zJZ0LI/AAAAAAAAA5A/kU8qKeOVTQ8/s1119/Skjermbilde+2013-07-01+kl.+15.36.40.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="305" src="http://2.bp.blogspot.com/-1TMJ3inVU0Y/UdGF7zJZ0LI/AAAAAAAAA5A/kU8qKeOVTQ8/s640/Skjermbilde+2013-07-01+kl.+15.36.40.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
I guess this has something to do with me been "clever" and opting in for MS linked accounts at one stage, which is now being abandoned by MS. So guess I have to wait until they unlink my account sometime in July...</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
Anonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.comtag:blogger.com,1999:blog-107427964630416391.post-37181509919450600512013-02-28T08:59:00.000+01:002013-02-28T09:02:39.552+01:00Rake task for rspec:featuresI'm using Capybara, rspec and poltergeist to test features/requests in Ruby on Rails<br />
<br />
I wanted a simple raketask to check the features without typing:<br />
<pre class="prettyprint">$ rake spec SPEC=spec/features/**/*_spec.rb</pre>
<p>I wanted to type:
<pre class="prettyprint">$ rake spec:features</pre>
</p>
<p>Creating the new raketask is quite simple:
<pre class="prettyprint lang-ruby">desc "Run the code examples in spec/features"
task "spec:features" do
Rails.env = ENV['RAILS_ENV'] = 'test'
ENV['SPEC'] = "spec/features/**/*_spec.rb"
Rake::Task['spec'].invoke
end
</pre>
Note that the spec is set as an ENV variable and not passed as an argument to the invoke command.
</p>
Anonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.comtag:blogger.com,1999:blog-107427964630416391.post-54256846823289516342012-11-29T14:23:00.001+01:002013-02-28T09:01:13.828+01:00Watching/monitoring CSS attributes with jQueryI needed to monitor dimension and size CSS attributes for a AAC switch-access library I'm developing. (A separate blog post on that when it's ready. It's already on github, but there are some annoying bugs, and only support for single switch elements, and no groups...)<br />
<br />
After testing jquery-resize (<a href="http://github.com/cowboy/jquery-resize">github.com/cowboy/jquery-resize</a>), jquery-watch (<a href="http://darcyclarke.me/dev/watch/">darcyclarke.me/dev/watch/</a>) and a few others, I found that they were almost what I was looking for, but not exactly what I needed.<br />
<br />
My requirements where:<br />
<br />
<ul>
<li>Monitor one or multiple CSS attribute</li>
<li>Use custom functions like 'outerWidth(true)' to get the full width including border, margin and padding. 'css('width')' would only return the element width.</li>
<li>Use requestAnimationFrame instead of setTimeout if available.</li>
<li>Either jQuery events or direct callbacks</li>
</ul>
Since I didn't find any existing jQuery plugins, jquery-csswatch was born.<br />
<br />
<b>Sources</b>: <a href="http://github.com/leifcr/jquery-csswatch">github.com/leifcr/jquery-csswatch</a>.<br />
<b>Example</b>: <a href="http://leifcr.github.com/jquery-csswatch/example/">leifcr.github.com/jquery-csswatch/example/</a><br />
<br />
The plugin is written in CoffeeScript.<br />
<br />Anonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.comtag:blogger.com,1999:blog-107427964630416391.post-68714707401478596492012-10-25T13:32:00.000+02:002013-02-28T09:00:42.191+01:00Rails 3.2 and virtual attributesI was fighting with virtual attributes for a few hours, and I'll share what I did wrong:<br />
<br />
Here's a model with one virtual attribute as well as a text field<br />
<br />
<pre class="prettyprint lang-ruby">class User < ActiveRecord::Base
attr_accessible :sanity_level
attr_accessor :sanity_level
validates_presence_of :sanity_level, :on => :update
end
</pre>
<br />
I would expect the following to work:<br />
<br />
<pre class="prettyprint lang-ruby">user = User.create(:name => "Leif")
user.save(:sanity_level => 5)
user.valid?
# => false # What?
user.errors.inspect
# => Errors show: Sanity Level cannot be blank... but it is 5?
user.update_attributes(:sanity_level => 5)
user.valid?
# => true
</pre>
I expected both to work, but it doesn't by design. After looking at activerecord and mysql2 source, it is easly spotted that save calls create_or_update without forwarding the hash, so of course it will fail!
<br />
To summarize and remember for the future:
<br />
<ol>
<li>Use <em>update_attributes</em> when updating with a hash for the attributes to update</li>
<li>Use <em>save</em> after setting attributes manually</li>
<li>Use <em>create</em> when creating new records</li>
<li>Use <em>build</em> when building new records with a hash and saving later</li>
</ol>
And a note to myself again: <i>Don't use save when updating attributes.</i>Anonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.comtag:blogger.com,1999:blog-107427964630416391.post-47600737541078307312012-06-29T23:16:00.001+02:002013-08-15T10:31:24.020+02:00Why I might move away from QtTo begin with I loved Qt, and I still do for some projects. However, things have changed a lot the last 5 years in the tech industry, and Qt has fallen a bit off the grid after Nokia took charge.<br />
<br />
Key points why I am reluctant in using Qt on future projects:<br />
<br />
<ul>
<li>Android</li>
<li>iOS</li>
<li>Windows RT</li>
<li>Windows Phone</li>
<li>Still struggling to support meego, and symbian. Big waste of resources!</li>
</ul>
<br />
<div>
I think that should be quite self-explanatory, but let me dig a bit deeper.</div>
<div>
<br /></div>
<div>
At one point Qt was on the cutting edge of new platforms, then symbian and meego wanted to join in. I knew already than that it meant trouble, because both were already dying platforms. </div>
<div>
<br /></div>
<div>
I've been using Qt successfully on quite a number of projects over the last 7-8 years, and it has been a pleasure.</div>
<div>
<br /></div>
<div>
Now I don't know if my next few projects will be Qt based at all, even though they will be both mobile and desktop applications.</div>
<div>
<br /></div>
<div>
Qt can support XP/Win7/Win8 Desktop and OS X, but not metro style WinRT (MS ARM based tablets). Qt cannot help me with mobile so I need a different framework here.</div>
<div>
<br /></div>
<div>
So I started looking this way. How many frameworks do I need to write the same software for iOS, Android, OS X, Win XP, Win 7, Win 8 and Win RT ?</div>
<div>
<br /></div>
<div>
I hoped to keep the number down to three, and with Qt, it still might be possible. But there are other frameworks lurking around as well now. </div>
<div>
<br /></div>
<div>
<h4>
Example: Mono/.NET:</h4>
</div>
<div>
I can use C# and write common code for all platforms that have Mono or .NET.</div>
<div>
Well. All platforms both mobile and desktop has either .NET or Mono. I have to write the GUI for Desktop (OS X, Win XP/7/8) as one, mobile needs one for iOS and one for Android. I would actually have 1 "base" code and 3 derative guis on top. That is managable.</div>
<div>
<br /></div>
<div>
<h4>
Example: Qt:</h4>
</div>
<div>
I can use C++ to write common objects for both mobile and desktop using STL. The GUI for OS X, Win XP/7/8 can be written in Qt, while the desktop for WinRT have to be written in C++/XAML. Android and iOS need a separate framework.</div>
<div>
<br /></div>
<div>
By choosing Qt, I end up having a C++ base and at least 4 gui implementations in various languages? It's almost doomed to fail. By choosing .NET/Mono I can have 3 gui implementations that actually use parts of each others code, so I save 1 framework/gui implementation. If that is 1 month of work, it's worth using a different framework.</div>
<div>
<br /></div>
<div>
I hope that Qt and the Trolls will open their eyes and make all non-gui parts of Qt compatible with more platforms. It's impossible to have the same GUI code for all the platforms we have today. Even thought it's possible, it will feel "Qt" on top of a native GUI, which isn't really user friendly...</div>
Anonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.comtag:blogger.com,1999:blog-107427964630416391.post-21799384179906328432012-06-29T23:00:00.002+02:002012-06-29T23:00:45.494+02:00Mobile framework comparisons will be continuedI've been on paternity leave, which is great, and therefore I've tried to keep the blog level down, as the mobile framework is closely related to my work. Paternity leave means stay with your kids, don't press keys on your computer... At least it does for me.<br />
<br />
I will do a couple of frameworks next week.<br />
<br />
I'm glad I've been a bit delayed, because of the launch of Win8 RT + Microsofts surface tablet changes the gameplan a bit. There aren't any frameworks that support Win8 RT yet, but I expect a few to be able to do so. (At least marmalade and other low level frameworks)Anonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.comtag:blogger.com,1999:blog-107427964630416391.post-6989537302636660952012-03-29T11:54:00.001+02:002012-03-29T11:55:37.999+02:00Choosing a mobile framework: Investigating Moai SDK<i>This is a follow-up for <a href="http://www.bitelm.com/2012/02/choosing-mobile-dev-platformframework.html">Choosing mobile dev platform/framework</a> and looks closer at the ninth step - Investigating each framework. Here we look at </i><i>Moai SDK. I'll go through each of the steps from the <a href="http://www.bitelm.com/2012/03/choosing-mobile-framework-investigating.html">common post</a> for all the frameworks.</i><br />
<br />
<h3>
<span style="font-size: large;">
Is it easy to get started?</span></h3>
I downloaded the SDK and compared to the previous tested frameworks, it isn't as "tool-centric". Use any editor you like, and use the command line to compile. Even if you don't know anything about the lua language, you should be up and running within 30-60 minutes, depending on your installation/setup skills.<br />
<br />
<h3>
<span style="font-size: large;">
Is it easy to extend the library?</span></h3>
As I originally started with C, assembly and moved onto C++, I would say that it is really easy to extend this library. However, if you don't know C++, you are stuck. There is some documentation on extending <a href="http://getmoai.com/wiki/index.php?title=Extending_Moai_SDK">here</a>, although it's more a reference than an example. If you are familiar to C++, check the source on github, and you should be able to extend the library without too much hassle. (Just be sure you don't write platform dependant C++ code... use std libs, don't use MS/Apple/Google c++ libs.)<br />
<h3>
<span style="font-size: large;"><br /></span></h3>
<h3>
<span style="font-size: large;">
Features I need for my project</span></h3>
<h4>
</h4>
<h4>
Drag and Drop</h4>
There is no "native" drag and drop, so it's a matter of writing routines that handle graphics and touch. Moai has good routines for both 2d and 3d. As I need 2d, I can use Box2d in order to handle collisions, which again can be used for drag and drop. Should be ok to implement.<br />
<h4>
</h4>
<h4>
Text to Speech</h4>
No support for that, so have to extend the library.<br />
<h4>
</h4>
<h4>
Basic image manipulation</h4>
Rotation and scaling, which is the basics. I do need some filters (Gray, inverse, opacity), which might need to be c++ extensions, depending on performance. Lua is usually quite fast, but it depends on the framework, and compiler.<br />
<h4>
</h4>
<h4>
Basic access to camera</h4>
When I saw the MOAICamera Class, I thought: cool, native camera access. But that was the camera "viewing" your objects. So this has to be written as an extension.<br />
<h4>
</h4>
<h4>
Drawing/painting</h4>
Here is where MOAI is really easy. As long as you know how to program your paiting/drawing, it's just to use the <a href="http://getmoai.com/docs/class_m_o_a_i_draw.html">MOAIdraw</a> class.<br />
<h4>
</h4>
<h4>
JSON support</h4>
Has built in parser for parsing between json string and a lua table. Read about <a href="http://getmoai.com/docs/class_m_o_a_i_json_parser.html">MoaiJsonParser here</a>.<br />
<h4>
</h4>
<h4>
HTTP(S)-request support</h4>
<a href="http://getmoai.com/docs/class_m_o_a_i_http_task_base.html">MoaiHttpTaskBase</a> does all the http magic. And if you need a cloud for your data, there is <a href="http://getmoai.com/platform/moai-cloud.html">Moai Cloud</a> as well. Looking at the cheap pricing for Moai cloud, I'm starting to consider if that is a better option than running my own service on a VPS.<br />
<h4>
</h4>
<h4>
Custom user interface placement and user interface elements</h4>
Nothing as of now, so you have to paint/draw, or use images. It is on the roadmap.<br />
<h4>
</h4>
<h4>
Audio playback (simple)</h4>
The audio engine in Moai is Untz open-source library, which seems quite capable from what I found about it. Retronyms is behind it and they do create some great audio applications. Playing a file was as simple as load, setting volume, setting looping, and then play. This is the kind of audio playback I like to see in a framework. Not too fancy, yet really powerful.<br />
<h2>
</h2>
<h3>
<span style="font-size: large;">
Deployment to supported platforms</span></h3>
Both <a href="http://getmoai.com/wiki/index.php?title=Building_Moai_Games_For_Android_Devices">Android</a> and <a href="http://getmoai.com/wiki/index.php?title=Building_Moai_Games_For_iOS_Devices">iOS </a>deployment is quite straight forward. You need a mac for iOS, and either mac or PC for Android.<br />
<h3>
<span style="font-size: large;"><br /></span></h3>
<h3>
<span style="font-size: large;">
Pricing</span></h3>
CPAL license! That's really impressive. Such a powerful framework is released under CPAL! You can ask them to buy a commercial license if you need to modify the framework and you don't wish to release your changes.<br />
<h3>
<span style="font-size: large;"><br /></span></h3>
<h3>
<span style="font-size: large;">
Direct access to APIs</span></h3>
No. You need to write extensions. Wrap them as small as possible, and you will soon have access to what you need.<br />
<h3>
<span style="font-size: large;"><br /></span></h3>
<h3>
<span style="font-size: large;">
Programming Language</span></h3>
Lua and/or C++. C++ isn't recommended as the main language, as it does require some changes to the framework. Most likely both C++ and Lua is needed when you hit the wall on what the framework can do.<br />
<h3>
<span style="font-size: large;"><br /></span></h3>
<h3>
<span style="font-size: large;">
Documentation</span></h3>
They are working on the <a href="http://getmoai.com/wiki/index.php?title=Main_Page">docs/wiki</a>, and that is somewhat needed. Although the samples in the SDK should be enough for most of your questions. The <a href="http://getmoai.com/docs/">API documentation</a> is OK, but it still needs a bit of work.<br />
<h3>
<span style="font-size: large;"><br /></span></h3>
<h3>
<span style="font-size: large;">
Supported platforms</span></h3>
<ul>
<li>iOS (3.0 and above)</li>
<li>Android (2.1 and above)</li>
<li>Chrome (The browser)</li>
</ul>
<h3>
<span style="font-size: large;"><br /></span></h3>
<h3>
<span style="font-size: large;">
Summary</span></h3>
Moai is a mature, and going forward, and has a proper <a href="http://getmoai.com/blog/moai-2012-roadmap.html">Roadmap</a> (Not all frameworks do). I think Moai has the potential to be one of the leading cross-platform frameworks. It really depends on where they go from now. I hope they consider native Windows/OS X as platforms as well, as having one codebase for both mobile and desktop is ideal. Although it's fully possible to use chrome on both platforms, it's not really the same as having native apps on desktop.
<br />
For Mobile, I think Moai is a winner, both in pricing, features and future roadmap. It's quite powerful and feature-rich, and it's likely to become even more powerful and feature-rich.Anonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.comtag:blogger.com,1999:blog-107427964630416391.post-77258175117396331622012-03-21T14:54:00.002+01:002012-03-29T10:27:52.357+02:00Version numbers on software with git, tags and sedAfter I moved from svn to git, I started doing some version numbers manually, as I felt I never had the time to figure out a good way to set version numbers using git. However, it's much easier and simpler than in svn, when done right.<br />
<br />
Usually I use version numbers like this: 1.0.290, where 1 is major, 0 is minor and 290 the revision number.<br />
<br />
I wanted to keep the same pattern when using git, as a lot of the software I maintain is moved from svn, where the revision was used directly from the svn repository.<br />
<br />
Instead of having automatic revision numbers, I'm only going to increase them when I want to. That makes it possible to commit without increasing the revision number, which is really want I wanted to do.<br />
<br />
Here's a quick and simple way to do it:<br />
Tag your repository v1.0.290 (or similar)<br />
<br />
To get the major, minor and revision number, git hash and number of commits since that tag:<br />
<br />
<pre class="prettyprint lang-sh">echo "major:"
git describe --tags --long | sed "s/v\([0-9]*\).*/\1/"
echo "minor:"
git describe --tags --long | sed "s/v[0-9]*\.\([0-9]*\).*/\1/"
echo "revision:"
git describe --tags --long | sed "s/v[0-9]*\.[0-9]*\.\([0-9]*\).*/\1/"
echo "commits since tag:"
git describe --tags --long | sed "s/v[0-9]*\.[0-9]*\.[0-9]*-\([0-9]*\).*/\1/"
echo "git_hash:"
git describe --tags --long | sed "s/v[0-9]*\.[0-9]*\.[0-9]*-[0-9]*-g\(.*\)/\1/"
</pre>
<div>
<br />
It should give an idea on how to get the data needed to update a project with the corresponding version info.<br />
<br /></div>Anonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.comtag:blogger.com,1999:blog-107427964630416391.post-88396296156504067562012-03-07T15:23:00.000+01:002012-03-21T15:05:52.408+01:00Choosing a mobile framework: Investigating Appcelerator Titanium<div>
<i>This is a follow-up for <a href="http://www.bitelm.com/2012/02/choosing-mobile-dev-platformframework.html">Choosing mobile dev platform/framework</a> and looks closer at the ninth step - Investigating each framework. Here we look at </i><i>Appcelerator Titanium. I'll go through each of the steps from the <a href="http://www.bitelm.com/2012/03/choosing-mobile-framework-investigating.html">common post</a> for all the frameworks.</i></div>
<div>
<br />
<span style="font-size: large;">Is it easy to get started?</span><br />
Installing Titanium Studio is very simple, and if you know how to write javascript, you should be able to create a project and be up and running within minutes after installing.<br />
<br />
<span style="font-size: large;">Is it easy to extend the library?</span><br />
You can write modules, which is fairly simple. There are thorough guides on how to do it here: (<a href="https://wiki.appcelerator.org/display/guides/Extending+Titanium">https://wiki.appcelerator.org/display/guides/Extending+Titanium</a>).<br />
<br />
<span style="font-size: large;">Features I need for my project</span><br />
<b>Drag and Drop</b><br />
There are several discussions on their forum (<a href="http://developer.appcelerator.com/question/33871/dragging-and-dropping-elements#answer-82651">here</a>), but nothing is native. Since it's javascript you can usually try the usual suspects (jquery drag and drop, sencha etc) to see how they perform. I found drag and drop performance to be way below par on slower android devices, as well as on iPhone 3GS.<br />
<br />
<b>Text to Speech</b><br />
You have to write and extension, which is quite simple.<br />
<br />
<b>Basic image manipulation</b><br />
There is some manipulation options through the image view, however, you cannot modify the image source, only how you view it. If that is what you want, you are all set. (Read about it here: <a href="http://developer.appcelerator.com/apidoc/mobile/latest/Titanium.UI.ImageView-object">Titanium.UI.ImageView</a>)<br />
<br />
<b>Basic access to camera</b><br />
Yes, through the <a href="http://developer.appcelerator.com/apidoc/mobile/latest/Titanium.Media-module">Media module</a>. Would probably be better if it was in a separate module, as I see the connection between media and Camera, but I also feel they should be separate.<br />
<br />
<b>Drawing/painting</b><br />
You can use a webview with a canvas, or write it in javascript. See this example for how to write an animation: (<a href="https://wiki.appcelerator.org/display/guides/Animations">https://wiki.appcelerator.org/display/guides/Animations</a>). Or there is the paint module found here: <a href="https://github.com/appcelerator/titanium_modules">https://github.com/appcelerator/titanium_modules</a>. I found this module quite easy to use, and it seems updated quite frequently. I think Drag and Drop might be better to implement through the paint module, as it will probably give better performance than using UI widgets in a view.<br />
<br />
<b>JSON support</b><br />
Supported in the framework (<a href="http://developer.appcelerator.com/apidoc/desktop/latest/Titanium.JSON-module">Titanium.JSON</a>)<br />
<br />
<b>HTTP(S)-request support</b><br />
Supported in the framework (<a href="http://developer.appcelerator.com/apidoc/desktop/latest/Titanium.Network-module">Titanium.Network</a>)<br />
<br />
<b>Custom user interface placement and </b><b>user interface elements</b><br />
Yes, either through extending a UI widget, using a webview, or just paiting directly. You probably have to extend on per-platform basis, depending on how you implement it.<br />
<br />
<b>Audio playback (simple)</b><br />
Through the <a href="http://developer.appcelerator.com/apidoc/desktop/latest/Titanium.Media.Sound-object.html">Titanium.Media.Sound</a> object. It's limited by formats supported by the platform as well as the number of channels supported by the platform.<br />
<br />
<span style="font-size: large;">Deployment to supported platforms</span><br />
This is one of the great things about Titanium. If your code isn't too complicated and isn't using too many 1-platform-only calls, you should be able to load up your project from either a mac or a PC and recompile/process for the platform you need to. It's then a matter of following simple guides on how to sign your app and submitting to each of the respective markets.<br />
<br />
<span style="font-size: large;">Pricing</span><br />
It's free to get started, however you will probably end up looking at their $49 per month indie pricing, as you are given access to some modules that might come in handy. Paypal module, urban airship, brightcove (to mention a few.<br />
<br />
<span style="font-size: large;">Direct access to APIs</span><br />
Through writing modules/javascript wrappers. You should be able to use most native apis that way, but some might be harder to implement than others.<br />
<br />
<span style="font-size: large;">Programming Language</span><br />
JavaScript and/or CSS/HTML5 for hybrid apps. Javascript for pure native apps. For desktop development, ruby, php and python is also supported. Although if you are creating an app for both desktop and mobile, you are stuck with JavaScript.<br />
<br />
<span style="font-size: large;">Documentation</span><br />
This is one of the strong points for Titanium. The documentation is really good, well sorted, easy to navigate, and if you need more information there is always the forums. However, people seem to be a bit reluctant on helping each other out compared to other frameworks. That might be because the platform is still quite new, and needs to establish a "core" developer fanbase.<br />
<br />
<span style="font-size: large;">Supported platforms</span><br />
<ul>
<li>iOS</li>
<li>Android </li>
<li>Bada (beta)</li>
<li>Windows
(Through their desktop SDK)</li>
<li>OS X (Through their desktop SDK)</li>
</ul>
<div>
<div>
I only wish Windows Phone 7 was on that list, but that probably comes when MS accepts using native code on Windows Phone 7.<br />
<br /></div>
<div>
<span style="font-size: large;">Other features</span><br />
Marketplace: (<a href="https://marketplace.appcelerator.com/landing">https://marketplace.appcelerator.com/landing</a>)<br />
Quite a few modules on github: (<a href="https://github.com/appcelerator/titanium_modules">https://github.com/appcelerator/titanium_modules</a> and there are plenty more if you search for appcelerator modules)<br />
<br /></div>
<div>
<span style="font-size: large;">Summary</span><br />
All inn all a really well documented and supported framework. If I was going to write a business app, I would probably choose it, but for a project that is partially game/app, it's not suited. Their target audience is information and media rich applications. E.g. apps connecting to facebook, twitter, flickr, or another web service, and giving you information, or letting you interact with it. If you are looking into apps that are somewhere between a game and application, you can use the framework, there is just other options for you that are better out there.</div>
<div>
<br /></div>
</div>
<br /></div>Anonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.comtag:blogger.com,1999:blog-107427964630416391.post-52944658776372249582012-03-01T21:14:00.002+01:002012-03-03T21:55:02.012+01:00Choosing a mobile framework: Investigating Marmalade SDK<i>This is a follow-up for <a href="http://www.bitelm.com/2012/02/choosing-mobile-dev-platformframework.html">Choosing mobile dev platform/framework</a> and looks closer at the ninth step - Investigating each framework. Here we look at (Made with) Marmalade SDK. I'll go through each of the steps from the <a href="http://www.bitelm.com/2012/03/choosing-mobile-framework-investigating.html">common post</a> for all the frameworks.</i><br />
<div>
<br />
<span style="font-size: large;">Is it easy to get started?</span><br />
As expected, Marmalde should have you up and running in no-time.<br />
Register an account.<br />
Register for a free trial<br />
Download SDK<br />
Open the example project, compile, run simulator. All working within a couple of minutes after download. Note: I already had MS Visual Studio 2008 installed. (<br />
<br />
<span style="font-size: large;">Is it easy to extend the library?</span><br />
It's quite easy to extend Marmalade through the <a href="https://www.madewithmarmalade.com/devnet/docs#/main/extensions.html">Marmalade EDK</a>, it's documented enough to get you started, and after an initial attempt to write an simple extension on Windows it was easier than I suspected. I'm guessing it's quite the same for other platforms. There's also a <a href="https://www.madewithmarmalade.com/devnet/tutorials/edk-tutorial">video </a>going through the steps for android and ios.<br />
<br />
<span style="font-size: large;">Features I need for my project</span><br />
<b>Drag and Drop</b><br />
Reading from the documentation, what is needed is a combination of S3E Pointer as well as graphics. This is basically what I assumed before looking at the framework, as the framework is pretty "raw" in concepts.<br />
<br />
<b>Text to Speech</b><br />
Must be implemented through an extension.<br />
<br />
<b>Basic image manipulation</b><br />
Marmalade provides two apis for handling graphics Iw2D and IwGx. Both seems quite capable of graphics manipulation, although I had to dig further to find details about image manipulation (<a href="https://www.madewithmarmalade.com/devnet/docs#/api/iwutilapidocumentation/iwutilapioverview/imagemanipulation.html">docs here</a>). It only documents how to load an image. I do find it somewhat easier to load and work with images through Iw2D. If you are doing 2d games, it might be a good idea to look at IwGame Engine (<a href="http://www.drmop.com/">drmop.com</a>). It leverages sprite and graphics stuff and makes it a bit easier (and most likely faster) to develop 2d programs for Marmalade.<br />
<br />
<b>Basic access to camera</b><br />
All done through S3E Camera capture. Quite simple and easy to use. It is in Beta at the moment, but it seems to work quite well. (<a href="https://www.madewithmarmalade.com/devnet/docs#/api/s3eapidocumentation/s3ecameracapture/s3ecameracaptureoverview.html">documentation here</a>)<br />
<br />
<b>Drawing/painting</b><br />
Can be done through Iw2D, IwGX or directly using openGL. IwGame Engine makes 2d drawing a bit easier, although all require quite some work to do what you want. There's also a lot of plugins in the marmalade git repository (<a href="https://github.com/marmalade/iw2dsprite">github.com/marmalade/</a>). For sprites it might be worth looking at iw2dSprite.<br />
<br />
<b>JSON support</b><br />
None. Since JSON is provided in C, it's a breeze to implement it within Marmalade which already compiles C/C++.<br />
<br />
<b>HTTP(S)-request support</b><br />
<a href="https://www.madewithmarmalade.com/devnet/docs#/api/iwhttpapidocumentation.html">IwHTTP</a> handles both and is quite straightforward. You probably want to wrap it in a class to handle your data and server connections, but it's quite capable.<br />
<br />
<b>Custom user interface placement and </b><b>user interface elements</b><br />
Since marmalade is mostly hardcore drawing using one of the drawing interfaces, this is one of the areas Marmalade is strong. And since it recently implements UI Builder + native UI, you can even combine them.<br />
<br />
<b>Audio playback (simple)</b><br />
Done through <a href="https://www.madewithmarmalade.com/devnet/docs#/api/s3eapidocumentation/s3eaudio/s3eaudiooverview.html">S3E Audio</a>. Simple and straight forward sound playback of mp3, wav etc. Usually limited by what the platform supports. For streaming audio, <a href="https://www.madewithmarmalade.com/devnet/docs#/api/s3eapidocumentation/s3esound.html">S3E Sound</a> can be used. It streams raw audio directly. There is a warning on using both simultaneously, as the platform usually has limits on how many tracks it handles.<br />
<br />
<span style="font-size: large;">Deployment to supported platforms</span><br />
There are quite a few steps for each platform, so you probably want to script things up if you are updating your apps on a frequent basis, however, many of the steps are one-off per application, so it's not that much work. Read more in the <a href="https://www.madewithmarmalade.com/devnet/docs#/main/platformguides.html">platform guides</a>,<br />
<br />
<span style="font-size: large;">Pricing</span><br />
Free trial/non-commercial. $199 for standard licencse, which gives you a marmalade splash-screen on your apps or pro at $499. both is per seat, per year. Neither expensive nor cheap. You can also get the entire sdk for free for commercial use by applying to thier <a href="https://www.madewithmarmalade.com/apps-program">apps program</a>. If you are unsure on how popular your app will become, go for the apps program, as you might be lucky that it takes off because of other apps distributed through the apps program. However, you get 80% after the store owner has taken their cut. Example: for a $1 app, you get 56 cent. 0,14 to marmalade and 0,30 to the store owner (apple or google that have 70/30 split).<br />
<br />
<span style="font-size: large;">Direct access to APIs</span><br />
Through EDK, so that's fine.<br />
<br />
<span style="font-size: large;">Programming Language</span><br />
C/C++, code is compiled and run through a VM. Would most likely give close to native speed. Seems to wrap up openGL on each platform as well as a few other apis into common api.<br />
<br />
<span style="font-size: large;">Documentation</span><br />
Plenty of documentation, although it's not really that well organized. You are likely to find documentation for every bit and piece of Marmalade, but<br />
<br />
<span style="font-size: large;">Supported platforms</span><br />
Now this one is a big plus for Marmalade!<br />
Supported platforms under support agreement:<br />
<ul>
<li>iOS (3.0 and above)</li>
<li>Android (1.5 and above)</li>
<li>Symbian (Symbian^3 and S60 5th Edition)</li>
<li>bada (all versions)</li>
</ul>
<div>
Beta supported platforms (Not under support agreement)</div>
<div>
<ul>
<li>LG TV</li>
<li>BlackBerry Tablet OS</li>
<li>Windows desktop (full-screen and windowed applications)</li>
<li>OSX desktop (full-screen and windowed applications)</li>
<li>Windows Mobile 6.x</li>
<li>Symbian S60 3rd Edition</li>
<li>Mobile Linux, generic implementation</li>
</ul>
<div>
Quite an impressive number of supported platforms for being able to write native performance apps. </div>
<div>
<br /></div>
<div>
<span style="font-size: large;">Other features</span><br />
<b>Code Community</b><br />
Code community with some libraries/plugins etc: <a href="https://www.madewithmarmalade.com/devnet/code-community">www.madewithmarmalade.com/devnet/code-community</a> /<br />
The community is found on github here: <a href="https://github.com/marmalade">github.com/marmalade</a><br />
<br /></div>
<span style="font-size: large;">Summary</span></div>
<div>
There is a learning curve to the platform as the API is quite basic. It's a low level framework to give you a common low-level api for multiple platforms. If you are writing a lot of 2d games, you probably want to add a 2d game library on top of Marmalade. Since there are tons of pure c/c++ libraries out there, you should be able to easily incorporate e.g Box2D and similar libraries to make development faster and easier. If you are used to writing everything from scratch, Marmalade offers a lot of flexibility. Comparing the price to the list of features and supported platforms, I would say you get a lot for every penny. However, this is not the library for you if you want a complete ready-to-go game engine or app engine. You have to be prepared to write a lot of the basics from scratch, which you might get directly in other frameworks.</div>
<div>
<br /></div>
<div>
<i>Update 02/03-12: Added other features</i></div>
<i>Update 03/03-12: </i><i>Added information about supported platforms, programming language, documentation and overall summary.</i></div>Anonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.comtag:blogger.com,1999:blog-107427964630416391.post-86751115594173758262012-03-01T18:43:00.001+01:002012-03-29T11:54:54.633+02:00Choosing a mobile framework: Investigating frameworks<i>This is a follow-up for <a href="http://www.bitelm.com/2012/02/choosing-mobile-dev-platformframework.html">Choosing mobile dev platform/framework</a> and looks closer at the ninth step - Investigating each framework. </i><br />
<i><br /></i><br />
I'm not going to look at how simple it is to write a "hello world" app, as that might be very easy, although the framework might be horrible. It's pointless to compare a framework based on "hello world".<br />
<br />
For each framework I'm going to look at the following key points:<br />
<ul>
<li>Is it easy to get started?</li>
<li>Is it easy to extend the library? (If some functionality is needed cross-platform)</li>
<li>I'll briefly investigate these features, as they are needed for my current project:</li>
<ul>
<li>Drag and Drop</li>
<li>Text to Speech</li>
<li>Basic image manipulation</li>
<li>Basic access to camera</li>
<li>Drawing/painting</li>
<li>JSON support</li>
<li>HTTP(S)-request support</li>
<li>Custom user interface placement</li>
<li>Custom user interface elements</li>
<li>Audio playback (simple)</li>
</ul>
<li>Deployment to supported platforms</li>
<li>Pricing (Yes, I think pricing is important to look at, although there is always a hidden cost for a "free" framework: usually workload )</li>
<li>Direct access to platform APIs or are they wrapped? (sometimes, you need to dig deeper than the framework wants you to....)</li>
</ul>
<div>
The frameworks in details:</div>
<div>
<ol>
<li><a href="http://www.bitelm.com/2012/03/choosing-mobile-framework-investigating_01.html">Marmalade SDK</a></li>
<li><a href="http://www.bitelm.com/2012/03/choosing-mobile-framework-investigating_07.html">Appcelerator Titanium</a></li>
<li><a href="http://www.bitelm.com/2012/03/choosing-mobile-framework-investigating_29.html">Moai SDK</a></li>
</ol>
</div>Anonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.comtag:blogger.com,1999:blog-107427964630416391.post-90005616929925191982012-03-01T09:59:00.004+01:002012-03-01T09:59:52.488+01:00Choosing a mobile framework: Web-based, appstore, desktop, mobile?<br />
<div>
<i>This is a follow-up for <a href="http://www.bitelm.com/2012/02/choosing-mobile-dev-platformframework.html">Choosing mobile dev platform/framework</a> and looks closer at the eigth step - Choose if your app should be web-based or an actual app in the appstore or both, should it be mobile and desktop?</i><br />
<br />
As mentioned in the original post, this one isn't always easy to figure out. Usually you want your app/product to target all end-users, no matter what kind of device they are on. Usually you can list up a couple of questions to figure out how to choose target devices/platforms.<br />
<br />
<div>
<span style="font-size: large;">Who is your target audience?</span>
</div>
<div>
For this project I do need to integrate into VLEs (virtual learning environments) at some point, but more customers require the end-product to be accessible on mobile devices. This points me towards a web-page/web-based application, as some of the code most likely can be ported between a mobile app and a VLE integration. My target audience uses both mobile and desktop platforms, so I need to target both.</div>
<div>
<br /></div>
<div>
<span style="font-size: large;">Should the application be mobile, desktop or both?</span></div>
<div>
Second, I look at what sizes/types of devices I need to target. My current project needs both to be accessible through desktop computers, as well as through mobile devices. Again this points to having a common code-base, so a web-app would be great. Although some frameworks provide this for native as well.</div>
<div>
<br /></div>
<div>
<span style="font-size: large;">Can the application be a web-page, or do you need it to be native?</span>
</div>
<div>
So it all comes down to the question of being native on a given platform. That is usually a question of performance, but not necessarily. As there will be some animation, drag and drop, sound, I think from a performance perspective it would be good to have a native app, but if a web-app performs OK, why shouldn't I just wrap that in any of the app-wrappers?</div>
<div>
<br /></div>
<div>
When looking at performance, always test on low-end devices. If you can get your app to perform well there, high-end devices shouldn't be a problem. For instance, I created a small drag and drop test in a web-app wrapper. Works perfectly well on a Samsung Galaxy S2 and Galaxy Tab 10.1, terrible on the iPad 1, bad on an iPad 2, and is totally unusable on a HTC Wildfire as well as on a Samsung Omnia 7.</div>
<div>
<br /></div>
<div>
Basically that is because of the JavaScript interpreter on each of the platforms is different, as well as the raw cpu-horsepower. I'm surprised to see that much difference between iPad 2 and Galaxy Tab, but I know Google and Apple has various JavaScript engines in each of their respective WebKit code bases. As for the Wildfire, I'm not surprised, as it had Android 2.1 and only a 600 MHz cpu, which isn't really that much.</div>
<div>
<br /></div>
<div>
However, the app I created was a quite simple animation, as well as some drag and drop touch gestures, so I suspected it to perform better.</div>
<div>
<br /></div>
<div>
This points me towards a native app instead.</div>
<br />
So 2 points for web-based, 1 for native. Although the performance factor is more important than common code-base, so I would say it's a draw for now. I will review this when looking at each of the frameworks later on.</div>Anonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.comtag:blogger.com,1999:blog-107427964630416391.post-17123852137387384922012-02-22T11:10:00.002+01:002012-03-07T15:37:37.741+01:00Choosing a mobile framework: Make a list over frameworks you do want to look into<div>
<div>
<i>This is a follow-up for <a href="http://www.bitelm.com/2012/02/choosing-mobile-dev-platformframework.html">Choosing mobile dev platform/framework</a> and looks closer at the seventh step -
Make a list over frameworks you do want to look into ?</i><br />
<br />
It is important to list any framework you think it's worth considering, and probably a few that you don't think is worth considering, as it's quite critical to select the right framework in order to save development time after you have learned a framework.<br />
<br />
This is not the step where you start looking at every detail about the framework, only find them, briefly read about them, and decide yes/no on the question: Can I consider this framework?</div>
<div>
<br /></div>
<div>
The frameworks I consider for this project are:</div>
<div>
<ul>
<li>Appcelerator Titanium (<a href="http://appcelerator.com/">appcelerator.com</a>)</li>
<li>Phonegap (Apache Cordova) (<a href="http://phonegap.com/">phonegap.com</a>)</li>
<li>Native iOS + Native Android (And perhaps native WinMo)</li>
<li>Rhomobile (<a href="http://rhomobile.com/">rhomobile.com</a>)</li>
<li>Corona SDK (<a href="http://anscamobile.com/">anscamobile.com</a>)</li>
<li>Moai SDK (<a href="http://getmoai.com/">getmoai.com</a>)</li>
<li>Mosync (<a href="http://mosync.com/">mosync.com</a>)</li>
<li>Mono for iOS/Android (<a href="http://xamarin.com/monotouch">xamarin.com/monotouch</a> / <a href="http://xamarin.com/monoforandroid">xamarin.com/monoforandroid</a>)</li>
<li>Edgelib (<a href="http://www.edgelib.com/">edgelib.com</a>)</li>
<li>Unity 3D (<a href="http://unity3d.com/">unity3d.com</a>)</li>
<li>Marmelade (
<a href="http://www.madewithmarmalade.com/">madewithmarmalade.com</a>) (Earlier known as airplay sdk)</li>
<li>appMobi (<a href="http://appmobi.com/">appmobi.com</a>)</li>
<li>App Game Kit (<a href="http://www.appgamekit.com/">appgamekit.com</a>)
</li>
<li>Cocos2D-x (<a href="http://www.cocos2d-x.org/">cocos2d-x.org</a>)</li>
<li>Others I might not have found yet?</li>
</ul>
<div>
You shouldn't even consider pricing of the frameworks at this point, although that might be a major factor to considering a framework.</div>
</div>
<div>
<br /></div>
<div>
You might want to consider the platforms you are choosing when looking at various frameworks. For instance, I prefer if a frameworks supports all the platforms I want to target, but for now, I will consider platforms that support at least iOS and Android.</div>
<div>
<br /></div>
<div>
Why don't I include AppInventor, DragonRad, DroidDraw and other click-and-create frameworks? Well... Quite simple: Try to do graphics/drawing/physics in any of them. If you just need forms/push buttons and sending/getting data to/from a website, sure, use any of them, but as soon as you move out of what a click-and-create framework can provide, you should consider a real framework where you can do some actual coding.</div>
<div>
<br /></div>
<div>
If you are going to be a mobile developer, be sure to choose a framework that doesn't limit you from your creative ideas.<br />
<br />
<i>Update 7/3: Added cocos2d-x</i></div>
</div>Anonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.comtag:blogger.com,1999:blog-107427964630416391.post-90015986201363862402012-02-17T23:31:00.000+01:002012-02-17T23:31:26.934+01:00Choosing a mobile framework: Any programming languages you want to avoid?<i>This is a follow-up for <a href="http://www.bitelm.com/2012/02/choosing-mobile-dev-platformframework.html">Choosing mobile dev platform/framework</a> and looks closer at the sixth step - Any programming languages you want to avoid?</i><br />
<i><br /></i><br />
It's better to consider languages you want to avoid rather than listing the ones you do want to dig into. If you start selecting a programming language, you will limit the number of frameworks you can consider.<br />
Example: you only want to write in C#, there is Mono/Monotouch and of course MS .net. But what if that platform doesn't support the features you need?<br />
<br />
Since the syntax of languages today is barely dialects of one another, you should as a developer/programmer be willing to jump into a new language, just as much as a toddler wants to learn how to walk. (Yes, I have a toddler at home).<br />
<br />
When you have learnt 3-4 programming languages, you start seeing the similarities, and the differences, and you usually figure out that most languages are very closely related. It's just another object, a function, a variable, an array here, a hash there etc.<br />
<br />
Although working on several languages can be confusing at times, always keep reference manuals close by when you are stuck.<br />
<br />
I only have one language I always want to keep away from, and that is VB. I always end up using more time figuring out why on earth Microsoft never put VB out of it's misery...<br />
<br />
When you have compiled your list over languages you don't want to learn, go through it again, and consider if you really do want do choose a framework that suits the needs for your project, or suits your programming language skills.Anonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.comtag:blogger.com,1999:blog-107427964630416391.post-30072214830137232412012-02-17T10:43:00.002+01:002012-02-17T10:44:03.719+01:00Choosing a mobile framework: Choose the platforms you need to target<i>This is a follow-up for <a href="http://www.bitelm.com/2012/02/choosing-mobile-dev-platformframework.html">Choosing mobile dev platform/framework</a> and looks closer at the fifth step - Choose the platforms you need to target</i><br />
<i><br /></i><br />
This should be fairly simple, as you just have to list up the platforms you want to support. However, it is a good idea to also consider if the application should be for smartphones, tablets and/or desktops.<br />
<br />
Here's a few examples:<br />
<ol>
<li>You are going to make a game for tablets, but the game would be just as good on a desktop. You should consider if you need to develop the same twice (1 for desktop and 1 for tablet) or if you should have 1 codebase for both.</li>
<li>You are making an app for smartphones, but hope it can be reused on tablets. Should you go all the way to desktop as well, or stick with smartphones only, and perhaps make a minor tweaked version for tablets?</li>
<li>You are going to develop a desktop-only application. My question today would be: why? Wouldn't it be better to do a desktop + tablet or web version?</li>
</ol>
<div>
Basically you end up looking at both platforms and technology, when choosing which platforms to support. I do however recommend just choosing platform and form factors in this step and if needed change when looking at frameworks.</div>
<br />
<br />
For my project I'm only targeting smartphones and tablets, however I do see that the application can work for desktop as well. So I will try to write reusable code. Reusable code is usually either very low level code or very high level code, while languages that are wrapper for low level quite often end up being quite platform specific.<br />
<br />
For instance, VB and C# libraries isn't reusable across platforms, but C and JavaScript libraries is most likely reusable. (JavaScript is depending on the platforms web browser capabilities). There are more factors than only the language, but I've more or less always been able to adopt/port a C library to any given platform.<br />
<br />
My conclusion for my current project will be to support in the following order:<br />
<ol>
<li>Android and iOS</li>
<li>Windows Phone</li>
<li>(Windows 8 Tablets, if released...)</li>
<li>Desktop</li>
</ol>
<div>
For form factors this is my priority:</div>
<div>
<ol>
<li>Tablets (7 inch and up)</li>
<li>Smartphones</li>
<li>Desktop (15 inch and up)</li>
</ol>
<div>
In a matrix to summarize:<br />
<br />
<table class="table-centered table table-striped table-bordered table-condensed">
<thead>
<tr>
<th scope="col"></th>
<th scope="col">Tablet</th>
<th scope="col">Smartphone</th>
<th scope="col">Desktop</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row" style="text-align: left;">Android</th>
<td>1</td>
<td>2</td>
<td>-</td>
</tr>
<tr class="alt">
<th scope="row" style="text-align: left;">iOS</th>
<td>1</td>
<td>2</td>
<td>-</td>
</tr>
<tr>
<th scope="row" style="text-align: left;">Windows (7 and newer)</th>
<td>4</td>
<td>-</td>
<td>3</td>
</tr>
<tr class="alt">
<th scope="row" style="text-align: left;">OS X (Lion+)</th>
<td>-</td>
<td>-</td>
<td>3</td>
</tr>
<tr>
<th scope="row" style="text-align: left;">Windows (XP/Vista)</th>
<td>-</td>
<td>-</td>
<td>5</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="4">Matrix for preferred platforms and form factors</td>
</tr>
</tfoot>
</table>
</div>
<div>
<br /></div>
<div>
Since MS is very likely to release Windows 8 with tablet support, I need to consider that already now, as that might end up being the third major tablet-market player. Why didn't I include Googles chromebook? Currently I have 0 demand for chromebook versions, and that isn't likely to change within the next 12-18 months.<br />
<br />
So focus is iOS and Android tablets first, smartphone second, then perhaps desktops (Win 7 + OS X).<br />
<br /></div>
</div>Anonymoushttp://www.blogger.com/profile/17026296374379007289noreply@blogger.com