RSS My Sample site Here my blog description http://davidpirek.com function chaining javascript tutorial http://davidpirek.com/blog/function-chaining-javascript-tutorial Apr 1 2014 12:00AM <p>Function chaining is somehow getting a popular way to structure your javascript code these days, especially with javascript promises on the rise. Here is a simple tutorial how to do function chaining in javascript.</p> <p>This is how you can do function chaining using object literal:</p> <pre> var foo = { test: function () { console.log('running test'); return this; }, bar: function () { console.log('running bar'); return this; } }; foo.test().bar(); </pre> <p>And here is how you can do function chaining using function constructor:</p> <pre> var foo = function () { this.test = function () { console.log('running test'); return this; }; this.bar = function () { console.log('running bar'); return this; }; }; var f = new foo() f.test().bar(); </pre> bitcoin trading API JavaScript http://davidpirek.com/blog/bitcoin-trading-api-node-js Jun 14 2013 12:00AM <p>Bitcoin world loves JavaScript. I was just going through <a href="https://npmjs.org/package/mtgox">API mtgox.com</a> and node.js is very well supported. This makes it a lot of fun to develop for, and also very fast. There are so many barriers to enter any real financial trading platform, (with some exception, but the new cyber currency makes it almost fun to create things high frequency trading algorythms. </p> <p>To make a real-time quote steaming is as easy as this:</p> <pre> var MtGox = require('mtgox'); var gox = new MtGox(); // Get maket stats gox.market('BTCUSD', function(err, market) { console.log(market); }); </pre> <p>Here is a link to the <a href="https://npmjs.org/package/mtgox">node.js npm package</a>.</p> online fax online failed http://davidpirek.com/blog/online-fax-failed Nov 14 2012 12:00AM <p>So it appears, that 2012 we have not still figured out how to send a Bank Of America officer a 25 page document over the internet.</p> <p>I am trying to send my scanned tax returns to a Bank Of America officer and being a online savvy guy, even a hacker, after 3 hours I have still not figured it out.</p> <p>Premise: send 25 pages of scanned PDF file to a Bank Of America loan officer.</p> <p>1 try: email: fails because scanned pdf is to large.</p> <p>2 try: uploaded the zipped file to a FTP of my website and send the guy the url: fails, the guy will just not click on it because he is scared it will get him in trouble with security.</p> <p>3. try: uploaded the zipped file to dropbox and send the guy the url: fails, the guy will just not click on it because he is scared it will get him in trouble with security.</p> <p>4. try: paid for credit at PamFax, trying to send the files via online fax: getting a message on my first file that the Bank Of America fax number is too slow, so I will be charged double the rate: second file fails because it's too large. Almost out of credit at PamFax and still no files send.</p> <p>5. try: begging the guy to click on the dorpbox links and just download the dam files: failed: no response</p> <p>6. try: googeling around how to downsize a pdf file so that I can either email it or fax it... only seeing links to adobe acrobat pro which can do it but costs around $150.</p> <p>7. try: going to a torrent site, and downloading a adobe acrobat pro with a crack, trying to install it: crack too complicated to implement, don't have time for that, so making regular trial registration for a 30 day trial, fortunately, it comes full featured for that period, so I can resize my files now... sending resized files to gay at BOF, no return messages so far... guy emails me got the files, replying back to him: "Great.... " after I send the reply email, geting a delivery failure, apparently the footer of his email with all the disclaimers does not pass their own security filters... go figure... but he got the files now... I have wasted 4 hours of my life.</p> google apps script tutorial http://davidpirek.com/blog/google-apps-script-tutorial Nov 2 2012 12:00AM <p>Few people brought to my attention <a href="https://developers.google.com/apps-script/">google apps script</a> so I did a little test drive today, and I am quite impressed. This suppose this is google's take on server-side JavaScript, that integrates with their google docs family and much more...</p> <p>Here is one use case that I tested: loading list of users from a remote JSON service, and storing it in google spreadsheet.</p> <pre> // Inserts user into spreadseet. var insertUser = function (user) { var sheet = SpreadsheetApp.getActiveSheet(); sheet.appendRow([user.name, user.userType]); }; // Request service. var service = function (url, callBack) { var response = UrlFetchApp.fetch(url), responseObj = JSON.parse(response.getContentText()); callBack(responseObj); }; // Main init function. function run() { var url = 'http://www.somedomain.com/json/user/list'; service(url, function(d){ var users = d.data.users; for (var i = 0; i < users.length; i++) { insertUser(users[i]); } }); } </pre> spell check API as a JSON rest-ful service http://davidpirek.com/blog/spell-check-api-as-a-json-rest-ful-service Nov 1 2012 12:00AM <p>I have been looking into ways to implement mulch-lingual spell checker. What I have found were some legacy code bases, and some paid-for services, nothing really suitable enough to fit my needs, so decided to write my own implementation. Here is the result of my node.js based <a href="http://www.spellcheckapi.com/">spell check API</a>.</p> <p>Here is the list of supported languages:</p> <p>1 Afrikaans (South Africa)<br />2 Amharic (Ethiopia)<br />3 Arabic (North Africa and Middle East)<br />4 Aragonese<br />5 Armenian (Eastern)<br />6 Azerbaijani (Azerbaijan, Iran)<br />7 Basque<br />8 Belarusian (Belarus )<br />9 Bengali (Bangladesh, India)<br />10 Breton (France)<br />11 Bulgarian (Bulgaria)<br />12 Catalan<br />13 Chichewa (Malawi)<br />14 Coptic (North Africa)<br />15 Croatian (Croatia)<br />16 Czech (Czech Republic)<br />17 Danish (Denmark)<br />18 Dutch (Netherlands)<br />19 English (AU,CA,GB,NZ,US,ZA)<br />20 Esperanto (anywhere)<br />21 Estonian (Estonia)<br />22 Faroese (Faroe Islands)<br />23 Persian (Iran)<br />24 Fijian (Fiji)<br />25 Finnish (Finland)<br />26 French (France, ...)<br />27 Frisian (Netherlands)<br />28 Friulian (Italy)<br />29 Galician (Spain)<br />30 Gascon (France)<br />31 German (Germany, ...)<br />32 Greek (Greece)<br />33 Gujarati (India)<br />34 Hawaiian (United States)<br />35 Hebrew (Israel)<br />36 Hindi (India)<br />37 Hungarian (Hungary)<br />38 Icelandic (Iceland)<br />39 Indonesian (Indonesia)<br />40 Interlingua (x-register)<br />41 Irish (Ireland)<br />42 Italian (Italy)<br />43 Kashubian (Poland)<br />44 Khmer (Cambodia)<br />45 Kinyarwanda (Rwanda)<br />46 Kiswahili (East Africa)<br />47 Korean<br />48 Kurdish (Syria)<br />49 Latin (x-register)<br />50 Latvian (Latvia)<br />51 Lingala (Democratic Republic of the Congo)<br />52 Lithuanian (Lithuania)<br />53 Luxembourgish (Luxembourg)<br />54 Malagasy (Madagascar)<br />55 Malay (Malaysia)<br />56 Māori (Aotearoa)<br />57 Macedonian (Former Yugoslav Republic of Macedonia)<br />58 Marathi (India)<br />59 Malayalam (India)<br />60 Mongolian (Mongolia)<br />61 Ndebele (South Africa)<br />62 Nepali (Nepal)<br />63 Northern Sotho (South Africa)<br />64 Norwegian (Norway)<br />65 Occitan (France)<br />66 Oriya (India)<br />67 Papiamentu (Netherlands Antilles)<br />68 Persian (Iran)<br />69 Polish (Poland)<br />70 Portuguese (Portugal + Brasil)<br />71 Punjabi (India)<br />72 Quechua (Bolivia)<br />73 Quichua (Ecuador)<br />74 Romanian (Romania)<br />75 Русский (Россия)<br />76 Sami<br />77 Scottish Gaelic (Scotland)<br />78 Serbian (Serbia, Republic Srpska)<br />79 Setswana (Africa)<br />80 Sinhala (Sri Lanka)<br />81 Slovak (Slovakia)<br />82 Slovenian (Slovenia)<br />83 Southern Sotho (South Africa)<br />84 Spanish (Spain, ...)<br />85 Swazi/Swati (South Africa)<br />86 Swedish (Sweden)<br />87 Tagalog (Philippines)<br />88 Tamil (India)<br />89 Tetum (Indonesia)<br />90 Thai (Thailand)<br />91 Tsonga (South Africa)<br />92 Ukrainian (Ukraine)<br />93 Urdu (Pakistan, India)<br />94 Uzbek (Uzbekistan)<br />95 Venda (South Africa)<br />96 Vietnamese (Vietnam)<br />97 Wayunaiki (Venezuela)<br />98 Welsh (Wales)<br />99 Xhosa (South Africa)<br />100 Yiddish (International)<br />101 Zulu (Africa)</p> raspberry pi server node.js ARM performance http://davidpirek.com/blog/raspberry-pi-server-nodejs-arm-performance Nov 1 2012 12:00AM <p>I have created an experimental project: <a href="http://www.raspberrypiserver.org/">running a node.js server + simple MVC CMS on the top of the Raspberry Pi</a> board. Turns out that a $25 computer can actually pull off a decent performance. In fact I have not found a load level where the server would start slowing down, did a 50 concurrent user test on it.</p> <p>There has been a discussion recently if the ARM based servers could replace conventional Intel based CPUs in the future. As ridiculous as it sounds to build servers off phone chips, I imagine that it is the same like if somebody in the 80's said that we would be running our server farms of PCs.</p> <p>As a JavaScript/node.js ninja this starts making even more sense, since ARM is the one who is really really <a href="http://blogs.arm.com/software-enablement/456-googles-v8-on-arm-five-times-better/">paying attention to the V8 open source project</a>, and the ARM chips are really trying to optimize for JavaScript performance. </p> <p>Here is another thing: joyent.com which is the main node.js hosting provider actually runs their servers on a minimal 250k memory VM instances and just scales by turning on more of these as needed. Why could not we do the same thing on per-board basis, and this way eliminate hardware failure as well as power consumption.</p> <p>node.js is single threaded which is a common criticism, so it cannot take advantage of multi-core CPUs, but maybe this is exactly why creating single core ARM (optimized for V8 JavaScript performance) board clusters is the most suitable hardware solution.</p> AT&T Billing on Mobile Purchases http://davidpirek.com/blog/att-billing-on-mobile-purchases Oct 12 2012 12:00AM <p> I got a text message today: "Your subscription for MySpace Mobile has auto-renew is on 11/11/12 Need help? Visit att.com/mobilepurchases" </p> <p>I have been getting a lot of span recently as a text message, but this one had a att.com domain name so it caught my interest. I called up the support, and the lady told me that I signed up for some kind of Java MySpace mobile app. This sounded really strange, since I have been using iPhone since it came out, and iPhone never supported Java and never charged subscription feels for it's apps. I did not really cared about the $3.20 monthly charge which does add up over the years, if fome some reason it was me to install this app sometimes in 2005, although I doubt because I am really cheap when it comes to software, especially subscriptions.</p> <p>After 5 minutes, of trying to get some sensible information how I can be charged for a java app when I am an iphone user, I got to the manager, but still no solid explanation. The only thing I was told that as a consumer I have the right to review my bill and dispute anything that goes 3 months back. The Att manager was not able to tell me when and how I made the purchase, making excuse that this is third party app, but that there is no contact information to the app provider. The funny part is that if I do a search in the app center I cannot even find anything that would resemble in name the app I have been generously paying for all those years. I did an internet search, and all the mentions about the "Cingular MySpace Mobile" date back to 2006, no mention of this app afterwards, so I suspect, it's been years since this app has been even available...</p> <p> I did a quick search and it looks like I am not the only one who felt trap, applied to things like ringtones. </p> <iframe width="560" height="315" src="http://www.youtube.com/embed/AsJEAG89pc4" frameborder="0" allowfullscreen></iframe> <p>So let me sum it up: AT&T has been charging me for product that never works on my phone and that is not even available, the fee is small enough, and the only answer I get is that as a consumer I have the right to review my bill and dispute any charges.</p> <p>UPDATE: two managers up, after all kinds of threats, and proving that this app is not even in the mobile app store, I am finally getting my money back, added up to $115 for all those years, and took me almost an hour on the phone... Apparently, anybody who has your phone number can sign you up for a recurring service that get's added to your phone bill, and if as a consumer you are not careful enough and review every dollar charge on your bill you only have 3 months to dispute this. There is a way to block this by pin, by ATT decided to turn this off by default, because obviously the revenue would suffer from not making all these mistakes, or kinds not able to make purchases without their parent's PIN.</p> raspberry pi node.js web server http://davidpirek.com/blog/raspberry-pi-nodejs-web-server Sep 12 2012 12:00AM <p>The Raspberry Pi $25 computer finally showed up in my mail. Unlike other use cases, my main purpose is to test this device as a node.js web server.</p> <p>Hardware setup: when you get all the right cables and connectors (make sure you don't pay more for the cables than for you paid for the computer, which might be a challenge to accomplish) connecting all the cables is pretty straight forward.</p> <p>Disk installation: I have wasted hours trying to get the Macbook pro card reader create a bootable SD card using the "dd" command according <a href="http://elinux.org/RPi_Easy_SD_Card_Setup">these instructions</a>. Somewhere half way through, the process crashed, and I had to start over again with no luck... what has finally worked was when I connected the SD card via a USB card reader and used windows based image maker.</p> <p>UI: Finally was able to boot, and see the "beautiful" UI of the Raspberry Pi Linux. Oh well, this is obviously not a workstation, or even an office computer, so let's move on...</p> <p>...moving to my basement, I connected The Raspberry Pi to the router (yes I did get the $35 version with the ethearnet connector), checked my router for "attached devices" to find out the IP address of The Raspberry, and connected via SSH. (forgot in the previous point, I did enable SSH in the first wizard when The Raspberry Pi was booting for the first time.</p> <p>Static IP: I plan to use this device as a node.js server, so first thing is to set up static IP. This is my template:</p> <p>type:</p> <pre> nano /etc/network/interfaces </pre> <p>paste this instead of the last line</p> <pre> # OLD # iface eth0 inet dhcp # NEW iface eth0 inet static address 192.168.1.100 netmask 255.255.255.0 network 192.168.1.0 broadcast 192.168.1.255 gateway 192.168.1.1 </pre> <p>node.js: I have used the "Debian" tutorial from the <a href="https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager">node.js website on github</a>. Goes like this:</p> <pre> apt-get install python g++ mkdir ~/nodejs && cd $_ wget -N http://nodejs.org/dist/node-latest.tar.gz tar xzvf node-latest.tar.gz && cd `ls -rd node-v*` ./configure make install </pre> <p>... taking a while, I was able to write all the above while I am waiting... ERROR</p> <p>After numerous attempts I gave up... but few weeks later I found <a href="http://www.raspberrypi.org/phpBB3/viewtopic.php?f=34&t=17647">this article</a>, and followed the directions, and YES I am running Node.js 0.8.9 on Raspberry Pi now!</p> flatplanet review, soho, best coffee in London http://davidpirek.com/blog/flatplanet-review Aug 21 2012 12:00AM <p>People used to say that England does not have good food. This is so wrong! It's my day 3 in London, and I cannot say but good things about the local cuisine.</p> <p>I have been in particular on a hunt for a good coffee, and finally found it. It's at <a href="http://www.flatplanet.co.uk/">Flatplanet</a>! Oh and the food is not bad either. Hand made organic pita breads from whole grain flower, with pizza-like toppings. In addition this place had some hipster decor, vintage speakers, and an piano downstairs that says "PLAY ME".</p> node.js mysql on windows http://davidpirek.com/blog/nodejs-mysql-on-windows Jun 29 2012 12:00AM <p>Well node.js is a second class citizen on windows after all. I have been trying to do a very simple task: connect to MySQL from node.js on a windows box. (don't ask my why), and ran into all kinds of issues with installing most of the known MySQL packages. This is mainly because of the lack of support of node-waf, which I let's you execute C++ libs out of node. The only package that I found working is the <a href="https://github.com/sidorares/nodejs-mysql-native">nodejs-mysql-native</a>. <!-- The problem with that is this particular lib, seems to be the least user friendly. I have been used to using MongoDb, and LINQ on .NET which provide nice object abstraction, so working with nodejs-mysql-native seems like a drag. --> </p> loading images via websockets in nodejs http://davidpirek.com/blog/loading-images-via-websockets-in-nodejs May 25 2012 12:00AM <p>Reading a lot recently about mobile optimization, I have started doing some HTML5 prototyping using non-traditional methods to build mobile web apps.</p> <p>One of the big bottle necks when loading websites is loading multiple images over HTTP, both the HTTP overhead and the browser connection limits make this process slow.</p> <p>One of the optimization methods used in mobile apps is inline the images in the CSS file base64 encoded. I extended this concept and using node.js server, I get the images, encode them into base64, and via client side javascript inject into the dom. ... adding another spin and inject them into the HTML5 canvas element.</p> <p>What actually took me the longest was figuring out how to load the image server-side and and encode it into base64. A lot of people seen to have issues with that, so here is the working solution:</p> <pre> var http = require('http'); // HTTP content getter. exports.get = function (settings) { if (!settings.port) { port = 80; } try { var request = http.request({ host: settings.host, port: settings.port, method: 'GET', path: settings.path }); request.on('response', function (response) { response.setEncoding('binary'); var body = ''; response.on('data', function (chunk) { body += chunk; }); response.on('end', function () { var base64 = new Buffer(body, 'binary').toString('base64'); var data_uri_prefix = "data:" + response.headers["content-type"] + ";base64,"; try { settings.callBack({ isSucessful: true, body: data_uri_prefix + base64 }); } catch (ex) { settings.callBack({ isSucessful: false, ex: ex }); } }); }); request.end(); } catch (ex) { settings.callBack({ isSucessful: false, ex: ex }); } }; </pre> <p>And here is how you can use the code:</p> <pre> loader.get({ host: 'webcam.novy-jicin.cz', path: '/record/current.jpg', callBack: function (image) { } }); </pre> <p>The next piece is using the websocekts to push it to the client. This is fairly simple, just set up a listener, with the "on" and return the encoded string.</p> <pre> socket.on('imageRequest', function (obj, callBack) { loader.get({ host: 'webcam.novy-jicin.cz', path: '/record/current.jpg', callBack: callBack }); }); </pre> <p>And now the client:</p> <pre> var canvas = document.getElementById("myCanvas"); var context = canvas.getContext("2d"); var imageObj = new Image(); imageObj.onload = function() { context.drawImage(imageObj, 0, 0); }; var refreshImage = function () { socket.emit('imageRequest', '', function (data) { imageObj.src = data; refreshImage(); }); }; // Start refresher. refreshImage(); </pre> <p>In my example, I am hitting a webcam in a loop of my home town, so that it's simulating a video stream.</p> node.js on Windows Server 2008 http://davidpirek.com/blog/nodejs-on-windows-server-2008 May 22 2012 12:00AM <p>Well I used to be a .NET guy after I switched to node.js Windows Server 2008 was my daily bread. Over the past few years I learned how to program linux servers in all flavors, just so that I could run node.js.</p> <p>Since Microsoft has been heavily supportive of node.js within their Azure hosting, as well as Visual Studio, I have been poking around little bit and the development setup and user experience is actually not all that bad :) You can now very easily install node.js on Windows Server 2008 and even get some basic project setup from Visual Studio 2010 Express.</p> <p>Power Shell actually behaves very close to shell, well not quite, but for using basic node.js it's good enough. You can use the package manager, the "ls" command still works, if you type node -v it does give you the latest version. There was nothing like this when I started doing node.js, which forced me to become a linux guy, but now, maybe I would get just lazy and use Windows Server 2008 as my node.js hosting/dev platform :)</p> <p>The nice part is that you can actually run node.js through IIS, which will take care of your domain routing, probably some error handeling too. Here is a tutorial that shows how to set up your <a href="http://www.amazedsaint.com/2011/09/creating-10-minute-todo-listing-app-on.html">node.js website on IIS</a>.</p> <img src="http://www.davidpirek.com/img/l/fbeed1df-f81c-4dc9-ae78-bd539d5b48e5.png" /> AMD JavaScript require.js alternative http://davidpirek.com/blog/amd-javascript-requirejs-alternative May 10 2012 12:00AM <p> Looking at require.js, it seems way too much code for two little functions, so I took the specs of AMDjs and created my own implementation: </p> <pre> (function(){ // Gloab settings. var namespace = 'APP', filePath = 'js/', filePrefix = 'APP.'; // Set app namespace. window[namespace] = {}; // Script loader. var loadScript = function(key, sScriptSrc, callBack) { // Gets document head element. var oHead = document.getElementsByTagName('head')[0]; if(oHead) { // Creates a new script tag var oScript = document.createElement('script'); // Adds src and type attribute to script tag. oScript.setAttribute('src', sScriptSrc); oScript.setAttribute('type','text/javascript'); // Calling a function after the js is loaded (IE). var loadFunction = function() { if (this.readyState == 'complete' || this.readyState == 'loaded') { callBack(key); } }; oScript.onreadystatechange = loadFunction; // Calling a function after the js is loaded (Firefox). oScript.onload = function(){ callBack(key); }; // Append the script tag to document head element. oHead.appendChild(oScript); } }; // For each, takes arrays and objects. var forEach = function(a, callBack){ var length = a.length; // Object does not have .length propery. if(length) { for (var i = 0; i < length; i++){ callBack(i, a[i]); } } else { for (var key in a) { callBack(key, a[key]); } } }; var handleDependencies = function(dependencies, callBack) { var _dependencies = [], length = dependencies.length, remoteModules = {}, index = 0; // Check if dependancies were defind. if(length === 0) { callBack(_dependencies); return true; } // Check if dependancies exist, load them otherwise. forEach(dependencies, function(i, d){ // Decide if dependancy is loaded. if(window[namespace][d]) { index++; _dependencies.push(APP[d]); if(length === index) { callBack(_dependencies); } } else { // TODO: sometimes there is overlap in dependancy loading, // which causes wrong callback timing: fix. loadScript(d, filePath + filePrefix + d + '.js', function(key){ index++; _dependencies.push(APP[key]); if(length === index) { callBack(_dependencies); } }); } }); }; // Global 'define' AMD method. window.define = function(name, dependencies, callBack){ handleDependencies(dependencies, function(d){ window[namespace][name] = callBack.apply(this, d); }); }; // Global 'require' method. window.require = function(dependencies, callBack){ handleDependencies(dependencies, function(d){ callBack.apply(this, d); }); }; }()); </pre> <p>Here is the <a href="https://github.com/dpirek/AMD-js">github sample code</a>.</p> linkedin.com touch code review http://davidpirek.com/blog/linkedin-com-touch-code-review May 9 2012 12:00AM <p>linkedin.com has been in the blogs a lot about their pro-HTML5/JavaScript approach to build their apps. They were one of the first companies to build their back end (mobile backend) on node.js, and now they are following amazon cloud reader and financial times in pioneering HTML5 only mobile apps. Lately they have released iPad app which they claim is 95% in HTML5.</p> <p>HTML5 for mobile devices is still a very young and undocumented technology, as due to the contrains of the mobile device hardware and network speed traditional optimization best practices don't really apply here.</p> <p>So I took took at their mobile app on: http://touch.www.linkedin.com to learn something from their R&D team and here are the findings:</p> <p>The code is structured with pretty advanced inheritance pattern. It does little remind me of sencha touch syntax. Classes are created via .extend(). Here is an example:</p> <pre> TLI.ListItemView = TLI.BaseView.extend({ initialize: function () { throw "This class must be subclassed!"; }, activateUrl: function () {}, activate: function () { var a = this.activateUrl(); if (a) window.location.href = a }, remove: function () { var a = this.$(".list_item_container"), b = $(this.el); b.height(a.height()); a.fadeOut(function () { b.slideUp() }) } }); </pre> <p>All CSS and javascript are inline on the page. (dependancies are injected later) No cashing offline mode or anything like that...</p> <p>linkedin mobile developers are not using any third party mobile libraries. Yep, I could not find a good one either. reading about all the minimalism and performance advices sencha touch or jQuery mobile just don't do it for me either, plus it takes all the fun away from the R&D development. The only third party lib I see is Backbone.</p> <p>Instead of images, I see HTML5 canvas being used as a loader spinner, and even to draw the logo. NICE!</p> <p>One of the optimization best practices for mobile HTML5 web dev is minimize parsed JavaScript parsing. I do se at least one object to be "pre-parsed" stored as a string:</p> <pre> window._serverData = ' { "locale": "en-us", "buckets": { "rateApp": "invite-rate", "homeNewsNum": { "name": "one", "payload": { "count": 1, "viewkey": "m_abtest_1_optionD", "clickkey": "m_abtest_1_optionD_tap" } }, "ads": { "name": "A", "payload": { "enabled": false } }, "adsText": { "name": "Other", "payload": { "leadtext": "send_me_more_info", "responsetext": "more_info_response", "disabledtext": "request_sent" } } } }'; </pre> <p>HTML5 Local storage is used for stroring user session data (no cookies) and also is used for storring dependancies of the framework. NICE! Actually here is their local storage class:</p> <pre> TLI.Storage = {}; (function (g) { g.$localStorage = localStorage; g.simulateWriteFailure = false; g.nonVolatileKeys = { "TLI:applicationLog": true, first_run: true }; g.ensureVersion = function () { var a = g.generateKey("storageVersion"), b = TLI.config.storageVersion; if (g.$localStorage[a] !== b.toString()) { g.clear(); try { if (g.simulateWriteFailure) throw "Write failure"; g.$localStorage[a] = b } catch (c) { TLI.localStorageAvailable = false; $log.error("Local storage write failure - " + c) } } }; g.get = function (a, b) { if (!b) b = JSON.parse; var c = null; if (TLI.localStorageAvailable) { g.ensureVersion(); if ((c = g.$localStorage[a]) && c.length) return b(c) } return c }; g.set = function (a, b, c) { if (!c) c = JSON.stringify; if (TLI.localStorageAvailable) { g.ensureVersion(); if (b === null || typeof b === "undefined") g.$localStorage.removeItem(a); else try { if (g.simulateWriteFailure) throw "Write failure"; g.$localStorage[a] = c(b) } catch (d) { if (d.name === "QUOTA_EXCEEDED_ERR") { console.log("****QUOTA_EXCEEDED_ERR: Resetting local storage...****"); g.reset(); g.$localStorage[a] = c(b) } else { TLI.localStorageAvailable = false; $log.error("Local storage write failure - " + d) } } return b } return null }; g.remove = function (a) { g.set(a, null) }; g.generateKey = function (a, b) { var c = "TLI:"; if (b) c += "user:" + b + ":"; c += a; return c }; g.clear = function () { TLI.localStorageAvailable && g.$localStorage.clear() }; g.createIndex = function (a) { g.set(a + ":index:undefined", {}) }; g.search = function (a) { g.get(a + ":index:undefined"); return [] }; g.reset = function () { g.nonVolatileKeys[$storage.generateKey("notif:inv:timestamp", TLI.User.getCurrentUser().id)] = true; g.nonVolatileKeys[$storage.generateKey("notif:msg:timestamp", TLI.User.getCurrentUser().id)] = true; var a = $storage.generateKey("inbox", TLI.User.getCurrentUser().id), b = $storage.generateKey("connections", TLI.User.getCurrentUser().id), c = $storage.generateKey("profile", TLI.User.getCurrentUser().id); g.nonVolatileKeys[a] = true; g.nonVolatileKeys[a + ":timestamp"] = true; g.nonVolatileKeys[b] = true; g.nonVolatileKeys[b + ":timestamp"] = true; g.nonVolatileKeys[c] = true; g.nonVolatileKeys[c + ":timestamp"] = true; var d = {}; _.each(g.nonVolatileKeys, function (e, f) { if (f) d[f] = g.get(f) }); g.clear(); _.each(d, function (e, f) { g.set(f, e) }) } })(TLI.Storage); </pre> <p>Not all the code is loaded at once I assume, what is on the page is just the core framework. Here is their "loader" class that can load js/CSS, even cross domain I see...</p> <pre> TLI.Loader = {}; (function (g) { var a = document.getElementsByTagName("script")[0], b = document.getElementsByTagName("link")[0], c = "localStorage" in window, d = function (h) { return "TLI:assets:" + h.split("/").pop() }, e = function (h) { setTimeout(function () { h && h.call(this) }, 0) }; g.injectJS = function (h, j) { var l = document.createElement("script"); l.type = "text/javascript"; l.innerHTML = h; a.parentNode.insertBefore(l, a); e(function () { j && j.call(this) }) }; g.sourceExternalJS = function (h, j) { var l = document.createElement("script"); l.src = h; l.type = "text/javascript"; a.parentNode.insertBefore(l, a); l.onload = j }; g.injectCSS = function (h, j) { var l = document.createElement("style"); l.type = "text/css"; l.innerHTML = h; b.parentNode.insertBefore(l, b); e(function () { j && j.call(this) }) }; g.sourceExternalCSS = function (h, j) { var l = document.createElement("link"); l.rel = "stylesheet"; l.href = h; b.parentNode.insertBefore(l, b); e(function () { j && j.call(this) }) }; g.injectAsset = function (h, j, l) { if (h === "js") g.injectJS(j, l); else h === "css" && g.injectCSS(j, l) }; g.sourceExternalAsset = function (h, j, l) { if (h === "js") g.sourceExternalJS(j, l); else h === "css" && g.sourceExternalCSS(j, l) }; g.fetchAsset = function (h, j) { var l = new XMLHttpRequest; l.open("GET", h.path, true); l.onreadystatechange = function () { (!l.readyState || l.readyState == "loaded" || l.readyState == "complete" || l.readyState == 4) && j.call(this, l.responseText) }; l.send(null) }; g.assetCallbacks = {}; g.fetchCrossDomainAsset = function (h, j) { var l = document.createElement("script"); l.type = "text/javascript"; var o = h.path; if (h.path.match(/\.js$/)) o = h.path.replace(/\.js$/, ".jsonp.js"); else if (h.path.match(/\.css$/)) o = h.path.replace(/\.css$/, ".css.jsonp.js"); l.src = o; g.assetCallbacks[h.name] = j; a.parentNode.insertBefore(l, a) }; var f = function (h, j, l) { if (TLI.config.enableUpdatePatching && TLI.config.bundleUpdates && typeof TLI.diff_match_patch !== "undefined") if (j = TLI.config.bundleUpdates[h.name + ":" + j + ":" + l]) return { name: h.name, path: j }; return null }, i = function (h, j) { var l = d(h.name), o = d(h.name) + ":version"; g.fetchCrossDomainAsset(h, function (n, p) { try { g.ensureAssetStorageVersion(); if (n === h.version) { localStorage[l] = p; localStorage[o] = h.version } } catch (q) { c = false } j(p) }) }, k = function (h, j, l) { var o = d(h.name), n = d(h.name) + ":version"; g.fetchCrossDomainAsset(j, function (p, q) { var t = null; try { g.ensureAssetStorageVersion(); t = localStorage[o]; if (p === h.version) { q = JSON.parse(q); a: { for (var r = (new TLI.diff_match_patch).patch_apply(q, t), s = 0, u = r[1].length; s < u; s++) if (!r[1][s]) { t = null; break a } t = r[0] } if (t) { localStorage[o] = t; localStorage[n] = h.version } else { i(h, l); return } } } catch (v) { c = false } l(t) }) }, m = function (h, j, l) { var o = d(j.name), n = d(j.name) + ":version"; TLI.Storage.ensureVersion(); var p = null; n = localStorage[n]; if (j.version.toString() === n) { p = localStorage[o]; g.injectAsset(h, p, l) } else(o = f(j, n, j.version)) ? k(j, o, function (q) { g.injectAsset(h, q, l) }) : i(j, function (q) { g.injectAsset(h, q, l) }) }; g.load = function (h, j) { var l = h.path.split("."), o = l[l.length - 1]; l = TLI.detect(navigator.userAgent); if (l.android && !l.versionAtLeast("2.2")) g.sourceExternalAsset(o, h.path, j); else TLI.config.cacheAssets && c ? m(o, h, j) : g.fetchAsset(h, function (n) { g.injectAsset(o, n, j) }) }; g.loadedAssets = {}; g.loadAll = function (h, j) { var l = 0, o = function () { if (l >= h.length) j && j.call(this); else { var n = h[l]; l++; if (g.loadedAssets[n.path]) o(); else { g.loadedAssets[n.path] = true; g.load(n, o) } } }; o() }; g.requireBundles = function (h, j) { for (var l = [], o = 0, n = h.length; o < n; o++) { var p = h[o], q = TLI.bundles[p]; if (q) { q.name = p; l.push(q) } } g.loadAll(l, j) }; g.requireBundle = function (h, j) { g.requireBundles([h], j) }; g.recvAssetFile = function (h, j, l) { (h = g.assetCallbacks[h]) && typeof h === "function" && h.call(this, j, l) }; g.clearAssetStorage = function () { for (var h in localStorage) h.match("^TLI:assets:") && localStorage.removeItem(h) }; g.ensureAssetStorageVersion = function () { var h = TLI.config.assetStorageVersion, j = localStorage["TLI:assets:storageVersion"]; if (!j || j.toString() !== h.toString()) { g.clearAssetStorage(); localStorage["TLI:assets:storageVersion"] = h } } })(TLI.Loader); </pre> <p>Templating is done via backbone and so is routing I suppose. Templates are placed in js files as a object literal.</p> <p>Also see zeptojs included</p> <p>No responsive design here! every device has a separate CSS file which is loaded based on JavaScript detection.</p> <p>base64 image inlining is used heaviliy in the CSS</p> Bahamas Atlantis Vacation Review http://davidpirek.com/blog/bahamas-atlantis-vacation-review May 4 2012 12:00AM <p>Finally decided on our vacation destination: Bahamas. Well everybody talks about Atlantis, so I wanted to first see it before paying $250+ per night at the cheapest room they have. So first day we stayed at the Sheraton Nassau Beach Resort & Casino, not a bad hotel, but they did scam us off $40 per day at our checkout, so for those who paid full price around 200 at that time, you would be getting the same price at the Atlantis Resort plust and the Atlantis goodies: the aquarium, move theatre, and of corse one of the best water parks in the world, plus a nice walkable little area (Sheraton Nassau Beach Resort & Casino is now in the middle of construction site, and each trip to the town will run you $20 with tip for taxi)</p> <p>Anyway, Atlantis is great. It is kind of a price trap, large bottle of water for $10... and pretty much no grocery store on the island, but if you can stock up with food and water, it's actually a great deal, with all the goodies you get. The beaches are amazing, the do clean them every day, water is clean and warm. People are actually nice (nicer than in Puerto Rico where we went last year) and there is just so much to do for all age groups, so you really don't need to leave the property.</p> <p>If you go: the mistake I made was booked the airfare before looking at the main Atlantis website, where they were actually giving up to $600 credit for the air tickets. In this case, priceline.com did not have the best deal at all!</p> javascript forEach loop for objects and arrays http://davidpirek.com/blog/javascript-foreach-loop-for-objects-and-arrays Apr 12 2012 12:00AM <p>This is probably too trivial to post, but I figured some people might google for it, so here we go: a universal forEach loop, pretty much the same implementation like jQuery has:</p> <pre> var forEach = function(a, callBack){ var length = a.length; // Object does not have .length propery. if(length) { for (var i = 0; i < length; i++){ callBack(i, a[i]); } } else { for (var key in a) { callBack(key, a[key]); } } }; </pre> <p>And here is how you use it for both objects and arrays:</p> <pre> each(['foo', 'bar'], function(i, d){ console.log(d) }); each({fo: 'bar', bar: 'foo'}, function(i, d){ console.log(d) }); </pre> HTML5 mobile app javascript framework http://davidpirek.com/blog/html5-mobile-app-javascript-framework Apr 11 2012 12:00AM <p>I have been doing some experiments in the HTML5 mobile app area. Read some blogs, saw some screen casts and this seems to be the recommendations for designing high performance HTML5 mobile apps: </p> <ul class="list"> <li>mind every line/character of your code like you mind every pixel in your designs</li> <li>even if you use cache, parsing javascript can still take a lot of time, so be very minimalistic</li> <li>inline javascript is ok, since it limits http connections</li> <li>use local storage for caching of HTML/CSS/JS</li> </ul> <p>With these in mind, I realized that using some of the existing framework (jquery mobile, or sencah touch) may not be the most optimal option. The main problem is that they are made to be cross platform, wich means they pack inside platy of code that may not be needed for particular OS, and also are designed for the lowest common denominator (this is what Steve Jobs hated), so they will never take full advantage of the specific HTML5/CSS features of a particular OS.</p> <p>So here is a code sniped that does the following: first it injects sockets.io in, then loads all other resources via websockets (css js and JSON), this is in theory (and reality) much faster since there is not HTTP header overhead, and there are no limits on HTTP requests. Then CSS and JS requests are cached into local storage. Also any data communicating with the server is not done via ajax but websockets. I have discovered an interesting behavior, it's not just that it's faster over the network, but you can do an interesting loading method that is not possible in classic ajax/http: you can send a short message to the server that you would like to receive information, and then the server can start sending you the requested data in smaller chunks. This gives the app an extremely interactive look... because data can start coming from the server instantly, and "animate" on the screen.</p> <pre> (function(){ // Header tag. var oHead = document.getElementsByTagName('head')[0]; // Get content div. var contentDiv = document.getElementById('content'); // Create namespace. window.APP = {}; // Script loader. APP.loadScript = function(sScriptSrc, callbackfunction) { // Gets document head element var oHead = document.getElementsByTagName('head')[0]; if(oHead) { // Creates a new script tag var oScript = document.createElement('script'); // Adds src and type attribute to script tag oScript.setAttribute('src',sScriptSrc); oScript.setAttribute('type','text/javascript'); // Calling a function after the js is loaded (IE). var loadFunction = function() { if (this.readyState == 'complete' || this.readyState == 'loaded') { callbackfunction(); } }; oScript.onreadystatechange = loadFunction; // Calling a function after the js is loaded (Firefox). oScript.onload = callbackfunction; // Append the script tag to document head element. oHead.appendChild(oScript); } }; // Executes script. APP.exeScript = function(js){ var oScript = document.createElement('script'); oScript.setAttribute('type', 'text/javascript'); oScript.innerHTML = js; oHead.appendChild(oScript); }; // Load css. APP.updateCss = function(css){ var oStyle = document.createElement('style'); oStyle.innerHTML = css; oHead.appendChild(oStyle); }; // Local storage wrapper. APP.storage = { set: function(key, value) { localStorage.setItem(key, value); }, get: function(key) { return localStorage.getItem(key); } }; // Load socket script. APP.loadScript('http://localhost:8083/socket.io/socket.io.js', function(){ APP.socket = io.connect('http://localhost:8083'); var socket = APP.socket; // Load css. APP.loadCss = function(path){ // Use local storage for cashing. var localCSS = APP.storage.get(path); if(localCSS) { APP.updateCss(localCSS); } else { socket.emit('loadFile', path, function (css) { APP.updateCss(css); APP.storage.set(path, css); }); } }; // Load some css. APP.loadCss('jqt/themes/css/jqtouch.css'); // JS socket loader. var require = function(path, callBack){ // Use local storage for cashing. var localJs = APP.storage.get(path); // TEMP ceche disabled. //localJs = false; if(localJs) { APP.exeScript(localJs); callBack(); } else { socket.emit('loadFile', path, function (js) { APP.storage.set(path, js); APP.exeScript(js); callBack(); }); } }; // Load libs. require('jqt/src/lib/zepto.min.js', function(){ require('/jqt/src/jqtouch.min.js', function(){ // Run your code here. }); }); }); }); }()) </pre> remote javascript coder for debugging http://davidpirek.com/blog/remote-javascript-coder-for-debugging Mar 22 2012 12:00AM <p>There are scenarios where refreshing page to see results of your updated code is just too painful. This could be coding a mobile HTML5 app, or inside of facebook canvas.</p> <p>inspired by jsconsole.com, I have created tool that lets you remotely update HTML/CSS/JavaScript. It uses HTML5 web sockets to do the transfer, so only works in "modern browsers" and runs on node.js server.</p> <img src="http://davidpirek.com/img/l/8c7e2e7c-1285-479d-b26b-fbc493c99dcc.png" /> <p>Here is the <a href="https://github.com/dpirek/Remote-Coder">github repo</a>.</p> <p>This is how you can used it: after setting up your node.js server, put this code on the page you would like to remotely control:</p> <pre> &lt;div id=&quot;content&quot;&gt;&lt;/div&gt; &lt;script src=&quot;http://localhost:8083/js/init.js&quot;&gt;&lt;/script&gt; </pre> <p>And then through the code.html page "inject" HTML/CSS/JS into the remote page.</p> nodejs on iphone running localhost webapp http://davidpirek.com/blog/nodejs-on-iphone-running-localhost-webapp Feb 13 2012 12:00AM <p>I have been doing a lot of HTML5 web apps in iOS lately, running them from both remote hots and via phonegap, but now I have take a totally different approach: being a node.js fan, I decided to hack my iphone just so that I could run nodejs on it. It works great, you can run version 6.x and even allows you to do npm. So to bring it all together, I copied my webapp to my iphone and ran it as a nodejs server, loading then on local host in the browser... it even supports websockets (socket.io). This is little different than Nathan Rajlich (the author of the node.js iphone package port) took, when trying to tackle directly into the phone's native APIs. I am keeping the spirit of a webapp, while still having more options than classic webapp or phonegap approach.</p> <p>Here are a few screenshots:</p> <img src="http://davidpirek.com/img/l/22906985-161c-4cdb-98b0-001f519a7405.png" /> <br /> <br /> <img src="http://davidpirek.com/img/l/a13b8c32-95d4-41ae-9392-6f7e822ef5ed.png" /> <p>Here are the steps to get there:</p> <p>1. Jailbrake you iphone</p> <p>2. install open shh (available in the syndia app store)</p> <p>3. install node.js 6.x (available in the syndia app store) (the package comes with npm)</p> <p>4. get the <a href="http://itunes.apple.com/us/app/ssh-terminal/id369875227">SSH Terminal</a> via iTunes appstore ($0.99)</p> <p>5. connect to wifi, look in your settings for the phone's ip address, and then you can login into your iphone (both via ssh and ftp. The default login for every iphone are: login: root: alpine (at this point recommend changing password so you don't get hacked :)</p> <pre> passwd root </pre> <p>Enter new password... </p> <p>6. at this point you should have both ssh and ftp access to your phone, so go ahead, npm install your favorite packages, and copy over your node.js project. (recommend keeping the phone plugged in, otherwise it will keep shutting off the wifi connection, and you will need to reset your ip address all the time)</p> <p>7. Either remotely, or locally (from your phone), start your node.js server.</p> <p>In conclusion: if this solution got packaged into a simple SDK (daemon running, starting node server via an icon) I could see some great possibilities. Both, native HTML5 apps, and phonegap apps have their limitations, this way you could load all your resources locally, go to the network when needed, plus have much more power over the network communications, like connecting to mongoDb directly, running websockets from localhost... the main benefit about this "all JavaScript" solution is really the easy and speed of development... you can write relatively complex apps in hours... </p> set static ip on ubuntu server 10.04 http://davidpirek.com/blog/set-static-ip-on-ubuntu-server-1004 Feb 7 2012 12:00AM <p>Here is how I was able to configure static ip address on ubuntu server 10.04 on my vSphere center at my house:</p> <pre> sudo nano /etc/network/interfaces </pre> <p>change this:</p> <pre> auto eth0 iface eth0 inet dhcp </pre> <p>to this:</p> <pre> auto eth0 iface eth0 inet static address 192.168.1.100 netmask 255.255.255.0 network 192.168.1.0 broadcast 192.168.1.255 gateway 192.168.1.1 </pre> <p>Lastly, run this command to uninstall the dhcp-client</p> <pre> sudo apt-get remove dhcp-client </pre> <p>Now your server runs on 192.168.1.100 so go to your router (if you are running this at home like me) and change your port forwarding ports to 192.168.1.100.</p> browser based shell terminal in node.js http://davidpirek.com/blog/browser-based-shell-terminal-in-nodejs Jan 31 2012 12:00AM <img src="http://davidpirek.com/content/files/images/a623d28e-5a37-437d-aa44-d9fb3e8a71c0.png" alt="terminal" /> <p>I have been playing around a little bit... with web sockets and executing shell commands via node.js, and created a web based shell terminal. This is really just a POC rather than finished product, how to created a web based terminal taking advantages of socket.io and node.js</p> <p>I have put the <a href="https://github.com/dpirek/browser-terminal">code on github</a>, so you can check it out. Before you run it, make sure you have all the necessary packages, and also, you have the update the web folder static file path in the config.js file.</p> JavaScript timer stop watch in node.js http://davidpirek.com/blog/javascript-timer-stop-watch-in-nodejs Jan 13 2012 12:00AM <p>Here is a nice little piece of code that can help you measure measure time: (actually using it in node.js)</p> <pre> var timer = function(){ var start, end; return { start: function(){ start = new Date().getTime(); }, stop: function(){ end = new Date().getTime(); }, getTime: function(){ return time = end - start; } }; }; </pre> <p>Here is the usage:</p> <pre> var t = timer(); t.start(); // do something t.stop(); console.log(t.getTime()); </pre> copy movies to iphone, how to http://davidpirek.com/blog/copy-movies-to-iphone-how-to Dec 26 2011 12:00AM <p>So I have some movies in avi format that I want to watch on my ipad. Obviously this feature is not build in itunes, as apple wants you ony to watch purchased content. I did some googeling around, found some paid software that did not look trust worthy either to put on my mac, finally found this little open source project: <a href="http://handbrake.fr/">HandBrake</a>. And it works great. I have my movies at another mac in my house network, and did not even have to get up from my couch, just pointed to the remote disk, through a few movies in the queue, and my son will have movies to watch on the ipad... I think HandBrake even rips DVDs directly...</p> <p>These guys even have a repo on github, so you know this is for real... real great tool :)</p> javascript cache buster url unique query parameter http://davidpirek.com/blog/javascript-cache-buster-url-unique-query-parameter Dec 19 2011 12:00AM <p>Browsers use different of caching files, so you want to ensure that you files will not be cached, apart from setting your servers right, here is a little javascript function that will add a random parameter at the end of the url</p> <pre> // Cache buster. var cacheBuser = function (url) { // Check if query already in url. if(url.indexOf('?') != -1) { // Check for '&' as the last character. if(url.substring(0, url.length - 1) === '&'){ return url + '_r=' + Math.random() * 5; } return url + '&_r=' + Math.random() * 5; } else { return url + '?_r=' + Math.random() * 5; } }; </pre> html5 canvas framework http://davidpirek.com/blog/html5-canvas-framework Dec 13 2011 12:00AM <p>So have been looking what are the cool JavaScript HTML5 canvas frameworks out there. What is my criteria: something light yet powerful, ideally written in "functional" manner.</p> <p><a href="http://easeljs.com">easeljs</a> is probably the best I have seen for creating flash like canvas games, especially with the demo game, and being bundled with other frameworks for sound and animations.</p> <p><a href="http://processingjs.org/">processingjs</a> has been around for a while, and has a great author: John Resing, yet I don't get the Java syntax, being a JavaScipter. Yet the community seems to be great and alive.</p> <p><a href="http://jcotton.sourceforge.net/">jcotton</a> seems to fit the bill with the functional approach, yet I don't see any community around this project, neither are there any advanced examples.</p> <p><a href="http://mbostock.github.com/d3/">d3</a> seems to be the closest to my selection criteria, great functional syntax, many advance examples, good community. The only fallback I can see is that this framework seems to be mainly geared towards chats.</p> <p><a href="https://github.com/mrdoob/three.js">three.js</a> is the most promising in 3d graphics, yet it's mostly geared towards WEBGL wich is not an official standart yet. Canvas however is an option with this framework.</p> JavaScript AMD, curl.js, require.js alternative http://davidpirek.com/blog/javascript-amd-curljs-requirejs-alternative Dec 13 2011 12:00AM <p>I have been looking at the <a href="http://unscriptable.com/index.php/2011/03/30/curl-js-yet-another-amd-loader/">AMD</a> spec (stands for Asynchronous Module Definition), and how this would apply to what I do in JavaScript. There are two notable frameworks: curl.js, require.js.</p> <p>The presupposition in need of a AMD is that you have a very large code base with many modules, where some of them may be only used in some pages or states of the app. In my <a href="http://www.davidpirek.com/javascript-mvc-jquery-plugin-framework">JavaScript MVC</a> approach, however, this is not the case: the initial load is just a very core and minimal library, and nay page specific logic is always loaded on demand.</p> HTML5 Canvas Cookbook book review http://davidpirek.com/blog/html5-canvas-cookbook-book-review Dec 1 2011 12:00AM <a class="left margin_right" href="http://www.amazon.com/gp/product/1849691363/ref=as_li_qf_sp_asin_il?ie=UTF8&tag=wwwdavidpirek-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=1849691363"><img border="0" src="http://ws.assoc-amazon.com/widgets/q?_encoding=UTF8&Format=_SL110_&ASIN=1849691363&MarketPlace=US&ID=AsinImage&WS=1&tag=wwwdavidpirek-20&ServiceVersion=20070822" ></a> <p>I got a chance to review recently published <a href="http://www.amazon.com/gp/product/1849691363/ref=as_li_qf_sp_asin_tl?ie=UTF8&tag=wwwdavidpirek-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=1849691363">HTML5 Canvas Cookbook</a> by Eric Rowell (Packt Publishing).</p> <p>Overall a very nice concise tutorial book. I have to praise the Eric for both his expertise, and ability to present the topic in a very understandable manner. It starts with a very simple topic how to draw a line, and goes all the way to game design. The JavaScript examples are very clean, and use conventional syntax style. In the first few chapters, I almost thought that this book was too entry level, but in a nice linear way, the books works up to the most advanced topics, line animations, or WebGL, applied to web design.</p> <p>The only think I have an issue with is probably the title: I would expect a "cookbook" to be a series of applied advanced topics, where this book is more of a beginner's tutorial. (HTML5 canvas being such a young technology, we are all begines).</p> <img src="http://www.assoc-amazon.com/e/ir?t=wwwdavidpirek-20&l=as2&o=1&a=1849691363" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> websockets css javascript loading http://davidpirek.com/blog/websockets-css-javascript-loading Nov 22 2011 12:00AM <p>We live in the noSQL world, how about noHTTP, noREST approach to the web?</p> <p>I have put together an experiment where I load all the resources (css/javascript/html/json) through websockets, actually could also load images if I think about it.</p> <p>In theory, this approach could be lighting fast way to load websites, since all that we need to load would a continuous stream form the server, all packaged on the fly. Why to over optimize so much. We in the mobile world we are still working over very slow connections in most cases, websites can take over 10 seconds to load, on the other hand we have available full HTML5 complaint browsers.</p> <p>Here is the server-side node.js code:</p> <pre> // References. var http = require('http'), lightnode = require('lightnode'), fs = require('fs'), c = require('../config'); // Create server. var server = new http.Server(); server.listen(c.config.portNumber); // Website static server. var website = new lightnode.FileServer(c.config.staticContentPath); // Request. website.delegateRequest = function(req, resp) { return website; }; // When a request comes to the ip server. server.addListener('request', function(req, resp) { website.receiveRequest(req, resp); }); // Sockets listerner. var io = require('socket.io').listen(server); io.sockets.on('connection', function (socket) { // Service chanel. socket.on('service', function (url, callBack) { if(url === 'css/get'){ fs.readFile(c.config.appPath + '/web/css/main.css', function (err, content) { callBack(content.toString()) }); } else if(url === 'js/jquery'){ fs.readFile(c.config.appPath + '/web/js/jquery.js', function (err, content) { callBack(content.toString()) }); } else { callBack({foo: 'none'}) } }); }); io.sockets.on('message', function(obj){ console.log(obj); }); console.log('running on port: ' + c.config.portNumber + ''); </pre> <p> And here comes the client side: </p> <pre> &lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;title&gt;NINJA&lt;/title&gt; &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=utf-8&quot; /&gt; &lt;/head&gt; &lt;body&gt; &lt;script src=&quot;socket.io/socket.io.js&quot;&gt;&lt;/script&gt; &lt;script type=&quot;text/javascript&quot;&gt; (function(){ var socket = io.connect(), oHead = document.getElementsByTagName(&#39;head&#39;)[0]; // Load js libs. socket.emit(&#39;service&#39;, &#39;js/jquery&#39;, function (d) { var oScript = document.createElement(&#39;script&#39;); oScript.setAttribute(&#39;type&#39;, &#39;text/javascript&#39;); oScript.innerHTML = d; oHead.appendChild(oScript); }); // Load styles. socket.emit(&#39;service&#39;, &#39;css/get&#39;, function (d) { var oStyle = document.createElement(&#39;style&#39;); oStyle.innerHTML = d; oHead.appendChild(oStyle); }); }()); &lt;/script&gt; &lt;/body&gt; &lt;/html&gt; </pre> <p>I have not done any real benchmarking yet, so will see how this works in real world. I can think of at least two drawbacks so far: no HTTP caching, and the battery consumption when having websockets connection open.</p> observer pattern in javascript class http://davidpirek.com/blog/observer-pattern-in-javascript-class Nov 21 2011 12:00AM <p>Here is a simple observer (subscriber/publisher) patter implemented in JavaScript using the module patter.</p> <pre> // Observer class. var observer = function(){ // Maintain list of subscribes. var subscribers = {}; return { subscribe: function(obj){ subscribers[obj.id] = obj.fn; }, publish: function(obj){ // Loop through all subscribers. for (var key in subscribers) { subscribers[key](obj); } }, unsubscribe: function(id){ delete subscribers[id]; } }; }; var instance = observer(); // Subscribe foo. instance.subscribe({ id: 'foo', fn: function(obj){ console.log('foo got message') console.log(obj) } }); // Subscribe bar. instance.subscribe({ id: 'bar', fn: function(obj){ console.log('bar got message') console.log(obj) } }); // Publish to foo and bar. instance.publish({message: 'hi dave'}); // Unsubscribe foo. instance.unsubscribe('foo'); </pre> <p>If you prefer "classical" aproach through prototypal inheritance, Dustin Diaz in his <a href="http://www.dustindiaz.com/javascript-observer-class/">blog post</a> suggest this approach:</p> <pre> function Observer() { this.fns = []; } Observer.prototype = { subscribe: function (fn) { this.fns.push(fn); }, unsubscribe: function (fn) { this.fns = this.fns.filter( function (el) { if (el !== fn) { return el; } } ); }, fire: function (o, thisObj) { var scope = thisObj || window; this.fns.forEach( function (el) { el.call(scope, o); } ); } }; </pre> loosely coupled JavaScript http://davidpirek.com/blog/loosely-coupled-javascript Nov 21 2011 12:00AM <p>Loose coupling has been the hidden power behind the success of JavaScript, and also of it's most popular framework: jQuery. Loose coupling let's you plug things together faster, and in bigger numbers.</p> <p>I just love the way this is summarized in the <a href="https://github.com/ender-js/Ender">Ender.js</a> read me file:</p> <p> <em><strong>Small, loosely coupled modules are the future and large, tightly-bound monolithic libraries are the past.</strong></em> </p> group by count json query http://davidpirek.com/blog/group-by-count-json-query Nov 18 2011 12:00AM <p>I am playing with some JSON type of data, and need to do some reports, in particular a "group by count" query, so here is what I came up with:</p> <pre> var groupByCount = function(list, name){ var groups = {}; list.forEach(function(d){ if(groups[d[name]]){ groups[d[name]]++; } else { groups[d[name]] = 1; } }); return groups; }; </pre> <p>(I am using the '.forEach' method available in node.js, but this could be replaced with a regular loop and used on the client.</p> appmobi xdk review html5 gaming engine http://davidpirek.com/blog/appmobi-xdk-review-html5-gaming-engine Nov 15 2011 12:00AM <p>I ran across appmobi xdk, so here are some of my first impressions from this HTML5 game engine.</p> <p>Forget about the chrome app, I felt like entering an late 90's span site, popups everywhere, alter messages trying to up sell me on license to something I did not even tried yet. Ater about 10 minutes, I figured out where to actually end the code, but the compared to for example http://c9.io this editor is a joke, though it actually uses the same Ace editor for highlighting, the usability feel weird, does not even expand to full width of the screen, cannot set fonts, and when I hit F5, I get all the promo popups and java alerts again... plus my whole session was lost, have to re-open everything... ok that's enough, let me try to open the project in my native editor.</p> <p>Looking through the code now, of the demo app called direct box 2d. I see a lot of DOJO style code, classical approach rather than functional, ok we all have our choices, but the code quality is not that great, poor commenting, code would not pass js lint with the "untyped" if statements (funny that js list is actually build in the ace editor as a plugin).</p> <p>On a good note, I live the concept of the 80's style 2d games, and would love to mess around with some gaming ideas, so I am hoping this project will mature little more, so that it does not turn developers off in the first impression... </p> node.js v0.6.0 installation http://davidpirek.com/blog/nodejs-v060-installation Nov 8 2011 12:00AM <p>Just installed node.js v0.6.0, so far so good!</p> <p>By mistake, I installed the unstable v0.5.x and it was hell to get rid of some of the binaries. When I tried to downgrade to version 4, not all the libraries wen away, and then I had big issues with some of the npm packages. So not upgrating to v6 cleaned up everything, and the box runs great!</p> sencha attacks jQuery: jQuery VS Ext JS http://davidpirek.com/blog/sencha-attacks-jquery-jquery-vs-ext-js Nov 4 2011 12:00AM <p>Sencha (Extjs) is running an adsence ad that directly attacks jQuery JavaScript library. While I have high regards of Sencha and their code quality, the fact that it is a commercial and they are using money to even do false advertising (explained later) makes me uneasy!</p> <p>The <a href="http://www.davidpirek.com/content/files/documents/69a1c935-6358-4f5a-bf21-37c7c3ff0a82.pdf">document</a> they offer as a comparison chart falsely compares their framework, uses twisted numbers and mainly totally misses the point by comparing two totally different products.</p> <p>Here are just few examples of the false claims.</p> <p>jQuery has no MVC architecture: in fact there are a few of them to choose. I myself <a href="https://github.com/dpirek/jQuery-MVC-Framework">wrote one</a>. </p> <p>jQuery has no layout manager: how about <a href="http://layout.jquery-dev.net/">this out</a>?</p> <p>jQuery has no Templates: well jquery.tmpl is actually now officially developed by Microsoft and unlike the flexibility that Extjs you can actually create true MVC views.</p> <p>In conclusion Extjs is a nice concise framework, but gives very little flexibility to a developer, or even UI engineer to create their own paradigm. In fact, the way it is polished and powerful, it takes most of the fun away out of programming. There are in facts thousands of jQuery plugins which the community produced which the few ext widgets can never match in numbers.</p> node.js MVC CMS with mongoDb is here! http://davidpirek.com/blog/nodejs-mvc-cms-with-mongodb-is-here Nov 1 2011 12:00AM <p>Just pushed out first version of my <a href="https://github.com/dpirek/nodejs-mvc-cms">node.js MVC CMS</a>. It will not some more work, but this CMS is now in a usable state, so I am sharing it!</p> <p>Sad to say this, but creating a node.js MVC CMS most likelly marked the end of the development of my <a href="http://www.aspnetcmsdemo.com">ASP.NET MVC CMS</a> :(, which was earlier hosted on <a href="http://www.davidpirek.com/cms">this page</a>.</p> <p>It's time to move in the real-time web, and node.js is just much better platform.</p> node.js HTML5 file uploader http://davidpirek.com/blog/nodejs-html5-file-uploader Oct 27 2011 12:00AM <p>I have been working on my <a href="http://www.nodejsmvc.com/">node.js MVC CMS</a> recently, and the last module missing to have all the core features was a file management system.</p> <p>I was able to put together two nice plugins, clean it up little bit, and integrate in my JavaScript MVC paradigm.</p> <p>For the node.js server side, I used <a href="https://github.com/felixge/node-formidable">formidable</a> lib. For some reason, the part that was the biggest trouble was actually saving the file. Here is what the final solution looked like:</p> <pre> var formidable = require('formidable'), c = require('../../config'), fs = require('fs'); exports.upload = function(req, res){ var form = new formidable.IncomingForm(); // Parse file. form.parse(req, function(err, fields, files) { if(files.fileToUpload){ // Read file. fs.readFile(files.fileToUpload.path, function(err, data){ // Save file. fs.writeFile(c.config.appPath + '/content/files/' + files.fileToUpload.name, data, 'utf8', function (err) { if (err){ // throw err; res.writeHead(200, {'content-type': 'text/plain'}); res.write(JSON.stringify({ isSucessful: false, message: 'Something went wrong!' })); res.end(); } else { // Sucess. res.writeHead(200, {'content-type': 'text/plain'}); res.write(JSON.stringify({ isSucessful: true, message: 'File was saved!' })); res.end(); } }); }); } else { res.writeHead(200, {'content-type': 'text/plain'}); res.write(JSON.stringify({ isSucessful: false, message: 'Did not receive any file!' })); res.end(); } }); }; </pre> <p>For the client, I found <a href="http://www.matlus.com/html5-file-upload-with-progress/">this tutorial</a> about how to do an HTML5 AJAX uploader. After I cleaned up the code little bit, this is what it looks like:</p> <pre> var messageWrp = $('#fileDataMsg', content); // Source: // http://www.matlus.com/html5-file-upload-with-progress/ $('#fileToUpload', content).change(function(){ var file = document.getElementById('fileToUpload').files[0]; if (file) { var fileSize = 0; if (file.size > 1024 * 1024){ fileSize = (Math.round(file.size * 100 / (1024 * 1024)) / 100).toString() + 'MB'; } else { fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + 'KB'; } // File info message. messageWrp.html('Name: ' + file.name + ' Size: ' + fileSize + ' Type: ' + file.type); } }); $('#uploadFileBtn', content).click(function () { // Form data. var fd = new FormData(); fd.append("fileToUpload", document.getElementById('fileToUpload').files[0]); var xhr = new XMLHttpRequest(); // Progress listerner. xhr.upload.addEventListener("progress", function (evt) { if (evt.lengthComputable) { var percentComplete = Math.round(evt.loaded * 100 / evt.total); messageWrp.html(percentComplete.toString() + '%'); } else { messageWrp.html('unable to compute'); } }, false); // On finished. xhr.addEventListener("load", function (evt) { // Parse json. var obj = $.parseJSON(evt.target.responseText); window.location.hash = 'File'; // Show success message. MVC.message.show({text: obj.message, hideDealy: 2000}); }, false); // On failed. xhr.addEventListener("error", function (evt) { MVC.message.show({text: 'There was an error attempting to upload the file.', hideDealy: 2000}); }, false); // On cancel. xhr.addEventListener("abort", function (evt) { MVC.message.show({text: 'The upload has been canceled by the user or the browser dropped the connection.', hideDealy: 2000}); }, false); xhr.open("POST", "/upload"); xhr.send(fd); return false; }); </pre> <p>Finally, this is the HTML part:</p> <pre> &lt;form enctype=&quot;multipart/form-data&quot; method=&quot;post&quot; action=&quot;upload/&quot;&gt; &lt;p&gt; &lt;label for=&quot;fileToUpload&quot;&gt;Select a File to Upload&lt;/label&gt; &lt;input type=&quot;file&quot; name=&quot;fileToUpload&quot; id=&quot;fileToUpload&quot; /&gt; &lt;/p&gt; &lt;p&gt; &lt;a class=&quot;button&quot; href=&quot;#&quot; id=&quot;uploadFileBtn&quot;&gt;Upload&lt;/a&gt; &lt;/p&gt; &lt;p id=&quot;fileDataMsg&quot;&gt;&lt;/p&gt; &lt;/form&gt; </pre> nodejs hosting amazon ec2 http://davidpirek.com/blog/nodejs-hosting-amazon-ec2 Oct 25 2011 12:00AM <p>So I have finally fingered out nodejs hosting on amazon ec2 linux instances, and the <a href="http://davidpirek.com/blog/installing-nodejs-on-amazon-ec2-linux">end to end setup</a>. </p> <p>It is kind of sad that the hosting options are currently very limited, some of the hosting providers are still in privat beta, others are too expensive... but I am a testimony that it is not that hard to set up your own amazon ec2 nodejs server.</p> <p>After you get through the learning curve, it is actually fairly easy... the scalability compare to the windows servers I am using with amazon ec2 is amazing... kind of never want to go back to .NET MVC any more... it so much faster to develop, deploy, change, scale... </p> installing nodejs on amazon ec2 linux http://davidpirek.com/blog/installing-nodejs-on-amazon-ec2-linux Oct 21 2011 12:00AM <p>Here is what worked form me when I tried to installed node.js on a fresh EC2 Amazon linux instance.</p> <pre> wget http://nodejs.org/dist/node-v0.4.12.tar.gz tar -zxvf node-v0.4.12.tar.gz cd node-v0.4.12 yum groupinstall "Development Tools" yum install make sudo make install </pre> <p>The main issue that I had to figure out was that Development Tools and make was not installed, so this took me little bit to figure out, being a Linux novice ;) </p> <p><a href="http://www.embracingthecloud.com/2010/12/05/InstallingNodejsOnAmazonEC2.aspx" target="_blank">This article</a> is a good starting point, however it's missing the above details, if you just pick the default instance. Of course, you can browser through the community instances, and probably find something that has all the node.js stuff already installed.</p> <p>Now going through the NPM installation, again a few hoops to jump, but figured it out:</p> <pre> sudo chown -R $USER /usr/local sudo curl http://npmjs.org/install.sh | sh </pre> <p>Now I am ready to roll! ... wait, not quite, npm installed but is not actually working... </p> <p>After reading around little bit, the fact that I build node without ssl support is not biting me back... so have to go back and reinstall... </p> <pre> sudo yum -y install openssl-devel </pre> <p>and now rebuild node again...</p> <pre> cd node-v0.4.12 sudo make install </pre> <p>ok now let's try it... </p> <pre> npm install lightnode </pre> <p>yea... it works... ready to roll now. In the meantime, I found <a href="http://www.murvinlai.com/install-nodejs.html">this article</a>, which seems to be more accurate, so in case my steps don't work, you can get some ideas here.</p> node.js mongoDb group by count http://davidpirek.com/blog/nodejs-mongodb-group-by-count Sep 16 2011 12:00AM <p>I am writing an app in node.js and starting to do some advanced data manipulation now. One thing I have not found in the mongoDb docs was how to do group by count query. </p> <p>So here is my solution. (nodejs code right out of my array utility "class")</p> <pre> // Arry to object with defined keys. exports.groupCount = function(array, key){ var group = {}; array.forEach(function(d, i){ var keyName = d[key]; if(group[keyName]){ group[keyName].count++; } else { group[keyName] = { count: 1 } } }); return group; }; </pre> ExsJs 4 code review http://davidpirek.com/blog/exsjs-4-code-review Sep 16 2011 12:00AM <p>The older version ExtJs Sencah, always bothered me with the Java-like classical approach. I am a "funcionalist" and don't like "classical art" to much in programming. It's too slow to develop with, to slow to run, to slow to respond to changes since it's not event oriented.</p> <p>It was pleasant to see that the "new" keyword is not gone from the NEW ExtJs. Nice! Moreover, they now talk the MVC language? This is my world :) Now I would still not call this a view: </p> <pre> Ext.define('AM.view.user.List' ,{ extend: 'Ext.grid.Panel', alias : 'widget.userlist', title : 'All Users', initComponent: function() { this.store = { fields: ['name', 'email'], data : [ {name: 'Ed', email: 'ed@sencha.com'}, {name: 'Tommy', email: 'tommy@sencha.com'} ] }; this.columns = [ {header: 'Name', dataIndex: 'name', flex: 1}, {header: 'Email', dataIndex: 'email', flex: 1} ]; this.callParent(arguments); } }); </pre> <p>... but it's a nice leap forward.</p> <p>One more not, what really amazes me about Sencha is how they are able to snap all the JavaScript talent around the world. Good job Sencha recruiting!</p> <p>While the framework is still too confined for me too use, I will be paying attention now. There are definitely many things to learn from you guys :) </p> OpenGl in JavaScript http://davidpirek.com/blog/opengl-in-javascript Sep 14 2011 12:00AM <p>I was looking around for a good javascript 3d engine, and this <a href="http://senchalabs.github.com/philogl/">project</a> looks pretty promising.</p> <p>As a side not, I have recently seen an interesting patter: Whenever I see a cool open-source framework the author sooner or later ends up working at Sencha. </p> node.js CMS: pure javascript CMS http://davidpirek.com/blog/nodejs-cms-pure-javascript-cms Sep 10 2011 12:00AM <p>For years I have been working on my asp.net CMS, and now when I decided to deprecate all my .NET code in favor of node.js, I rewrote my CMS in a few days. I plan to release my node.js CMS code soon, so stay tuned in. </p> <p>As I am working on it, I am still testing the stability of the code on this <a href="http://www.aktualniurokovesazby.cz/">morgtage website</a>.</p> <p>My mission is this: total minimalism. I don't want to use nodejs express framework, which is way too big and heavy. As my static server, I am using lightnode (node.js lib) which is the the smallest framework I could find. So the vision is the is "javascript minimalism" as this is the best coding pattern for javascript. :) </p> run shell from nodejs function http://davidpirek.com/blog/run-shell-from-nodejs-function Aug 25 2011 12:00AM <p>I have not inveted this one, but thought I share it. Here is how you can run shell commands from your node.js program:<p> <pre> var runShell = function(command){ var sys = require('sys'), exec = require('child_process').exec; function puts(error, stdout, stderr) { sys.puts(stdout); } exec(command, puts); } // Example. runShell('ls'); </pre> <p>If you want to package this code into a module, it would look something like this:</p> <pre> // Usage: // var sh = require('./shell'); // sh.run('ls'); exports.run = function(command){ var sys = require('sys'), exec = require('child_process').exec; function puts(error, stdout, stderr) { sys.puts(stdout); } exec(command, puts); } </pre> nodejs mvc framework http://davidpirek.com/blog/nodejs-mvc-framework Aug 22 2011 12:00AM <p>I have been working a an nodejs MVC framework, which I was able to use to build a simple nodejs CMS. Here is a live demo: <a href="http://www.nodejsmvc.com/">nodejsmvc.com</a>.</p> <p>There are a few frameworks out there already, but I wanted to have something really lightweight, so that I have flexibility to grow it the way I desire. Also, I wanted to create something similar in implementation as my <a href="http://www.davidpirek.com/javascript-mvc-jquery-plugin-framework">JavaScript MVC framework</a>.</p> <p>Here is a example of my routing class: </p> <pre> var fs = require('fs'), c = require('../../config'); // Router. exports.get = function(req, resp){ var url = req.url.toLowerCase(), arr = url.split('/'), controller = 'page', action = 'index', p1 = '', p2 = '', p3 = ''; try{ // Routing. if(arr[1] === 'hypoteky'){ var controller = 'hypoteky'; if(arr[2]){ p1 = arr[2]; action = 'detail'; } // Default route. } else { if(arr[1]){ p1 = arr[1]; } } // Load controller. var ctrl = require('../Controllers/' + controller); // Load view. fs.readFile(c.config.appPath + '/Views/' + controller + '/' + action + '.html', function (err, template) { if(template){ view = template.toString(); } // Load controller. ctrl.get({ controller: controller, action: action, p1: p1, p2: p2, p3: p3 }, view, function(content){ resp.writeHead(200, {'content-type': 'text/html'}); resp.write(content); resp.end(); }); }); } catch(ex){ // Error page. resp.writeHead(200, {'content-type': 'text/html'}); resp.write(ex); resp.end(); } }; </pre> <p>Here is an example of a controller:</p> <pre> var http = require('http'), tmpl = require('jqtpl'), fs = require('fs'), su = require('../lib/util.string'), ser = require('../lib/service'), c = require('../../config'), Mongolian = require("mongolian"); // Db. var server = new Mongolian(c.config.dbConnection), db = server.db(c.config.dbName); if(c.config.dbConnection !== 'localhost'){ db.auth(c.config.dbUserName, c.config.dbPassword); } var hypoteky = db.collection("hypoteky"), zonesDb = db.collection("zones"); var zones = []; var helpers = { htmlString: function(html){ try { // Search zones. for(var i in zones) { html = html.replace('{zone:' + zones[i].key + '}', zones[i].content); } return html; } catch (ex){ return ex; } } }; // HTTP content getter. exports.get = function(params, view, callBack){ // Detail. if(params.p1 !== ''){ fs.readFile(c.config.appPath + '/Models/hypSpiderKeys.js', function (err, data) { var keys = JSON.parse(data.toString()); helpers.listParams = function(d){ var sb = su.string.stringBuilder(); for (var i in keys) { if(d[keys[i].key]){ sb.append(keys[i].name); sb.append(': '); sb.append(d[keys[i].key]); sb.append('<br />'); } } return sb.toString(); }; hypoteky.findOne({ url: params.p1 }, function(err, hypoteka) { zonesDb.find().limit(50).toArray(function (err, zonesList) { // Zone list. zones = zonesList; // Array to obj conversion. var zone = {}; for(var i in zones) { zone[zones[i].key] = zones[i].content; } var content = tmpl.tmpl(view, {hypoteka: hypoteka, zone: zone}, helpers); callBack(content); }); }); }); } else { // Hyp list. hypoteky.find().limit(300).toArray(function (err, hypotekaList) { zonesDb.find().limit(50).toArray(function (err, zonesList) { // Zone list. zones = zonesList; // Array to obj conversion. var zone = {}; for(var i in zones) { zone[zones[i].key] = zones[i].content; } var content = tmpl.tmpl(view, {hypoteky: hypotekaList, zone: zone}, helpers); callBack(content); }); //var content = tmpl.tmpl(view, {hypoteky: hypotekaList}); //callBack(content); }); } }; </pre> <p>As a view engine, I use the tmpl port the nodejs.</p> jquery safari peek-a-boo bug when sliding flash http://davidpirek.com/blog/jquery-safari-peek-a-boo-bug-slidedown-flash Jul 14 2011 12:00AM <p>I saw in interesting bug recently, so I thought share this. If you try to slideDown a Flash movie or ad using the jQuery slideDown() method, Safari browser will not show the flash movie until you move your mouse or scroll the page. The solution: don't use slideDown(), use fade instead.</p> iframe without src attribute in javascript jQuery http://davidpirek.com/blog/iframe-without-src-attribute-in-javascript-jquery Jul 5 2011 12:00AM <p>Sometimes, you need to put your content into a "cage" yet you don't what to go through the expanse of having a full iframe load. (this is very expensive, kind of like opening new page) and you get into all kinds of cross domain issues, so your inner frame content cannot talk to the parent frame.</p> <p>Well the good news is that there is a trick for that. You can create an iframe as an element, and document.write() the content into it, without going through the expanse of opening a new page.</p> <p>Here is what the code would look like (in jQuery) syntax:</p> <pre> // Create iframe. var iframe = $('&lt;iframe /&gt;'); // Add it to the dom. $("#targetDiv").append(iframe); // Find the iframe in the DOM. var iWindow = iframe[0].contentWindow; // Get the iframe document. var iDoc = iWindow.document, // Write the content "string" into the iframe. iDoc.open(); iDoc.write("Hello world!"); iDoc.close(); </pre> <p> The "iDoc.close()" part of the code is little tricky in firefox, so I ended up doing some kind of callback to check when the iframe finished loading, and then do the "iDoc.close()". But that part of the code was little messy, so I leave it out for now. </p> <p>What can this be used for? All kinds of little hacks and trick, especially when dealing with cross-domain loading/sending. For example this is the base approach for doing cross-domain posts. Another one can be, if you are trying to dynamically load script tags that contain document.write() such as third party ads. Google uses similar approach in their gData API to do all server/client communication. I have not explored this one in depth yet, I but last time I was trying to find a source in the Facebook iframe app API, I did not see one... </p> ajax is dead! http://davidpirek.com/blog/ajax-is-dead Jun 20 2011 12:00AM <p>As much as buzz word this has been, in reality AJAX is really an outdated technology these days. Here are the technologies that have surpassed it:</p> <p>JSONP does not really XHR request, allows for cross-domain loading, and in many browsers parses faster. </p> <p><a href="http://en.wikipedia.org/wiki/WebSockets">WebSockets</a> seems to be the new "ajax" paradigm for server/client communication. While this feature is not full available in all browsers, there are frameworks such as <a href="http://socket.io/">socket.io</a> that have workarounds for older browsers, so this feature is ready to use.</p> craigslist scams http://davidpirek.com/blog/craigslist-scams Jun 1 2011 12:00AM <p>craigslist.com is a highly spammed site. Partially due to its success. From my experience, every post I make get a few scam emails from africa, trying to rip me off... </p> <p>Recently, however, I have seen new type of marketing scam. This is how it goes: someone posts an ad in the free stuff section, in my case it was "hot tub" that they are offering for free to pick up. As soon as you send them an email, you get a response that somebody else has already asked for the item, so you are second in line. In few hours you get another email saying that the tub is now gone, but here some product info that you can buy: nice spam marketing email.</p> <p>When this happened to me second time, I knew this was a pattern. Guessing from the type of english, this person was actually local.</p> hidden iframe post jquery plugin with iframe http://davidpirek.com/blog/hidden-iframe-post-jquery-plugin-hidden-iframe Apr 4 2011 12:00AM <p>I have been pretty getting pretty excited lately about the possibilities of JSONP + JavaScript MVC possibilities, but one piece that was still missing was how to send data back to the server in a distributed server scenario where your JavaScript RIA app is getting and sending data to different servers. Of course a simple server side wrapper could solve the solution, where you post to your server which sends the data to other server.</p> <p>So here is the solution: I create a hidden iframe on the fly and a form inside of it, post the data inside of it, and remove the iframe from the DOM. (this is not an original solution of course :) but I have not seen anybody wrapping this in a nice jQuery plugin)</p> <p>Here is the <a href="https://github.com/dpirek/iFrom">github project link</a> which will have the most up to date code.</p> <p>Demo:</p> <form action="/page/post" method="post" class="clearfix"> <input name="name" class="left margin_right" value="m" /> <select name="age" class="left margin_right"> <option value="s">1</option> </select> <button class="button" >ok</button> </form> <script type="text/javascript"> (function ($) { // Element factory. var createElement = function (context, name, atributes) { // Create element. var element = context.createElement(name); // Add atributes. $.each(atributes, function (i, d) { element[d.key] = d.value; }); return element; }; $.fn.iForm = function (op) { var defaults = { start: function () { }, sucess: function () { }, buttonSelector: '.button', disabledButtonClass: 'disabled' }; $.extend(defaults, op); return this.each(function () { var form = $(this), postUrl = form.attr('action'), postMethod = form.attr('method'), submitButton = $(defaults.buttonSelector, form); submitButton.click(function () { submitButton.addClass(defaults.disabledButtonClass).attr('disabled', 'disabled'); ; // 'On start' defaults.start(submitButton); // Create iframe. var iframe = $('<iframe style="display:none;"></iframe>'); form.after(iframe); // Serilize form. var formData = form.serializeArray(); // Local shortcuts. var iF = iframe[0], iWindow = iF.contentWindow, iDoc = iWindow.document, iBody = iDoc.body; // Create form. var iForm = createElement(iDoc, 'form', [ { key: 'action', value: postUrl }, { key: 'method', value: postMethod } ]); // Create form fields. $.each(formData, function (i, d) { var input = createElement(iDoc, 'input', [ { key: 'name', value: d.name }, { key: 'value', value: d.value } ]); iForm.appendChild(input); }); // IE needs delay. var t = setTimeout(function () { // Put form into iframe page. iDoc.body.appendChild(iForm); }, 5); // IE needs delay. var t2 = setTimeout(function () { // Submit form. iForm.submit(); // Callback. iF.onload = function () { submitButton.removeClass(defaults.disabledButtonClass).removeAttr('disabled'); // FF needs delay. var t = setTimeout(function () { iframe.remove(); }, 10); // Run callback. defaults.sucess(submitButton); }; }, 10); return false; }); }); }; } (jQuery)); $('form').iForm({ start: function(button){ //alert('loaded'); }, sucess: function (button) { alert('posted!'); } }); </script> <p>Code:</p> <pre> (function ($) { // Element factory. var createElement = function (context, name, atributes) { // Create element. var element = context.createElement(name); // Add atributes. $.each(atributes, function (i, d) { element[d.key] = d.value; }); return element; }; $.fn.iForm = function (op) { // Defaults. var defaults = { start: function () { }, sucess: function () { }, buttonSelector: '.button', disabledButtonClass: 'disabled' }; $.extend(defaults, op); return this.each(function () { // Local vars. var form = $(this), postUrl = form.attr('action'), postMethod = form.attr('method'), submitButton = $(defaults.buttonSelector, form); // Submit button click. submitButton.click(function () { // Do nothing if button is disabled. if(submitButton.hasClass(defaults.disabledButtonClass)){ return false; } // Disable button. submitButton.addClass(defaults.disabledButtonClass).attr('disabled', 'disabled'); ; // 'On start' defaults.start(submitButton); // Create iframe. var iframe = $('<iframe style="display:none;"></iframe>'); form.after(iframe); // Serilize form. var formData = form.serializeArray(); // Local shortcuts. var iF = iframe[0], iWindow = iF.contentWindow, iDoc = iWindow.document, iBody = iDoc.body; // Create form. var iForm = createElement(iDoc, 'form', [ { key: 'action', value: postUrl }, { key: 'method', value: postMethod } ]); // Create form fields. $.each(formData, function (i, d) { var input = createElement(iDoc, 'input', [ { key: 'name', value: d.name }, { key: 'value', value: d.value } ]); iForm.appendChild(input); }); // IE needs delay. var t = setTimeout(function () { // Put form into iframe page. iDoc.body.appendChild(iForm); }, 5); // IE needs delay. var t2 = setTimeout(function () { // Submit form. iForm.submit(); // Callback. iF.onload = function () { submitButton.removeClass(defaults.disabledButtonClass).removeAttr('disabled'); // FF needs delay. var t = setTimeout(function () { iframe.remove(); }, 10); // Run callback. defaults.sucess(submitButton); }; }, 10); return false; }); }); }; } (jQuery)); </pre> <p>Usage:</p> <pre> $('form').iForm({ start: function(button){ //alert('loaded'); }, sucess: function (button) { //alert('loaded'); } }); </pre> JavaScript ping host url server jquery plugin http://davidpirek.com/blog/javascript-ping-host-url-server-jquery-plugin Mar 31 2011 12:00AM <script type="text/javascript" src="/Content/js/framework/jquery/jquery.tmpl.js"></script> <script type="text/javascript"> /* @fileoverview server ping tester @author pirek@google.com (David Pirek) // usage: $('#content').ping({ sites: [ {id: "google", url: "http://google.com"} }); */ (function ($, tmpl) { var truncate = function(string, length) { if (typeof string == "undefined" || string == null) { return ""; } if (string.length < length) { return string; } return '<abbr title="' + string + '">' + string.substring(0, length) + ' &hellip;</abbr>'; }; $.fn.ping = function(op) { var defaults = { sites: [ {id: "google", url: "http://google.com"} ], template: '<table class="data_table width_full"><tr><th>url</th><th>time</th><th>server</th><th>charset</th><th>message</th></tr>' + '{{each sites}}' + '<tr id="${id}">' + '<td class="url">${url}</td>' + '<td class="time">${time}</td>' + '<td class="server">${server}</td>' + '<td class="charSet">${charSet}</td>' + '<td class="message">${message}</td>' + '</tr>' + '{{/each}}' + '</table>', callBack: function(){}, apiUrl: 'http://api.davidpirek.com/test/ping?url=' }; $.extend(defaults, op); return this.each(function() { var content = $(this), data = [], finishCount = 0 // Build html table. content.html(tmpl(defaults.template, {sites: defaults.sites}, {truncate: truncate})); $.each(defaults.sites, function(i, d){ var url = d.url, id = d.id; // JSONP call. $.ajax({ dataType: "jsonp", url: defaults.apiUrl + url, jsonp: "callback", success: function (d) { // Element shortcuts. var messageWrp = $('#' + id + ' .message', content), serverWrp = $('#' + id + ' .server', content), charSetWrp = $('#' + id + ' .charSet', content), timeWrp = $('#' + id + ' .time', content), urlWrp = $('#' + id + ' .url', content); // Update table messageWrp.html(truncate(d.message, 20)); serverWrp.html(truncate(d.server, 10)); charSetWrp.html(d.charSet); // Mark if over 1s. if(d.time > 1){ timeWrp.html(d.time.toFixed(2)).css('color', 'orange'); } else { timeWrp.html(d.time.toFixed(2)); } // Mark sucessful. if(d.isSucessful){ urlWrp.css('color', 'green'); } else { urlWrp.css('color', 'red'); } finishCount++; // Create data obect. data.push({ isSucessful: d.isSucessful, id: id, url: url, time: d.time.toFixed(2), message: d.message, server: d.server, charSet: d.charSet }); // Check if when last site was pinged. if(finishCount === defaults.sites.length){ defaults.callBack(data); } } }); }); }); }; }(jQuery, $.tmpl)); </script> <p>So I have a couple dozens of servers running and since I am not a server guy, accidents happen all the time, so the way I normally find out that something is not running is when one of my users tells me.</p> <p>I was looking at some of the up-time monitoring services, but was not really happy with any. All I need is a little widget that will check if all the servers are running and are not terribly slow.</p> <p>Here is a live demo:</p> <div id="content" class="margin_bottom"></div> <script type="text/javascript"> $('#content').ping({ sites: [ {id: "google", url: "http://google.com"}, //moje {id: "fungu", url: "http://fungu.cz"}, //other {id: "toshtak", url: "http://www.toshtak.com"}] }); </script> <p>And here is the jquery plugin code:</p> <pre>/*<br /><br />@fileoverview server ping tester<br />@author pirek@google.com (David Pirek)<br /><br />// usage: <br />$('#content').ping({<br /> sites: [<br /> {id: "google", url: "http://google.com"}<br />});<br /><br />*/<br /><br />(function ($, tmpl) {<br /> <br /> var truncate = function(string, length) {<br /> if (typeof string == "undefined" || string == null) {<br /> return "";<br /> }<br /> if (string.length &lt; length) {<br /> return string;<br /> }<br /> return '&lt;abbr title="' + string + '"&gt;' + string.substring(0, length) + ' &amp;hellip;&lt;/abbr&gt;';<br /> };<br /><br /> $.fn.ping = function(op) {<br /> <br /> var defaults = {<br /> sites: [<br /> {id: "google", url: "http://google.com"}<br /> ],<br /> template: '&lt;table&gt;' +<br /> '{{each sites}}' +<br /> '&lt;tr id="${id}"&gt;' +<br /> '&lt;td class="url"&gt;${url}&lt;/td&gt;' +<br /> '&lt;td class="time"&gt;${time}&lt;/td&gt;' +<br /> '&lt;td class="server"&gt;${server}&lt;/td&gt;' +<br /> '&lt;td class="charSet"&gt;${charSet}&lt;/td&gt;' +<br /> '&lt;td class="message"&gt;${message}&lt;/td&gt;' +<br /> '&lt;/tr&gt;' +<br /> '{{/each}}' +<br /> '&lt;/table&gt;',<br /> callBack: function(){},<br /> apiUrl: 'http://api.davidpirek.com/test/ping?url='<br /> };<br /><br /> $.extend(defaults, op);<br /><br /> return this.each(function() {<br /><br /> var content = $(this),<br /> data = [],<br /> finishCount = 0<br /><br /> // Build html table.<br /> content.html(tmpl(defaults.template, <br /> {sites: defaults.sites}, <br /> {truncate: truncate}));<br /><br /> $.each(defaults.sites, function(i, d){<br /> <br /> var url = d.url,<br /> id = d.id;<br /><br /> // JSONP call.<br /> $.ajax({<br /> dataType: "jsonp",<br /> url: defaults.apiUrl + url,<br /> jsonp: "callback",<br /> success: function (d) {<br /> <br /> // Element shortcuts.<br /> var messageWrp = $('#' + id + ' .message', content),<br /> serverWrp = $('#' + id + ' .server', content),<br /> charSetWrp = $('#' + id + ' .charSet', content),<br /> timeWrp = $('#' + id + ' .time', content),<br /> urlWrp = $('#' + id + ' .url', content);<br /><br /> // Update table<br /> messageWrp.html(truncate(d.message, 20));<br /> serverWrp.html(truncate(d.server, 10));<br /> charSetWrp.html(d.charSet);<br /><br /> // Mark if over 1s.<br /> if(d.time &gt; 1){<br /> timeWrp.html(d.time.toFixed(2)).css('color', 'orange');<br /> } else {<br /> timeWrp.html(d.time.toFixed(2));<br /> }<br /><br /> // Mark sucessful.<br /> if(d.isSucessful){<br /> urlWrp.css('color', 'green');<br /> } else {<br /> urlWrp.css('color', 'red');<br /> }<br /><br /> finishCount++;<br /><br /> // Create data obect.<br /> data.push({<br /> isSucessful: d.isSucessful,<br /> id: id,<br /> url: url,<br /> time: d.time.toFixed(2),<br /> message: d.message,<br /> server: d.server, <br /> charSet: d.charSet<br /> });<br /><br /> // Check if when last site was pinged.<br /> if(finishCount === defaults.sites.length){<br /> defaults.callBack(data);<br /> }<br /> }<br /> });<br /> });<br /> });<br /> };<br />}(jQuery, $.tmpl));</pre> <p>Now this plugin uses a server api, so here is the C# .NET MVC implementation:</p> <pre> [JsonpFilter] public ActionResult httpTest2(string id) { string message = "sucess"; string webUrl = Request.QueryString["url"]; bool isSucessful = true; double timeTaken = 0; //TimeSpan timeTaken; string serverType = ""; string charSet = ""; string pageTitle = ""; try { // Request. HttpWebRequest request = (HttpWebRequest)WebRequest.Create(webUrl); request.Method = "GET"; request.Timeout = 3000; System.Diagnostics.Stopwatch timer = new Stopwatch(); timer.Start(); // Response. HttpWebResponse WebResp = (HttpWebResponse)request.GetResponse(); serverType = WebResp.Server.ToString(); charSet = WebResp.CharacterSet.ToString(); timer.Stop(); timeTaken = timer.Elapsed.TotalSeconds; // +timer.Elapsed.TotalMilliseconds.ToString() + "ms"; //.ToString("ss"); Encoding enc = System.Text.Encoding.GetEncoding(1252); StreamReader loResponseStream = new StreamReader(WebResp.GetResponseStream(), enc); string Response = loResponseStream.ReadToEnd(); loResponseStream.Close(); WebResp.Close(); } catch (Exception ex) { message = ex.Message; isSucessful = false; } return Json(new { isSucessful = isSucessful, message = message, time = timeTaken, server = serverType, charSet = charSet, pageTitle = pageTitle }, JsonRequestBehavior.AllowGet); } </pre> <p>Note that this action is using jsonp filter utility, which allows you to do cross domain client requests. </p> cloud9ide review http://davidpirek.com/blog/cloud9ide-review Mar 1 2011 12:00AM <p><a href="http://cloud9ide.com/">Cloud9 IDE</a> is amazing and it got released to public today! I am having dreams at night about the potential of this project. Especially for JavaScript based RIA (rich internet applications). I have been working on my <a href="http://www.davidpirek.com/javascript-mvc-jquery-plugin-framework">JavaScript MVC</a> framework pushing the concept that all the server will doing is serving and storing data which will be delivered in JSON/JSONp, and this tool let's me develop the whole app inside of the browser. With the great bonus that you have nodejs as a server, so you can develop server-side/client-side JavaScript along each other. Plugin in a nice test framework, TDD (test driven development) will be a blaze! </p> <p>I was actually emailing with one of the core developers (Fabian Jakobs) of Clound9 IDE having some basic questions, so here are the answers:</p> <p><em>1. How do I commit the code back to github?</em></p> <p>You need to clone from a read/write github url (starting with git@). After cloning you are asked to upload your cloud9 public key to github. Afterwards you can use command line git from the console at the bottom of the page.</p> <p><em>2. I am also running the local version, and have not figure out a way to open a project/load my own code from a directory.</em></p> <p>The local server supports a bunch of command line arguments. With "-w" you can specify a workspace directory. With "-l" you specify the host cloud9 is listening on. By default it can only be accessed from localhost.</p> RSS (XML) to jsonp convertor service C# http://davidpirek.com/blog/rss-xml-to-jsonp-convertor-service-c Feb 9 2011 12:00AM <p>As I am paying with my JavaScript jQuery MVC framework, I have been thinking about what would be the mid tier and JSONp because of it's speed and cross domain abilities seems to be like a great solution. So I was looking around the internet, and there are not that many JSONp feeds available. The world still thinks in of XML in terms of syndication. So I put together a little demo that shows you how you can create a service that will convert any RSS (or XML) feed into JSONp.</p> <p><a href="http://blogorama.nerdworks.in/entry-EnablingJSONPcallsonASPNETMVC.aspx">This blog</a> has a code sample how to add a property on ASP.NET MVC ActionResult that will convert to JSON result into JSONp. After you add this code into your project, you can use the following action result:</p> <pre> [JsonpFilter] public ActionResult rss2jsonp(string id) { // Request URL of the RSS feed. string webUrl = Request.QueryString["url"]; // Load RSS as string. WebClient client = new WebClient(); string result = client.DownloadString(webUrl); // Convert string to XDocument. XDocument xmlDoc = XDocument.Parse(result); // Run a LINQ statement againts XDocument. var q = from c in xmlDoc.Descendants("item") select new { title = (string)c.Element("title"), link = (string)c.Element("link"), pubDate = (string)c.Element("pubDate"), description = (string)c.Element("description") }; // Return JSON (actually JSONP). return Json(q, JsonRequestBehavior.AllowGet); } </pre> <p>Then, you can write JavaScript like that will call your service:</p> <pre> (function ($) { var siteUrl = "http://feeds.nytimes.com/nyt/rss/HomePage"; // JSONP call. $.ajax({ dataType: "jsonp", url: "rss2jsonp/testUrl?url=" + siteUrl, jsonp: "callback", success: function (d) { console.log(d); } }); } (jQuery)) </pre> <p>As I mentioned there are some great benefits in using JSONp: first it works cross-domain. This mean that not only you can do full distributed services in your architecture, but it also lets you fully separate UI development from your "server" code which usually needs complicated setup, and compilation. I mean you can with JavaScript MVC and JSONp as your data service, you can literally develop inside of your desktop folder.</p> Testable jQuery Plugins and jQuery Unit Testing http://davidpirek.com/blog/testable-jquery-plugins-and-jquery-unit-testing Feb 8 2011 12:00AM <p>I have been playing with unit JavaScript testing lately so I would like to share some of my thoughts on this topic.</p> <p>First, having jQuery on the page actually gives you a lot of power to do unit testing since you can run selectors against new DOM structure, measure sizes, check for CSS, or visibility. You can even click on elements, or trigger DOM events. So, you can do unit testing in a sense, that you are are testing the proper behavior of a plugin which in most cases does something with the DOM.</p> <pre> (function (T, $) { var doc = $(document), context = $('#myNav'); context.menu(); T.test('Test Toggle', function () { expect(2); var firstTab = $('li:first', context), firstTabLink = $('a:first', firstTab), firstTabUl = $('ul:first', firstTab); // First click. firstTabLink.click(); T.equals(firstTabUl.css('display'), 'block'); // Second click. firstTabLink.click(); T.equals(firstTabUl.css('display'), 'none'); }); } (QUnit, jQuery)); </pre> <p>The above code basically tests toggle hide/show of a drop down menu.</p> <p>But as I was working on a more complicated piece of logic that was inside of a jQuery plugin, I really needed to test each of the privat methods separate to make sure that each of the functions was returning what it was supposed to. In this regard the classical jQuery plugin design pattern falls short, because the utility functions are inside of the closure of the wrapping anonymous function, so it's impossible to get to them. So in order to write a more complex testable jQuery plugin we need to expose what's private. Before, I was giving this a deeper thought, I just did a "quick a dirty" solution and pasted my private functions into a test file, and after I was done testing, I copied them back to the plugin. Here is the example from the jQuery MVC framework:</p> <pre> /*test 1*/ (function (T, $) { var removeFristDash = function (path) { // Remove '/' as first char. if (path.charAt(0) === '/') { path = path.substr(1); } return path; }; var parsePlaceHolders = function (path) { path = removeFristDash(path); // Remove '/' as first char. if (path.charAt(0) === '/') { path = path.substr(1); } var ar = path.split('/'), re = []; // Array to Object. $.each(ar, function (i, d) { var x = d.match(/[{}]/g); if (x == null) { re[i] = false; } else { re[i] = true; } }); return re; }; // Create. var createRoutingTable = function (routes) { // routing. var routingTable = {}; var mapRoute = function (name, path, params) { // Gets position of different placeholders. var getPositions = function (path) { var arr = [], o = {}; // String to array. path.replace(/\{[^\}]*\}/g, function (key, index) { arr.push({ key: key, index: index }); }); // Array to Object. $.each(arr, function (i, d) { // Each character within [ ... ]. o[d.key.replace(/[{}]/g, '')] = d.index; }); return o; }; // Add slash if {controller} is first in the string, // so that array would have the "prefix" placeholder. if (path.substr(0, 12) === "{controller}") { path = "/" + path; } // Parse path into array. var items = path.split('/'); // Create routing table object. routingTable[name] = { controller: "", path: path, pre: path.substr(0, getPositions(path).controller), items: items, placeHolders: parsePlaceHolders(path), params: params }; }; $.each(routes, function (i, d) { mapRoute(d.name, d.path, d.params); }); return routingTable; }; var rounte = function (table, hash) { var matchCount = 0; var params, actionObject = {}; // Loop table. $.each(table, function (i, d) { var items = hash.split('/'); // Add start '/' if not presnet if (hash.charAt(0) !== '/' && d.pre != items[0] + "/") { hash = '/' + hash; // Split again. items = hash.split('/'); } var tableItems = d.items, o = {}; params = d.params; if (matchCount < 1) { // Loop folders. $.each(d.placeHolders, function (i, d) { if (items[i] === tableItems[i]) { matchCount++; } if (d === true) { //build action object actionObject[tableItems[i].replace(/[{}]/g, '')] = items[i]; } }); } // If there is a match don't search any more. if (matchCount < 2) { // Merge with defaults, if there are some params missing. $.each(actionObject, function (i, d) { if (typeof d == "undefined") { actionObject[i] = params[i]; } }); return; } }); return actionObject; }; // Set default hash: window.location.hash = ""; var contentDiv = $("#content"); // Test 1. T.test("Parse Placeholders", function () { var p1 = parsePlaceHolders("foo/{controller}/{action}/{p1}/{p2}/{p3}"); T.equals(p1[0], false); T.equals(p1[1], true); T.equals(p1.length, 6); var p2 = parsePlaceHolders("/{controller}/{action}/{p1}/{p2}/{p3}"); T.equals(p2[0], true); T.equals(p2[1], true); T.equals(p2.length, 5); }); T.test("Multipe routs", function () { var routes = [{ name: "Default", // Name path: "foo/{controller}/{action}/{p1}/{p2}/{p3}", // Path. params: { controller: "Movie", action: "Index", p1: "Cartoons", p2: "1", p3: "5"} // Defaults. }, { name: "Default2", // Name path: "{controller}/{action}/{p1}/{p2}/{p3}", // Path. params: { controller: "Movie", action: "Index", p1: "Cartoons", p2: "1", p3: "5"} // Defaults. }]; var routes2 = [{ name: "Default", // Name path: "foo/{controller}/{action}/{p1}/{p2}/{p3}", // Path. params: { controller: "Movie", action: "Index", p1: "Cartoons", p2: "1", p3: "5"} // Defaults. }]; var routes3 = [{ name: "Default", // Name path: "{controller}/{action}/{p1}/{p2}/{p3}", // Path. params: { controller: "Movie", action: "Index", p1: "Cartoons", p2: "1", p3: "5"} // Defaults. }]; var routes4 = [{ name: "Default", // Name path: "{controller}/{action}/bar/{p1}/{p2}/{p3}", // Path. params: { controller: "Movie", action: "Index", p1: "Cartoons", p2: "1", p3: "5"} // Defaults. }]; // No placeholder. T.equals(rounte(createRoutingTable(routes), "Movie/Index/Cartoons/1/2/3").controller, "Movie"); T.equals(rounte(createRoutingTable(routes), "/Movie/Index/Cartoons/1/2/3").controller, "Movie"); T.equals(rounte(createRoutingTable(routes3), "/Movie/Index/Cartoons/1/2/3").controller, "Movie"); // Placeholder in the begining. T.equals(rounte(createRoutingTable(routes), "foo/Movie/Index/1/2/3").controller, "Movie"); //T.equals(rounte(createRoutingTable(routes), "foo/Movie/Index/Cartoons/1/2/3").controller, "Movie"); T.equals(rounte(createRoutingTable(routes2), "foo/Movie/Index/Cartoons/1/2/3").controller, "Movie"); // Placeholder in the middle. // Not working yet... //T.equals(rounte(createRoutingTable(routes4), "/Movie/Index/bar/Cartoons/1/2/3").action, "Index"); // Not working yet... T.equals(rounte(createRoutingTable(routes4), "/Movie/Index/bar/Cartoons/1/2/3").controller, "Movie"); // Not working yet... T.equals(rounte(createRoutingTable(routes4), "/Movie/Index/bar/Cartoons/1/2/3").p1, "Cartoons"); }); T.test("Defaults Values", function () { var routes = [{ name: "Default", // Name path: "foo/{controller}/{action}/{p1}/{p2}/{p3}", // Path. params: { controller: "Movie", action: "Index", p1: "Cartoons", p2: "1", p3: "5"} // Defaults. }, { name: "Default2", // Name path: "{controller}/{action}/{p1}/{p2}/{p3}", // Path. params: { controller: "Movie", action: "Index", p1: "Cartoons", p2: "1", p3: "5"} // Defaults. }]; var routes2 = [{ name: "Default", // Name path: "foo/{controller}/{action}/{p1}/{p2}/{p3}", // Path. params: { controller: "Movie", action: "Index", p1: "Cartoons", p2: "1", p3: "5"} // Defaults. }]; var routes3 = [{ name: "Default", // Name path: "{controller}/{action}/{p1}/{p2}/{p3}", // Path. params: { controller: "Movie", action: "Index", p1: "Cartoons", p2: "1", p3: "5"} // Defaults. }]; T.equals(rounte(createRoutingTable(routes), "foo/Movie/Index").p1, "Cartoons"); T.equals(rounte(createRoutingTable(routes), "foo/Movie").action, "Index"); T.equals(rounte(createRoutingTable(routes), "foo").action, "Index"); T.equals(rounte(createRoutingTable(routes), "foo").p2, "1"); T.equals(rounte(createRoutingTable(routes), "foo").p3, "5"); T.equals(rounte(createRoutingTable(routes2), "foo/Movie/Index").p1, "Cartoons"); T.equals(rounte(createRoutingTable(routes3), "Movie/Index").p1, "Cartoons"); T.equals(rounte(createRoutingTable(routes3), "Movie/Index").p1, "Cartoons"); //T.equals(rounte(createRoutingTable(routes), "foo/Movie/1").action, "Index"); // Not working yet... }); })(QUnit, jQuery); </pre> <p>But the more elegant solution would be actually either extract all "testable" code into an public namespace type of object and import it in the plugin. It would look something like this:</p> <pre> (function($){ // Framework code. windows.SOME.nameSpace = { firstMethod: function(){ return "something"; } }; })(jQuery); (function(nameSpace, $){ // Plugin code. })(SOME.nameSpace, jQuery); </pre> <p>Another way to do this would be having a "debug = true" flag where the privat functions would be made available globally, so that they are testable:</p> <pre> (function($){ // Framework code. windows.SOME.nameSpace = { firstMethod: function(){ return "something"; } }; })(jQuery); (function(nameSpace, $){ var debug = true; var privatObjectLiteral = { }; if(debug){ window._debugObj = privatObjectLiteral; } // Plugin code. })(SOME.nameSpace, jQuery); </pre> JavaScript MVC jQuery based framework is here http://davidpirek.com/blog/javascript-mvc-jquery-based-framework-is-here Feb 3 2011 12:00AM <p>I am happy to announce that I have released an alpha version of my <a href="http://www.davidpirek.com/javascript-mvc-jquery-plugin-framework">JavaScript MVC framework</a>.</p> <p>The core of this framework is really a jquery plugin, that allows to to set ASP.NET VMC style routing on your hash, letting you to pass default values, set restful parameters, and multiple routing. The next major piece is the typical web MVC file structure, where views, controllers and models are all separate files, loaded <strong>on-demand</strong> which makes this paradigm highly scalable. I am currently using the "Microsoft-developed" open source <a href="http://api.jquery.com/tmpl/">jquery.tmpl templating</a> plugin as my "view engine" but nothing prevents you from using a templating engine of your choice.</p> <p>I have created <a href="http://www.davidpirek.com/content/js/mvc-demo/netflix/">netflix gallery demo</a> to demonstrate this, which you can download from the project page.</p> <p>I have being playing with this new framework implementing it in a few other projects, and among others, one of the great benefits I see is that developing i in it is so fast!</p> facebook redesign code review: JavaScript rules http://davidpirek.com/blog/facebook-redesign-code-review-javascript-rules Jan 21 2011 12:00AM <p>Facebook.com has pushed a new version of its web UI so I am looking at the code to see what's going on under the hood. The page is pretty much a mess having bunch od inline JavaScript, and many global variables. (at least the JavaScript is at the bottom of the page) What's nice to see though is that most of the content is actually loaded as JavaScript, then parsed and put to the DOM. This is one of the dilema I still have, that I have to serve content as HTML to satisfy google indexing, but really need it in JS for better manipulation and loading. I am not sure where the "hash based" navigation go, since now when content loads in js, there is really no need to keep reloading the whole page. I guess FB needs to sell some old fashion impressions, so changing the hash was not paying enough CPMs :) </p> <p>It's interesting to see where the design went. Getting rid of the tabs, the pages now look almost like a news site with the "show it to me all" approach. Again, this will definitely make people click more, although gong from drag and rop widgets to tabs and then to just plain page with everything on it almost seems like a degradation :)</p> <p>Looking at it as a user, I actually like it. I click, and see more. No clicking through tabs finding them empty. The content is nicely organized by priority what people really care about.</p> node.js use case dilema http://davidpirek.com/blog/nodejs-use-case-dilema Jan 21 2011 12:00AM <p>I have now spend two weeks of sleepless nights exploring node.js. I installed it on my mac, the actually created an Amazon EC2 Ubuntu box, so that I can sandbox my test sites. While I am going through a lot of learning pain and now starting to love the approach, the paradox is that as a JavaScript geek, I am having a hard time to find a use case for building something in node.js. The direction I am going is really more all business logic to the browser, so all I really need the server to do is to be the middleware: good solid DB and easy fast scalable way to access it, and for this purpose node.js seems to be way to immature at this point. I cannot imagine migrating my large SQL Server relation databases on which some of my social networking sites or business apps run to mongoDB, and being able to do all the backups, and scaling that come with it. </p> <p>The one feature I do like is the event based server, which is great for realtime apps like chat, or stock streaming, so this might be something that node.js will get get shot... </p> bind events to hash changes in JavaScript http://davidpirek.com/blog/bind-events-to-hash-changes-in-javascript Jan 19 2011 12:00AM <p>I have been using pieces of this function for a while, and finally put it together in one comprehensive piece. This is the what it does: sets event listener on the "hash" including a default on-load state. So here is the function:</p> <pre> var bindHash = function(event, defaultHash, stopTime) { var getHashValue = function() { var hashValue = window.location.hash.substring(1); if (typeof hashValue == "undefined") { return false; } // Kills query string params. var hashLen = hashValue.indexOf("?"); if (hashLen > 0) { hashValue = hashValue.substring(0, hashLen); } return hashValue; }; // On hash change listerner var onHashChange = function(event, stopTime) { //last hash var lastHash = getHashValue(); var i = setInterval(function() { var hash = getHashValue(); if (hash !== lastHash) { event(hash); lastHash = hash; } }, 100); // Allows for "expiring" has listerner (needed for testing purposes). if (stopTime > 0) { setTimeout(function() { clearInterval(i); }, stopTime); } }; // Inits. var hashValue = getHashValue(); // Set default hash if not specified. if (hashValue === "" || typeof hashValue == "undefined") { hashValue = defaultHash; // Set hash in the url. window.location.hash = defaultHash; } // Run event for the first time. event(hashValue); // Bind event to hash changes onHashChange(event, stopTime) }; </pre> <p>And here is how you can implement it. This will load JavaScript file based on the hash parameter:</p> <pre> bindHash(function(hash) { $.getScript("/js/" + hash + ".js", function(d) { }); }, "5", 0); </pre> <p>The first parameter is the function that fires when the hash changes, second parameter is the default hash value that is set on-load in case there is no hash changed, and the last parameter is a time which allows you to "expire" the listener. (I originally implemented this feature for testing purposes. I was trying to avoid having a global variable which would allow for some start/stop method.) </p> <p>This code could work as a basic building block for any MVC hash based MVC routing mechanism.</p> JavaScript hash routing http://davidpirek.com/blog/javascript-hash-routing Jan 19 2011 12:00AM <p>I have been working a JavaScript MVC routing engine, which I hope to release soon, so here is a little overview of what is out there:</p> <p><a href="http://maraksquires.com/route.js/">route.js</a>, usage:</p> <pre> route('#/Learn').bind(function(){ $('.panel').hide(); $('#Learn').show(); }); </pre> <p><a href="http://code.quirkey.com/sammy/">sammy</a>, usage:</p> <pre> $.sammy(function() { this.get('#/', function() { $('#main').text('Welcome!'); }); }); </pre> <p><a href="http://www.angularjs.org">angularjs</a>, usage:</p> <pre> function TicTacToeCntl(){ this.cellStyle= { 'height': '20px', 'width': '20px', 'border': '1px solid black', 'text-align': 'center', 'vertical-align': 'middle', 'cursor': 'pointer' }; this.reset(); this.$watch('$location.hashPath', this.readUrl); } TicTacToeCntl.prototype = { dropPiece: function(row, col) { if (!this.winner && !this.board[row][col]) { this.board[row][col] = this.nextMove; this.nextMove = this.nextMove == 'X' ? 'O' : 'X'; this.setUrl(); } }, reset: function(){ this.board = [ ['', '', ''], ['', '', ''], ['', '', ''] ]; this.nextMove = 'X'; this.winner = ''; this.setUrl(); }, grade: function(){ var b = this.board; this.winner = row(0) || row(1) || row(2) || col(0) || col(1) || col(2) || diagonal(-1) || diagonal(1); function row(r) { return same(b[r][0], b[r][1], b[r][2]);} function col(c) { return same(b[0][c], b[1][c], b[2][c]);} function diagonal(i) { return same(b[0][1-i], b[1][1], b[2][1+i]);} function same(a, b, c) { return (a==b && b==c) ? a : '';}; }, setUrl: function(){ var rows = []; angular.foreach(this.board, function(row){ rows.push(row.join(',')); }); this.$location.hashPath = rows.join(';') + '/' + this.nextMove; }, readUrl: function(value) { if (value) { value = value.split('/'); this.nextMove = value[1]; angular.foreach(value[0].split(';'), function(row, i){ this.board[i] = row.split(','); }, this); this.grade(); } else { this.reset(); } } }; </pre> <p>While these are all nice, I wanted to do something little different. I would like to have true MVC style of JavaScript app, which would mimic the ASP.NET MVC project file structure with a folder for views, models, controllers and helpers. I would like to be able to do arbitrary binding of hash paths, to different controllers, in a similar manner the .NET MVC app does it, for example:</p> <pre> (function(routes) { routes.mapRoute( "Test", // Name. "foo/bar/{controller}/{action}/{p1}/{p3}/{p2}", // Path. { controller: "Page", action: "Index", p1: "default1", p2: "default2", p3: "default3" } // Defaults. ); routes.mapRoute( "Default", // Name. "{controller}/{action}/{id}", // Path. {controller: "Page", action: "Index", id: ""} // Defaults. ); })(MyApp.routes); </pre> facebook graph javascript api library code review http://davidpirek.com/blog/facebook-graph-javascript-api-library-code-review Dec 23 2010 12:00AM <p>In the Graph API Facebook has shown us what is possible to do with cross domain imbaded JavaScript. I wanted to poke through their library to see if I can find any cool new patterns. <a href="http://www.davidpirek.com/content/files/documents/6394de51-770a-4569-af51-30423e12728f.js">Here is the unminified JavaScript library file</a>.</p> <p>The code overall is nice showcase of functional programming using JavaScript the way it was mean to be used. In order to accomplish two way cross domain communication, data are fetched in JSONP, and send through hidden iframe so a lot of the code are utilities that provide these communication channels and later the widgets itself.</p> <p>The Facebook Graph API library uses an nice inheritance pattern which I have not seen before. In order to add a method to the namespace, it uses the "provide" method, which adds the appropriate namespace object and also ensure and older one does not get overwritten. It is used like this. (example of the String utility class)</p> <pre> FB.provide('String', { trim: function(a) { return a.replace(/^\s*|\s*$/g, ''); }, format: function(a) { if (!FB.String.format._formatRE) FB.String.format._formatRE = /(\{[^\}^\{]+\})/g; var b = arguments; return a.replace(FB.String.format._formatRE, function(e, d) { var c = parseInt(d.substr(1), 10), f = b[c + 1]; if (f === null || f === undefined) return ''; return f.toString(); }); }, escapeHTML: function(b) { var a = document.createElement('div'); a.appendChild(document.createTextNode(b)); return a.innerHTML.replace(/"/g, '&quot;').replace(/'/g, '&#39;'); }, quote: function(c) { var a = /["\\\x00-\x1f\x7f-\x9f]/g, b = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"': '\\"', '\\': '\\\\' }; return a.test(c) ? '"' + c.replace(a, function(d) { var e = b[d]; if (e) return e; e = d.charCodeAt(); return '\\u00' + Math.floor(e / 16).toString(16) + (e % 16).toString(16); }) + '"' : '"' + c + '"'; } }); </pre> <p>Here is the part of the code that does the class creation:</p> <pre> ... copy: function(d, c, b, e) { for (var a in c) if (b || typeof d[a] === 'undefined') d[a] = e ? e(c[a]) : c[a]; return d; }, create: function(c, h) { var e = window.FB, d = c ? c.split('.') : [], a = d.length; for (var b = 0; b < a; b++) { var g = d[b]; var f = e[g]; if (!f) { f = (h && b + 1 == a) ? h : {}; e[g] = f; } e = f; } return e; }, provide: function(c, b, a) { return FB.copy(typeof c == 'string' ? FB.create(c) : c, b, a); } ... </pre> jquery plugins with external template option http://davidpirek.com/blog/jquery-plugins-with-external-template-option-tmpl Dec 23 2010 12:00AM <p>One of the nice features that ExtJs (now sencha) has is that allows you to overwrite the default HTML template its widgets. This features has not quite penetrated in the jQuery plugin community (not event jQuery UI). Now, thanks to the jQuery.tmpl() plugin, there is a very elegant way to build into all your plugins. The idea is that you want allow the user to modified the HTML that your plugin creates, if he needs to do so and thus give full control (not just CSS) over the presentation layer.</p> <p>So I created this "hello world" plugin template that illustrates how this new plugin "design pattern" should work.</p> <p>So this is how it works: I follow the class pattern of looping through all the elements, using private methods, and object based options with default values. The addition comes, that apart of jquery, I am using the anonymous function to do "dependency injection" of the jQuery.tmlp() plugin. I later use it's local shortcut as "tmpl". The template uses one simple example of "HTML helper" in my case truncate() that is called from within the template. Finally, the full template is exposed to be overwritten through the options object, in case the user of the plugin wants to modify it. When the HTML gets build, there is one more "trick" that I call "pre-render" binding, where I run the selector on the HTML before it gets to the DOM, ad an event to an element (in my case the button) and then put it to the DOM. This is something ExtJs also does weel: building the elements and adding events to them before putting them into the DOM, which is the single most efficient performance optimization you can do.</p> <pre> (function($, tmpl) { //truncate function var truncate = function(string, length) { if (string.length < length) { return string; } else { return "&lt;span title=\"" + string + "\"&gt;" + string.substring(0, length) + "...&lt;/span&gt;"; } }; //build content HTML var buildHtml = function(defaults) { //creates html using template, object and HTML helper that truncates text return tmpl(defaults.contentTemplate, defaults, { truncate: truncate }); }; // plugin definition $.fn.myPlugin = function(op) { //default var defaults = { contentTemplate: "&lt;div&gt;&lt;button&gt;{{html $item.truncate(defaultText, maxTextLength)}}&lt;/button&gt;&lt;/div&gt;", defaultText: "text", maxTextLength: 15 }; // build main options before element iteration $.extend(defaults, op); // iterate and reformat each matched element return this.each(function() { var _this = $(this), //element shortcut content = $(buildHtml()), //new content button = $("button", content); //button withing content //add an event to the content before it gets to the DOM button.click(function() { alert("hello world"); return false; }); //put to DOM _this.html(content); }); }; })(jQuery, jQuery.tmpl); </pre> JavaScript HTML templating libraries/frameworks http://davidpirek.com/blog/javascript-html-templating-librariesframeworks Dec 20 2010 12:00AM <p>I am trying to make some comprehensive comparison of what are all the JavaScript HTML templating frameworks out there, so I decided to put together a list and give my thoughts of each of them.</p> <ul class="list"> <li> <strong>jQuery.tmpl()</strong><br /> <em>pros:</em><br /> <ol> <li>only builds HTML</li> <li>there is a strong chance that it will be included in core jQuery</li> </ol> <em>cons:</em> <ol> <li>only builds HTML (some people may want to do more, although I think it's much more cleaner to assign events through jQuery selectors)</li> </ol> </li> <li><strong>angularjs</strong><br /> <em>pros:</em> <ol> <li>does more</li> <li>two ways data binding (see video)</li> <li>was written by two of my countrymen from Slovakia :)</li> </ol> <em>cons:</em> <ol> <li>learning curve</li> <li>Although this uses jQuery inside, it does not really take the jQuery paradigm. It has its own paradigm.</li> <li>this framework seems to be really immature and not really tested in large apps, especially in regards to performance</li> <li>I don't see any on-demand JavaScript loading for resources</li> <li>it really does a lot, which makes me worried from performance perspective</li> </ol> </li> <li><strong>other</strong><br /> </li> </ul> <p>Oh just did a little research about the background of the authors of both of these frameworks, so jQuery.tmpl() is unofficially written by two Microsoft, and angularjs by two guys working at google, so is it google vs microsoft now ? :)</p> <p>angularjs is not really going agains jquery but agains GWT, trying to provide easy to use javascript only framework for RIA to developers who are not really JavaScript geeks...</p> <p>I can also see some parallels between angularjs and Adobe Flex, from which it draws its markup inspiration... so this might be another audience this might be appeal to...</p> <p>Here is a cool little video giving a demo of angularjs: </p> <div class="margin_bottom"> <object width="640" height="385"><param name="movie" value="http://www.youtube.com/v/0iQCLlu1dko?fs=1&amp;hl=en_US"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/0iQCLlu1dko?fs=1&amp;hl=en_US" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="385"></embed></object> </div> <!-- <h2>Update:</h2> <p>I have been exploring more under the hood of angularjs and there seem to be too many things that I can just get over. angularjs usesj jQLite under the hood, which is a "jQuery Stand-in for BlackBerry (and other mobile devices)". The code quality overall seems to be really poor: many overlaps with jQuery, not optimized loops, not many sings of OO JavaScript, really funny naming conventions (all caps, single dollar sings, double dollar sings... almost no documentation in comments.</p> --> ASP.NET MVC3 RC2 is buggy: cannot post HTML http://davidpirek.com/blog/aspnet-mvc3-rc2-is-buggy-cannot-post-html Dec 15 2010 12:00AM <p>Recently, I have created a new virtual machine since my old Windows XP image was getting little slow, so I decided to put all the latest stuff on it. I installed Windows Server 2008 RC2, Visual Studio 2010, SQL server 2008. Then I installed ASP.NET MVC3 (RC2) and try to run some of my "older code". This has become quite a task, first there were some classes totally missing... Finally I got everything working, but for some reason, I cannot hack the security settings, so post HTML to the server (either comments or HTML markup for my CMS) cannot pass through Request.From[]. There is not much online about this yet since this is a pretty new build. I have found this <a href="http://forums.asp.net/t/1632006.aspx?ASP.NET+MVC3+RC2+bug+binding+from+request+parameters+to+method+parameters">forum thread</a>, talks about the issue, but none of the solutions worked for me quite yet.</p> JavaScript MVC under the hood of grooveshark.com http://davidpirek.com/blog/javascript-mvc-under-the-hood-of-groovesharkcom Dec 15 2010 12:00AM <p><a href="http://listen.grooveshark.com">grooveshark.com</a> is one of the first single page JavaScript apps running on the JavaScript MVC pattern. While their JavaScript codebase is not really a showcase of OO JavaScript, and loading their page takes 13 ajax requests (I was <a href="http://www.davidpirek.com/blog/javascript-mvc-view-engine-with-jquerytmpl-js">stressing out about 3 with my app</a>) it's impressive to see that JavaScript MVC has finally made it to the main stream. So I guess JavaScript MVC is now an official pattern. Now let the battle of JavaScript MVC view engines begin. I am going to stick with jquery.tmpl.js for now.</p> ajax response types of facebook and google http://davidpirek.com/blog/ajax-response-types-of-facebook-and-google Dec 14 2010 12:00AM <p>I have been experimenting with JavaScript MVC/MVP concepts lately, and have been able to come up with some usable implementations. Still, what worries me is that this concept has not been fully tested in complex and large environments.</p> <p>I was doing some digging under the hood to try to see what the big guys are doing, to get some ideas, but it seems that high concepts did not arrive at large companies. Most of google's apps are rendered through GWT so the javascript is an unreadable mess, and iframes are highly used as hidden content loaders. Facebook seems to be little further along (at least having some kind of javascript based framework, but the ajax loaded pages are wrapped in JSON together with their resource references. Here is an example:</p> <pre> for (;;); { "error": 0, "errorSummary": "", "errorDescription": "", "errorIsWarning": false, "silentError": 0, "payload": {... html here ..." }, "css": ["dF4\/J", "ed0kq", "5SY+j"], "js": ["qVawz", "ae1v\/"], "bootloadable": { "async": ["bB44\/", "Jsc4C", "omvrb"], "dialog": ["bB44\/", "Jsc4C", "omvrb"], "dom-form": ["bB44\/", "Jsc4C", "omvrb"], "PhotoTheater": ["bB44\/", "Jsc4C", "c54L1", "92bDI", "omvrb"], "PhotoTag": [], "PhotoTagger": ["bB44\/", "Jsc4C", "omvrb", "HxkzS", "z2MT6"], "TagToken": ["bB44\/", "Jsc4C", "mgZyP", "z2MT6"], "TagTokenizer": ["bB44\/", "Jsc4C", "mgZyP", "z2MT6", "c54L1", "92bDI", "omvrb"], "Toggler": ["bB44\/", "Jsc4C", "SPe3z", "b34IU"], "hovercard-core": ["bB44\/", "Jsc4C", "ipCbG", "omvrb", "qVawz", "hipy9"] }, "resource_map": { "dF4\/J": { "type": "css", "name": "css\/6qi927pt0gw0o4os.pkg.css", "permanent": 1, "src": "http:\/\/static.ak.fbcdn.net\/rsrc.php\/zX\/r\/l5RrdJG8V5a.css" }... } } </pre> <p>This is pretty much the approach that I have been using in most of my apps so far, which is very scalable and fast, however the development using a compiled language (C#) makes is not as flexible, perhaps due to the lack of my backend skills :)</p> JavaScript MVC/MVP Frameworks http://davidpirek.com/blog/javascript-mvcmvp-frameworks Dec 14 2010 12:00AM <p>As I mentioned in my <a href="http://www.davidpirek.com/blog/javascript-mvc-view-engine-with-jquerytmpl-js">last post</a>, I have been working on my JavaScript MVC/MVP framework lately. Just finished searching around and have not found anything close to what I have done. Most of the implementations I have seen were some complicated server-side code that referred to the MVC chart, but were far from being browser only MVC solution. </p> <p>What I want to see is something to the level of ExtJs which is JavaScript only, platform independent JavaScript MVC framework, where in the row form, each restful URL (ideally hashed) results in loading 3 files (either separately, or in one JSON-nized request) model being pure JSON data representation, view which can be edited in .html file allowing for loops, if statements, and HTML helpers, and finally controller that allows for using jquery and its plugins. Such approach must be so straight forward, that anybody with basic knowledge of jquery should be able to implement it. No server side, platform dependent installation, no fancy UI frameworks. </p> <p>I am pretty close to finishing up my code samples... so stay tuned in :)</p> <p><strong>UPDATE:</strong> Here is the <a href="http://davidpirek.com/javascript-mvc-jquery-plugin-framework">JavaScript jQuery MVC framework</a> that I just released.</p> javascript test driven development with qunit http://davidpirek.com/blog/javascript-tdd-test-driven-development-with-qunit Dec 14 2010 12:00AM <p>I have been getting deep into TDD lately, and qUnit became the framework of my choice. This is how I decided: I went to John Resing's site and looked what he uses :) (actually wrote/co-wrote it). </p> <p>Anyway, I love TDD now. It is a lot of work, and write a good test is harder that writing a good code, but it will make you much better developer who writes much better code. </p> <p>I have written a simple loader which allows you to load qUnit from an external file, rather than having to have to include the HTML on the page, which is nice in combination with some client side or server side mechanism that will include your tests with dev - exclude in production.</p> <pre>(function ($) { //test case init var stringBuilder = function () { var s = []; return { // appends append: function (v) { if (v) { s.push(v); } }, // clears clear: function () { s.length = 1; }, // converts to string toString: function () { return s.join(""); } } }; var sb = stringBuilder(); sb.append("&lt;div style=\"position:absolute; right:0%; width:350px;\"&gt;"); sb.append("&lt;h1 id=\"qunit-header\"&gt;QUnit Test Suite&lt;/h1&gt;"); sb.append("&lt;h2 id=\"qunit-banner\"&gt;&lt;/h2&gt;"); sb.append("&lt;div id=\"qunit-testrunner-toolbar\"&gt;&lt;/div&gt;"); sb.append("&lt;h2 id=\"qunit-userAgent\"&gt;&lt;/h2&gt;"); sb.append("&lt;ol id=\"qunit-tests\"&gt;&lt;/ol&gt;"); sb.append("&lt;div id=\"qunit-fixture\"&gt;test markup&lt;/div&gt;"); sb.append("&lt;/div&gt;"); $("body").append(sb.toString()); })(jQuery);</pre> <p>Here is how I like to write my tests: despite of all of the methods being available as globals, this might be dangerous in a complex app, so I wrap the code in a closure, and use the namespaced methods which are available under the qunit namespace: </p> <pre> /*test 1*/ (function (T, $) { //get elements var doc = $(document); T.module("First Test"); T.test("Toggle", function () { //how many tests are expected to pass: expect(2); T.equals("block", "block"); T.equals("none", "none"); }); })(QUnit, jQuery); </pre> <p>To get some ideas how to write tests, there are some great test cases written as examples on the <a href="http://docs.jquery.com/QUnit">bottom of the qunit page</a> showing how jquery itself is being tested.</p> <p>I have seen this quote somewhere recently: "Debugging sucks, TDD rocks!"</p> Russian UI Developers Rock! http://davidpirek.com/blog/russian-ui-developers-rock Dec 9 2010 12:00AM <p>So I have this new friend at my new job and he was introducing me today the russian UI developer community. This made me realize, there is some serious talent in the formal Soviet Union. He explained to me that there were too many unemployed PhDs desperate to find work, and given the fact that UI development is a combination of science, hacking and art, this fits the Russian culture very well. Combine this with some a lot of time and bright brains, and you got the perfect environment for "browser innovation." </p> <p>Sergey (Siarhei) mentioned these two websites to be his favorite sources of inspiration:<br /> http://habrahabr.ru/ (Каскадные Таблицы Стилей) http://chikuyonok.ru/<br /> </p> <p>So if you get get through the "azbuka" with your google translator, you might find some innovative ways to make your browser do things you did not know it was capable of.</p> jQuery Star Ratings Plugin http://davidpirek.com/blog/jquery-star-ratings-plugin Nov 30 2010 12:00AM <p>I needed a very simple jQuery Star Ratings Plugin, and what I found available out there just seemed too cluttered and complicated, so I came up with my own. The idea was to create something with the small amount of code possible, that would have two features: show rating, and allow for adding star rating to any type of form simply taking over a input filed</p> <p>So here is what I came up with in a few hours:</p> <pre> (function ($) { $.fn.stars = function (op) { var starHtml = "&lt;div&gt;&lt;ul class=\"stars clearfix\"&gt;&lt;li&gt;&lt;a href=\"#1\" class=\"star_1\" rating=\"1\"&gt;&lt;span class=\"selected\"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=\"#2\" class=\"star_2\" rating=\"2\"&gt;&lt;span class=\"selected\"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=\"#3\" class=\"star_3\" rating=\"3\"&gt;&lt;span class=\"selected\"&gt;&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=\"#4\" class=\"star_4\" rating=\"4\"&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=\"#5\" class=\"star_5\" rating=\"5\"&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;"; return this.each(function () { var wrap = $(this), html = $(starHtml), d = $(".dd", html), a = $("a", html), currentRating = wrap.attr("rating"), isReadOnly = true; if (typeof currentRating == "undefined") { currentRating = "0"; isReadOnly = false; } //rating html.addClass("stars_" + currentRating); //bind events if is not read only if (isReadOnly) { //return false on click a.click(function () { return false; }); //kills link default cursor a.css("cursor", "default"); } else { //add default rating (0) wrap.val(currentRating); //click a.click(function () { var _this = $(this), r = _this.attr("rating"); //add value wrap.val(r); //remove class var currentClass = html.attr("class"); html.removeClass(currentClass); //add current class html.addClass("stars_" + r); //update current currentRating = r; return false; }); //hover a.hover(function () { var _this = $(this), r = _this.attr("rating"); //add value wrap.val(r); //remove class var currentClass = html.attr("class"); html.removeClass(currentClass); //add current class html.addClass("hover_" + r); return false; }, function () { var currentClass = html.attr("class"); html.removeClass(currentClass); //add current class html.addClass("stars_" + currentRating); currentRating return false; }); } //put on page wrap.after(html).hide(); }); }; })(jQuery); </pre> <p>And this is how you can use this plugin:</p> <pre>&lt;p&gt;Display Stars&lt;/p&gt;<br /><br />&lt;span class="star_rating" rating="1"&gt;&lt;/span&gt;<br /><br />&lt;span class="star_rating" rating="2"&gt;&lt;/span&gt;<br /><br />&lt;span class="star_rating" rating="3"&gt;&lt;/span&gt;<br /><br />&lt;span class="star_rating" rating="4"&gt;&lt;/span&gt;<br /><br />&lt;span class="star_rating" rating="5"&gt;&lt;/span&gt;<br /><br />&lt;p&gt;Rate With Stars&lt;/p&gt;<br />&lt;input name="rating" value="0" class="star_rating" /&gt;</pre> <p>And here is how you can initiate it:</p> <pre> $(".star_rating").stars(); </pre> <p>It works great for blog post comments, or simple directories, as this is where I am using it</p> <p>You can download the source code of the star <a href="http://www.davidpirek.info/content/files/documents/7c66322f-5fdc-4ba8-88d9-efb344bdd0a2.zip">jquery plugin here</a>.</p> multiple ajax requests with one callback http://davidpirek.com/blog/multiple-ajax-requests-with-one-callback Nov 22 2010 12:00AM <p>I am messing around with some JavaScript MVC patterns, and one of the features I need is to be able to make several ajax request (in my case, for the view and the model) and have one final callback when the data comes back (in my case run the controller). So I came up with this nice utility function, build on the top of jQuery, that does that:</p> <pre> var ajaxMulti = function(requests, callback){ var sucessIndex = 1, numberOfRequests = requests.length - 1, data = {}; $.each(requests, function(i, d){ $.ajax({ type: d.type, url: d.url, dataType: d.dataType, success: function (d2) { data[d.url] = d2; if (sucessIndex > numberOfRequests) { callback(data); } else { sucessIndex++; } } }); }); }; //usage ajaxMulti([ { type: "Get", url: "ax/Util/getView/message.list", dataType: "html" }, { type: "Get", url: "zpravy/listIncomming", dataType: "json" } ], function(d){ console.log(d); }); </pre> <p>I am using the url as key, and what's nice about this is that it allows for different data formats for each request, which is what I need for my JavaScript MVC code.</p> JavaScript loop with closure http://davidpirek.com/blog/javascript-loop-with-closure Nov 19 2010 12:00AM <p>There is no block scope in JavaScript, so unlike in other languages, loops don't have scope. Fortunately, there is a really simple solution to that: just put a function inside of the loop that will create JavaScript functional scope:</p> <pre> for (var i=0; i<=numberOfItems; i++){ (function(i, d){ //do something with d }(i, d[i])); } </pre> <p>The more elegant way of course is to use the jQuery's $.each() function, which offers the "functional closure" by default. It can be used like this:</p> <pre> $.each(data, function(i, d){ //do something with d }); </pre> <p>Of course, we could make the first example to follow the same pattenr like the jQuery "each" function, so the code would look some like this:</p> <pre> var forEach = function(d, f){ var l = d.length, for (var i=0; i<=l; i++){ f(i, d[i]); } }; </pre> Test-Driven JavaScript Development book review http://davidpirek.com/blog/test-driven-javascript-development-book-review Nov 8 2010 12:00AM <div class="clearfix margin_bottom"> <img src="http://tddjs.com/images/ttdjs.png" class="left margin_right" /> <p>I have picked up a new book in Barnes & Noble today called Test-Driven JavaScript Development by Christian Johansen. Until a few years ago, most of the JavaScript books used to be really poor, but it looks like lately, as JS is coming to popularity, we are starting to see some good JavaScript books, and this is definitely one of them.</p> <p>TDD (test driven development) is usually a technique used by seasoned developers. And this seems to be the case here also. First time I have seen TDD done in JavaScript was from the YUI guys. Since several TDD framework have appeared on the JS scene. (http://docs.jquery.com/QUnit).</p> <p>Apart from simple to advanced TDD examples, Christian also digs in some complicated JS issues, so this reading becomes inspiring in other ways also. In fact, it almost seems like he likes to talk about really advanced JavaScript issues, and TDD is was just an way to get a book contract, so he wraps his examples into "tests" so that the book would still hold the topic. When I think about this, there is not really much to talk about in TDD, you have to use the right framework, and the just write tests, which are really too specific to talk about in a generic book, so instead the author sidetracks into advanced JavaScript programming issues like cross-browser JavaScript strategies and inheritance patters. Either way, this is great reading if you are still foggy on things like prototypal vs classical inheritance. </p> <p>I consider doing unit testing in JavaScript a sing of seniority, so TDD is something a guru UI developer should get involved if have not done so.</p> </div> JavaScript inheritance/extendanding with closures http://davidpirek.com/blog/javascript-inheritance-extendanding-with-closures Nov 8 2010 12:00AM <p>While browsing the Test-Driven JavaScript Development book, I have noticed a nice JavaScript pattern that I would like go over. I will call it extending with closure. The most basic way how to add a method to an existing class in JavaScript is to add it to the prototype:</p> <pre> Date.prototype.newDateMethod = function(param) { //do something return date; }; </pre> <p>But you can get much fancier than that wrapping the whole code into an anonymous closure, which allows you to have private methods inside. (well you could have private methods here to though, but still it looks like much nice way to organize the code.)</p> <pre> Date.prototype.newMethod = (function () { function doSomething() { //do something return date; } function doSomethingElse() { //do something return doSomething(); } return doSomethingElse; }()); </pre> extjs viewport jquery plugin http://davidpirek.com/blog/extjs-viewport-jquery-plugin Oct 28 2010 8:01PM <p>I am not a big fan of hacking the browser to behave like a windows app, but sometimes the business needs it so be it. ExtJs has done a great job with its Ext.Viewport class. I have found a nice jQuery plugin that does the same thing. Here is the <a href="http://layout.jquery-dev.net/demos/nested.html">demo</a>.</p> jqGrid.js author: Rumen Stankov http://davidpirek.com/blog/jqgridjs-author-rumen-stankov Oct 28 2010 12:00AM <p>The jqGrid.js plugin has been getting a lot of buzz recently, as Microsoft started to officially endorse jQuery, and large corporation have been looking for jQuery alternatives to their Telerik and Infragistics ASP.NET controls. So this is the guy behind jqGrid? He's name is Rumen Stankov and he lives in Bulgaria. I googled Rumen's name before, but not until today I read his <a href="http://www.linkedin.com/profile/view?id=13551617&authType=name&authToken=hjOM&locale=en_US&pvs=pp&pohelp=&trk=ppro_viewmore">linkedin profile</a>. He previously worked at Telerik and Infragistics (Telerik's competitor) and now he started his own company (Trirand Inc, main product being jqGrid) that competes with both Telerik (4 years) and Infragistics (2 years 2 months) :) that's what I call an entrepreneur :) </p> <p>I have a feeling that unless Telerik and Infragistics get their act together given the paradigm shift (web forms being dead) we will hear much more from this company.</p> <p>Lesson learned: don't outsource! I supposed Telerik and Infragistics wanted to save money by outsourcing to poor country like Bulgaria that has smart UI developers. Now out of this small country comes a startup that will compete with the big guys.</p> HTML helpers in the "jquery.tmpl.js view engine" http://davidpirek.com/blog/html-helpers-in-the-jquerytmpljs-view-engine Oct 27 2010 12:00AM <p>In my <a href="http://www.davidpirek.info/blog/javascript-mvc-view-engine-with-jquerytmpl-js">last post</a> I was showing how you can build a JavaScript MVC framework around the jquery.tmpl.js plugin. To finish this up, every good MVC framework needs HTML helpers. The jquery.tmpl.js plugin already supports calling functions from the template, but has a hook that encodes all HTML that come out of these functions. What I was hoping to do was to have a nice little helper that would create button based on the text and href params, something like this: </p> <pre> ... {{each pages}}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr class="cr_${id} alt"&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;a href="/${page_url}"&gt;${page_title}&lt;/a&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ${page_url}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ${page_status}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ${page_date_modified}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ${CMS.Helpers.button("edit", "#ajax/edit")}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {{/each}} ... </pre> <p>Obviously, the helper needs to be defined as a global object, so let's create something like this:</p> <pre> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; window.CMS = {<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Helpers: {<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; button: function (text, href) {<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "&lt;a href=\"" + href + "\" class=\"button\"&gt;" + text + "&lt;/a&gt;";<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }; </pre> <p>Now in order to make this work, I had to comment out the line where it HTML encodes all content returned by functions from the template:</p> <p>From this:</p> <pre> .... encode: function (text) { // Do HTML encoding replacing < > & and ' and " by corresponding entities. return ("" + text).split("<").join("&lt;").split(">").join("&gt;").split('"').join("&#34;").split("'").join("&#39;"); } .... </pre> <p>To this:</p> <pre> .... encode: function (text) { return text; } .... </pre> <p><strong>Update:</strong> so I figured out a proper way render HTML helpers using the "HTML property" in the markup and the "options" which let's you define private methods used within the template. It is not quite obvious it's possible at first, but it actually works out pretty sweet. So here is the magic:</p> <pre> (function ($) { //HTML helpers var CMS = { Helpers: { button: function (text, href) { return "&lt;a href=\"" + href + "\" class=\"button\"&gt;" + text + "&lt;/a&gt;"; } } }; var sucessIndex = 0, view, model, contentDiv = $("#content_div"), controler = function (m, v) { var tmplOptions = CMS, html = $.tmpl(v, m, tmplOptions), //merge page model with view content = $(html), //turn html into jQuery object links = $("a", content); //query this object (before it even touches DOM) links.click(function () { alert("You clicked me!"); return false; }); //put content on the page (in the DOM) contentDiv.html(content); }; //get view $.ajax({ type: "Get", url: "ajax/getView/test", dataType: "html", success: function (v) { view = v; //checks if model was loaded borefore running controler if (sucessIndex > 0) { controler(model, view); } else { sucessIndex++; } } }); //get model $.ajax({ type: "Get", url: "ajax/jPageModle", dataType: "json", success: function (m) { model = m; //checks if view was loaded borefore running controler if (sucessIndex > 0) { controler(model, view); } else { sucessIndex++; } } }); })(jQuery); </pre> <p>... and the template would now look something like this:</p> <pre> ... <td> {{html $item.Helpers.button("edit", "#ajax/edit")}} </td> ... </pre> <p>So another great benefit of this approach is that we can fully avoid using global variables. Also like the fact that the "options" object can go "deep" meaning, you can "import" a full namespace of functions (as I am doing by putting the "helpers" in the "Helpers" namespace, that can be referenced in the view. This is really powerful!</p> <p>I love this new paradigm, and if the browser can handle it, I suppose the only thing left on the wish list is that Boris would together with is Scott Guthrie from the MVC3 and base ASP.NET MVC3 Razor and jquery.tmpl.js on the same syntax. That would really kick ass!</p> javascript MVC view engine with jquery.tmpl.js http://davidpirek.com/blog/javascript-mvc-view-engine-with-jquerytmpl-js Oct 26 2010 12:00AM <p>I have been looking on JavaScript MVC frameworks laterally and was able to ceate a demo build on the <a href="http://github.com/nje/jquery-tmpl">query.tmpl.js jQuery plugin</a> developed by <a href="http://www.borismoore.com/">Boris Moore</a> (also see John Resig the author of jQuery being involved in this project). My goal was to mimic full MVC paradigm where JSON would be the model, and view could be separated into an .HTML file, so that when editing, I would get highlighting, and editing it would be user friendly. In this demo, I am using ASP.NET MVC3 and VS 2010, but this paradigm could be used with any type of backend.</p> <p>First the view: in a separated file, called test.html, I wrote this markup representing the page view (since this content is to be loaded via ajax, into an "ajax div" there is not header or footer, since these are already on the page):</p> <pre>&lt;div class="hr"&gt;<br />&lt;h1 class="icon_page_l"&gt;<br />&nbsp;&nbsp;&nbsp; ${page_title} &lt;span&gt;Test&lt;/span&gt;<br />&lt;/h1&gt;<br />&lt;/div&gt;<br /><br />&lt;table class="data_table width_full"&gt;<br />&nbsp;&nbsp;&nbsp; &lt;tbody&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;th&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Title<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/th&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;th&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Url<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/th&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;th&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Status<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/th&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;th&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Date Modified<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/th&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;th&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/th&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {{each pages}}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr class="cr_${id} alt"&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;a href="/${page_url}"&gt;${page_title}&lt;/a&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ${page_url}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ${page_status}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ${page_date_modified}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;a class="ajax_action button right" href="#ajax/editPage/${id}/"&gt;Edit&lt;/a&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;a class="ajax_post_action button margin_right right"<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; href="#ajax/deletePage/${id}/"&gt;Delete&lt;/a&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {{/each}}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tbody&gt;<br />&lt;/table&gt; </pre> <p>This is what I have in the backed ASP.NET MVC, pretty much a mapped string method that will load my HTML file, and minify it (put all in one line) on the fly. Nothing too fancy, but it just allows me to have my "views" nicely formated:</p> <pre> public string getView(string p1, string p2, string p3) { string jsView = "~/Content/js/Views/" + p1 + ".html"; return CMS.Util.Ajax.minifyJs(CMS.Util.Ajax.LoadStaticFile(jsView)); } </pre> <p>Next, I create my model, which is ASP.NET MVC JSON result. The .NET MVC proved to be really powerful here, with few lines of code I can create wrapper object with two child objects each representing a DB table. </p> <pre> public JsonResult jPageModle(string p1, string p2, string p3) { //data context var dataContext = new linqDataContext(); //query zones var zones = from m in dataContext.zones select m; //query pages var pages = from m in dataContext.pages select m; //create page model var pageModel = new { page_title = "Hello World", pages = pages, zones = zones }; return Json(pageModel, JsonRequestBehavior.AllowGet); } </pre> <p>A couple notes here, in ASP.NET MVC3 I believe, by default, JSON results will need to be POSTS, for some security reasons. Well I am kind of purist here, so I am setting it to get.</p> <p>This is what comes out of the JSON result as our model:</p> <pre> { "page_title": "Hello World", "pages": [{ "id": 6, "page_url": "index", "page_html": "", "page_author": "dpirek@gmail.com", "page_settings": null, "page_title": "účetnictví on-line", "page_meta_description": "", "page_meta_keywords": "", "page_status": 1, "page_date_modified": "\/Date(1287169574310)\/" }, { "id": 10, "page_url": "about", "page_html": "", "page_author": "", "page_settings": null, "page_title": "About Page", "page_meta_description": "", "page_meta_keywords": "", "page_status": 1, "page_date_modified": "\/Date(1276185200060)\/" }, { "id": 31, "page_url": "test", "page_html": "test", "page_author": "", "page_settings": null, "page_title": "ted a asf asf asf", "page_meta_description": "", "page_meta_keywords": "", "page_status": 1, "page_date_modified": "\/Date(1287422731090)\/" }] } </pre> <p>And now comes the controller: (before you run this, make sure you have the references to the latest jQuery and the jquery.tmpl.js plugin. </p> <pre> (function ($) { var sucessIndex = 0, view, model, contentDiv = $("#content_div"), controler = function (m, v) { var html = $.tmpl(v, m), //merge page model with view content = $(html), //turn html into jQuery object links = $("a", content); //query this object (before it even touches DOM) links.click(function () { alert("You clicked me!"); return false; }); //put content on the page (in the DOM) contentDiv.html(content); }; //get view $.ajax({ type: "Get", url: "ajax/getView/test", dataType: "html", success: function (v) { view = v; //checks if model was loaded borefore running controler if (sucessIndex > 0) { controler(model, view); } else { sucessIndex++; } } }); //get model $.ajax({ type: "Get", url: "ajax/jPageModle", dataType: "json", success: function (m) { model = m; //checks if view was loaded borefore running controler if (sucessIndex > 0) { controler(model, view); } else { sucessIndex++; } } }); })(jQuery); </pre> <p>A few notes here:</p> <p>One of the dilemmas was how to make two ajax calls (one for view, one for model) and make sure the controller runs only after both of them are loaded. I originally made the calls nested with was really ugly from performance point of view, so I created "sucessIndex" which basically a number which keeps track on successful ajax calls, and after each new successful ajax call this index is checked if it reached high enough, so that we have all we need and can run our controller.</p> <p>Another nice feature that I am showing here is how you can bind element events before the content ever reaches the DOM: Creating jQuery object out of HTML string: $(html) which can then be queried inside, and added events. This pattern is commonly used inside of jQuery UI where a lot of HTML flies around, and is the most efficient way to create larger chunks of HTML code with events attached to it.</p> how to destroy your competitors SEO traffic http://davidpirek.com/blog/how-to-destroy-your-competitors-search-traffic Oct 22 2010 12:00AM <p>I have learned the most important lesson about SEO the hard way: if you site gets hacked through malicious comment script, google will first black list you, and then trop your page rank. I don't know if this was done as an intended attack to my site, or by some robot, but I can see the 80% drop in traffic, even after the site was cleared with google as a clean site, and since google does not care, I think this might be a nice way to destroy your competitor's search rankings. So now it looks security matters after all.</p> Amazon ec2 Windows server pricing http://davidpirek.com/blog/amazon-ec2-windows-pricing Oct 22 2010 12:00AM <p>The best hosting ever: Amazon ec2</p> <p>$0.013/hour x 24 hours x 30 days = <strong>$9.36</strong> + pre-paid year $54/12 = $4.50.</p> <p>total: <strong>$13.86/month</strong></p> <p>This pricing is for the micro instance which is big enough to host a couple dozens of smaller websites. The cheapest virtual dedicated server from godaddy.com will run you $27/month with the prepaid year and you don't get all the other cool features like backup, cloud features.... </p> <p>I am really hooked. Why would anybody use any other hosting. ec2 is good cheap for a beginner, and will scale forever... For those of you who are little scared of having your own server: I was too, and it takes a few sleepless nights to figure things out, but the control you get is definitely worth it, and hey, not you are a hosting company, so you can host for one or two of your clients, and your ec2 server is paid for :).</p> google SMTP server C# class http://davidpirek.com/blog/google-smtp-server-c-class Oct 22 2010 12:00AM <p>As I am moving many of my websites onto my own amazon ec2 servers, one of the issues I am facing is how to send emails from my ASP.NET apps. I spend quite some time trying to configure the Windows Server 2008 SMTP servers (not an easy job) and then learned that actually sending mail from the Amazon IP addresses will get red flag as bad email and most likely get caught in spam filters. So here is what you can do: use googles (gmail) SMTP server, link it to you domain's email address through the gmail process, and then use the following C# class to send your mail:</p> <pre> using System; using System.Net.Mail; using System.Net; namespace KS.Util { public class postMaster { //address public string emailTo; public string emailFrom; //message public string subject; public string message; public bool isHtml = false; //login public string login; public string password; public string smtpServer; //system public bool isSuccessful = false; public string errorMessage = "sucess"; public void send() { try { NetworkCredential loginInfo = new NetworkCredential(login, password); MailMessage msg = new MailMessage(); msg.From = new MailAddress(emailFrom); msg.To.Add(new MailAddress(emailTo)); msg.Subject = subject; msg.Body = message; msg.IsBodyHtml = isHtml; SmtpClient client = new SmtpClient(smtpServer); client.EnableSsl = true; client.UseDefaultCredentials = false; client.Credentials = loginInfo; client.Port = 587; client.EnableSsl = true; client.Send(msg); isSuccessful = true; } catch (Exception ex) { errorMessage = ex.Message.ToString(); } } } } </pre> <p>Here is how you use it:</p> <pre> NS.Util.postMaster pm = New NS.Util.postMaster() pm.emailTo = strName + " <" + strEmail + ">"; pm.emailFrom = "post master <admin@YourDomain.com>"; pm.subject = EmialSubject; pm.message = EmialBody; pm.isHtml = True; pm.login = "YourEmail@gmail.com"; pm.password = "password"; pm.smtpServer = "smtp.gmail.com"; pm.send(); </pre> asp.net mvc 3 deployment to windows server 2008 http://davidpirek.com/blog/aspnet-mvc-3-deployment-to-windows-server-2008 Oct 16 2010 12:00AM <p>Just spend a few sleepless nights trying to deploy ASP.NET MVC 3 on my Amazon ec2 claud server. First, I am not a server guy so some of the points I will mention might be no brainer to most of you, but still, I whish there was at least more stuff on the web, explaining the process... </p> <p>I am deploying ASP.NET MVC 3 on WIndows 2008, which is running IIS7.</p> <ul class="list"> <li>Step one, upgrade to ASP.NET 4.0</li> <li>Set the application pool in IIS to use ASP.NET 4.0</li> <li>Copy the ASP.NET MVC 3 dll (System.Web.MVC) (version 3!) into the dll folder</li> <li>Donload AspNetWebPages.msi and install on your machine, find the folder it installed in the program files, and copy the following assemblies to the bin folder of your website: System.Web.Razor.dll, System.Web.Routing.dll, System.Web.Webpages.dll, and WebMatrix.Data.dll</li> </ul> <p>This article will probably be absolete once ASP.NET MVC is officially released, but for those of you like me that really want to start using Razor, hope this provides some guidance</p> mvc 3 encoded html in Razor: get rid of it http://davidpirek.com/blog/mvc-3-encoded-html-razor-get-rid-of-it Oct 13 2010 12:00AM <p>I have just gone through some conversion pains in merging my code from ASP MVC1 to ASP.NET MVC3 using the Razor view engine. Here is the biggest one. All of my helpers started to be HTML encoded and for hours I could not figure out how to render HTML from the server at all. I was also rendering some HTML from the database, which was also getting encoded. What was wired was that this was especially happening when using the Razor view engine, which I otherwise love... </p> <p>So this is the solution, in MVC3 there is a new type for HTML helpers called MvcHtmlString which should be used rather than just string, which gets encoded by default. So your helpers should always look like this:</p> <pre> public static MvcHtmlString doSomething(this HtmlHelper htmlHelper, string html) { //do something with the HTML return MvcHtmlString.Create(html); } </pre> <p>In addition, you have some HTML coming from another source like a database, you have to wrap your Razor tag in the MvcHtmlString.Create function like this:</p> <pre> @MvcHtmlString.Create(View.pageHtml) </pre> <p>Hope this helped :)</p> mugtug.com darkroom code review http://davidpirek.com/blog/mugtugcom-darkroom-code-review Oct 4 2010 12:00AM <p>I am looking at http://mugtug.com/darkroom in firebug and it's one big canvas element. Pretty impressive! Kind of what bespin did with the creating a code editor, these guys did with creating web based Photoshop.</p> <p>Give the complexity I suspect there is a nice "HTML5 canvas javascript library" and I am right. Everything is custom code from these guys, and it is actually nicely written, at least at first glance. Looking at the global object, it is pretty pouted so I suspect, there are some layers of good code and bad code, somebody did not have time minify the JS files yet, which is a actually good for us, because we can see what's going on better. </p> <p>As for utilizing third party code, I see references to Jquery, Douglas Crockford's JSON stringifier, and Canvas2D.js, Javascript Library, that helps you to work with canvas.</p> <p>To my disappointment, the performance seems to be not quite there yet. This has nothing to do with the code, but the browser is perhaps not ready yet to do this type of image processing. When I uploaded a little large image, and tried to do some color adjustments, I had to wait seconds till something happened, freezing the screen rather than getting a wait sign.</p> <p>Anyway, this app proves that the future for HTML5 looks pretty exciting. In the code I even see some signs that "tough events" are supported, opening a whole new market for rich internet tablet apps.</p> amazon ec2 review for ASP.NET MVC http://davidpirek.com/blog/amazon-ec2-review-for-aspnet-mvc Oct 1 2010 12:00AM <p>Before I start, I want to say that I am not a server guy, although recently I have been becoming one with some much infrastructure I needed to build...</p> <p>Anyway, I cannot but rave at Amazon's EC2!. I am using it to host ASP.NET MVC sites and it works great, and the price is a joke for the micro instances, that are more than enough for hosting at least a dozen small websites.</p> <p>I have been looking at the service 10 months back, but they did not support WIndows Server 2008 which is needed to host ASP.NET MVC, but now they have it, so I was finally able to test it out. I already have one Windows 2008 virtual dedicated server in Europe, which costs me about $20 a month, which I thought was cheap because this usually runs you at least $40 a month. Anyway, what I got running as a cloud instance has more ram (600 mag) and is fast as hell.</p> <p>Issues: Here were were my hiccups when setting up: I pretty much used <a href="http://blogs.iis.net/bills/archive/2009/01/13/how-to-run-windows-amp-iis-in-the-cloud-on-amazon-ec2-in-15-mins.aspx">this article</a> as a reference for setup. For some reason, I had issues getting the password for the server for a while, so I ended up creating more than one instances before I got through. The next "mistake" I made was not reading up on the "security groups" so as a default, it set access to the server only from my home IP address, even for the HTTP connection, so I moved this website there, looked at it and it ran fine, but when I looked at it from work it was down, so I thought something is wrong with the server which took me a day or two to realize track that this was actually because of the security group feature, which by the way is awesome!</p> <p>Customer service: you have to pay for but you get what you pay for: the best customer service ever! Nothing like you typical hosting company. They answer right away, give you very lengthy explanation, analyze the whole situation. I in fact bought the customer service, got my issue resolved and canceled it to save money :) so you can make even customer service "on demand" and "scalable" :)</p> <p>Backup: I was trying to get my other hosting provider in europe to backup my whole instance, and they said that this is something they are working on and will probably cost a lot of money. Again, not being a server guy, I have spend many sleepless nights configuring that server, and the ideal that it could just be hacked and all lost was my nightmare. EC2 backup is like a dream come true. It takes like 10 seconds and you can relaunch as another instance in another country right away. I mean this is really awesome</p> <p>In conclusion I just don't know how another hosting company can still stay in business. Yes there is little learning curve, but if a UI guy can figure out how to use this, then anybody can. They made the server stuff so easy to use that I think I am starting to like it :) Anybody needs help setting up their EC2 server? :)</p> html beautifier jquery plugin http://davidpirek.com/blog/html-beautifier-jquery-plugin Sep 30 2010 5:43AM <p>I was in a need for an HTML beautifier, so I was searching around, but did not find one that came in a jQuery plugin package, so I used <a href="http://tools.arantius.com/tabifier">some script</a> I found on the net, and made one. This is how you can use it:</p> <pre> $("#Textarea_id").htmlBeautifier ("<p>&lt;div&gt; some text &lt;/div&gt;</p>"); </pre> <p>Or you can pre-process an HTML that comes from the server in the JSON as a string, let's say when you want to put it in a textarea and edit it.</p> <pre> $("#Textarea_id").htmlBeautifier (myJson.html); </pre> <p>Finally, here is the plugin:</p> <pre> (function($){ var level=0, lSize=100, finishTabifier = function(code, targetElm) { code=code.replace(/\n\s*\n/g, '\n'); //blank lines code=code.replace(/^[\s\n]*/, ''); //leading space code=code.replace(/[\s\n]*$/, ''); //trailing space targetElm.val(code); level=0; }, cleanHTML = function(code, targetElm) { var i=0; function cleanAsync() { var iStart=i; for (; i<code.length && i<iStart+lSize; i++) { point=i; //if no more tags, copy and exit if (-1==code.substr(i).indexOf('<')) { out+=code.substr(i); finishTabifier(out, targetElm); return; } //copy verbatim until a tag while ('<'!=code.charAt(point)) point++; if (i!=point) { cont=code.substr(i, point-i); if (!cont.match(/^\s+$/)) { if ('\n'==out.charAt(out.length-1)) { out+=tabs(); } else if ('\n'==cont.charAt(0)) { out+='\n'+tabs(); cont=cont.replace(/^\s+/, ''); } cont=cont.replace(/\s+/g, ' '); out+=cont; } if (cont.match(/\n/)) { out+='\n'+tabs(); } } start=point; //find the end of the tag while ('>'!=code.charAt(point)) point++; tag=code.substr(start, point-start); i=point; //if this is a special tag, deal with it! if ('!--'==tag.substr(1,3)) { if (!tag.match(/--$/)) { while ('-->'!=code.substr(point, 3)) point++; point+=2; tag=code.substr(start, point-start); i=point; } if ('\n'!=out.charAt(out.length-1)) out+='\n'; out+=tabs(); out+=tag+'>\n'; } else if ('!'==tag[1]) { out=placeTag(tag+'>', out); } else if ('?'==tag[1]) { out+=tag+'>\n'; } else if (t=tag.match(/^<(script|style)/i)) { t[1]=t[1].toLowerCase(); tag=cleanTag(tag); out=placeTag(tag, out); end=String(code.substr(i+1)).toLowerCase().indexOf('</'+t[1]); if (end) { cont=code.substr(i+1, end); i+=end; out+=cont; } } else { tag=cleanTag(tag); out=placeTag(tag, out); } } if (i<code.length) { setTimeout(cleanAsync, 0); } else { finishTabifier(out, targetElm); } } var point=0, start=null, end=null, tag='', out='', cont=''; cleanAsync(); }, tabs = function() { var s=''; for (var j=0; j<level; j++) s+='\t'; return s; }, cleanTag = function(tag) { var tagout=''; tag=tag.replace(/\n/g, ' '); //remove newlines tag=tag.replace(/[\s]{2,}/g, ' '); //collapse whitespace tag=tag.split(' '); for (var j=0; j<tag.length; j++) { if (-1==tag[j].indexOf('=')) { //if this part doesn't have an equal sign, just lowercase it and copy it tagout+=tag[j].toLowerCase()+' '; } else { //otherwise lowercase the left part and... var k=tag[j].indexOf('='); var tmp=[tag[j].substr(0, k), tag[j].substr(k+1)]; tagout+=tmp[0].toLowerCase()+'='; var x=tmp[1].charAt(0); if ("'"==x || '"'==x) { //if the right part starts with a quote, find the rest of its parts tagout+=tmp[1]; while(x!=String(tag[j]).charAt(String(tag[j]).length-1)) { tagout+=' '+tag[++j]; } tagout+=' '; } else { //otherwise put quotes around it tagout+="'"+tmp[1]+"' "; } } } tag=tagout.replace(/\s*$/, '>'); return tag; }, ownLine=['area', 'body', 'head', 'hr', 'i?frame', 'link', 'meta', 'noscript', 'style', 'table', 'tbody', 'thead', 'tfoot'], contOwnLine=['li', 'dt', 'dt', 'h[1-6]', 'option', 'script'], lineBefore = new RegExp( '^<(/?'+ownLine.join('|/?')+'|'+contOwnLine.join('|')+')[ >]' ), lineAfter=new RegExp('^<(br|/?'+ownLine.join('|/?')+'|/'+contOwnLine.join('|/')+')[ >]'), newLevel=['blockquote', 'div', 'dl', 'fieldset', 'form', 'frameset','map', 'ol', 'p', 'pre', 'select', 'td', 'th', 'tr', 'ul'], newLevel=new RegExp('^</?('+newLevel.join('|')+')[ >]'), placeTag = function(tag, out) { var nl=tag.match(newLevel); if (tag.match(lineBefore) || nl) { out=out.replace(/\s*$/, ''); out+="\n"; } if (nl && '/'==tag.charAt(1)) level--; if ('\n'==out.charAt(out.length-1)) out+=tabs(); if (nl && '/'!=tag.charAt(1)) level++; out+=tag; if (tag.match(lineAfter) || tag.match(newLevel)) { out=out.replace(/ *$/, ''); out+="\n"; } return out; }; // plugin definition $.fn.htmlBeautifier = function(code, settings) { // iterate and reformat each matched element return this.each(function() { cleanHTML(code, $(this)); }); }; })(jQuery); </pre> new.evite.com JavaScript code review http://davidpirek.com/blog/newevitecom-javascript-code-review Sep 30 2010 5:42AM <p>eveite.com despite its prominence was a for a long time behind its competitors in areas like design, usability, and UI performance. Not any more! the new site is mind blowing in many aspects. The design is great, the way it uses CSS and progressive enhancement is great, and what's the most amazing part, the JavaScript part is setting a new bar for all the competitors. Single page Ajax JavaScript app build on the top of Jquery, this is the state of the art. I am not aware of any other company in this space who does RIA JavaScript apps, so this is really a leap forward!</p> <p>So let's take a look under the hood. Most of the JS is in main.js which is minified and little hard to read, but the basic patterns are obvious. Nothing too stunning but the code is decent. It has an anonymous function as a wrapper, the code uses comma separated multiple local var definition. The code structure is little flat, so not much closure management, but at least it does not spill out to the global object.</p> JavaScript Inheritance vs delegation http://davidpirek.com/blog/javascript-inheritance-vs-delegation Sep 30 2010 12:00AM <p>It is commonly asked question these days in the JavaScript community: "How to structure code in a large JavaScript application?" Specifically, how to do you write code base that can draw from reusable components.</p> <p>In "classical" languages, this is done through Inheritance and sub classing. You have a base class, that inherits from its super class. Since JavaScript has become popular in recent years, and being a very powerful language, this pattern has been forced event on JavaScript, creating features that are not originally there. Frameworks like ExtJS (now sencha) or dojo are an example of such structures.</p> <p>JavaScript being a prototypal language, has it's own inheritance model: delegation. Here is a <a href="http://en.wikipedia.org/wiki/Delegation_(programming)">Wikipedia description of delegation</a> and by the end of the article you see: "Delegation is a fundamental technique used in languages of prototype-based programming (such as JavaScript)."</p> <p>One of the patterns that uses delegation is called Strategy pattern: a pattern whereby algorithms can be selected at runtime. Here is an example of JavaScript strategy pattern (taken from <a href="http://en.wikipedia.org/wiki/Strategy_pattern">Wikipedia</a>) </p> <pre> var Button = function(submit_func, label) { this.label = label; this.on_submit = function(numbers) { return submit_func(numbers); }; }; var numbers = [1,2,3,4,5,6,7,8,9]; var sum = function(n) { var sum = 0; for ( var a in n ) { sum = sum + n[a]; } return sum; }; var a = new Button(sum, "Add numbers"); var b = new Button(function(numbers) { return numbers.join(','); }, "Print numbers"); a.on_submit(numbers); b.on_submit(numbers); </pre> <p>I must admit I came to the discover of delegation backwards: I have been writing very complex frameworks, and never quite had a use for classical inheritance. At the same time working with ExtJs, and seeing how much unnecessary overhead classical inheritance brings to the JavaScript, I never left quite right using it anyway. Finally, reading more on this topic, I realized that in fact delegation is the intended way of structuring your code in prototype-based programming languages. Just as a side note, prototype-based programming languages are newer (more modern) than classical languages, and I believe more powerful.</p> stopbadware.org blocks my site http://davidpirek.com/blog/stopbadwareorg-blocks-my-site Sep 23 2010 12:00AM <p>So my site got attacked by some kind of smart comment SQL injection that put script tags into my comments which is not that big of a deal. What's the most scrary thing about it however how it got blacklisted by google, and since all the "modern" browsers talk to this google blacklist API, nobody can actually access the site, instead gets a big red box "this site is dangerous!" While I understand the good intention, the fact that that google can practically erase you from the internet is little scary. So now I am forced to write this post via a good old IE browser. </p> <p>It looks like I am not the first blog this has happened to. <a href="http://www.michelfortin.com/google-blocks-my-blog-and-warns-visitors/">This guy</a> had a similar problem, so I just hope it will not hurt my search positions, and I will not loose to much business over it.</a> single page javascript ajax/RIA application http://davidpirek.com/blog/single-page-javascript-ajaxria-application Sep 21 2010 12:00AM <p>It looks like single page javascript ajax/RIA application is finally (6 years after google showed the world gmail) l becoming a buzz word, so I wanted to again over the performance benefits of single (RIA) apps: </p> <ul class="list"> <li> An average "weight" of a page of a typical web app is 500kb, an single page app pages in my framework are 2.5k! That's 200 times smaller. </li> <li>Event an optimized page using css sprites will have dozens of HTTP requests, while a single page app will ideally have just one (plus some new images if present)</li> <li>Since now you are hitting the server much less, like 200 times, you actually save a lot of server resources...</li> </ul> <p>I understand that the old school web world is scared of JavaScript development, JS is here to stay, so get used to it!</p> how to hack priceline.com with JavaScript http://davidpirek.com/blog/how-to-hack-pricelinecom-with-javascript Sep 16 2010 12:00AM <p>I love priceline.com. I am a deal maker and love to get a discount for little extra work. The problem is that recently that "extra work" has become a lot of work. I have gotten as much as 50% off deals on "name our price" offers, so I know there are great deals.</p> <p>So when you name your price, it tells you what the average value is and you know that the computer has a specific number that it will accept. The way the system is designed is to discourage from increasing your offer by $5 until you hit the minimal acceptable price. So the way I use to do it, is clear all my browser cookies and start offer again. What a pain to fill out all the payment forms again so that I can make a new fresh offer. Recently after I spent an hour do this, I said enough, and used my JavaScript skills to automate the process a little bit. The idea is that you will have a script that will fill all the forms for you, so that when you clear your cookies to full the server that you are a new customer, you don't have to spend 5 minutes re-typing all the payment and contact info.</p> <p>This is what you need: Firefox + Firebug plugin, and the following scripts</p> <p>Form page one:</p> <pre> (function(){ var $ = function(e){ return document.getElementById(e); }, val = function(e, v){ $(e).value = v; }, gVal = function(o){ var i=0, l = o.length; for (i=0; i<=(l-1); i++){ val(o[i].id, o[i].value); } }, data = [ { id: "offer/@Price", value: "150" }, { id: "offer/hotels/reserve_name[0]/@first_name", value: "David" }, { id: "offer/hotels/reserve_name[0]/@last_name", value: "Pirek" } ]; gVal(data); })(); </pre> <p>The second page only has initials so you can do it manually, and for the last page use this:</p> <pre> (function(){ var $ = function(e){ return document.getElementById(e); }, val = function(e, v){ try{ $(e).value = v; } catch(err){ console.log("cond not set" + v); } }, gVal = function(o){ var i=0, l = o.length; for (i=0; i<=(l-1); i++){ try{ val(o[i].id, o[i].value); } catch(err){ console.log("cond not find" + o[i].id); } } }, data = [ { id: "offer/customer/address[0]/CONTACT_FIRST_NAME", value: "David" }, { id: "offer/customer/address[0]/CONTACT_LAST_NAME", value: "Pirek" }, { id: "offer/customer/address[0]/ADDRESS_LINE1", value: "My Street" }, { id: "offer/customer/address[0]/city", value: "New York" }, { id: "offer/customer/address[0]/province_code", value: "NY" }, { id: "offer/customer/address[0]/postal_code", value: "00000" }, { id: "offer/customer/phone[0]/PHONE_NUMBER", value: "732123123" }, { id: "offer/customer/Email_Address", value: "name@gmail.com" }, { id: "Scratch/Email_Address_Confirm", value: "name@gmail.com" }, { id: "Scratch/CC_Type", value: "AX" }, { id: "offer/customer/creditcard[0]/CREDIT_CARD_NUM", value: "0000000000000" }, { id: "offer/customer/creditcard[0]/EXPIRATION_MONTH", value: "10" }, { id: "offer/customer/creditcard[0]/EXPIRATION_YEAR", value: "2012" }, { id: "Offer/Payment/Security/SECURITY_CODE", value: "0000" } ]; gVal(data); })(); </pre> <p>So this is what you do, open your Firebug (click on the firebug icon in the right bottom corner), click on the "console" tab and paste the JavaScript in the right box, then click "Run" and you should see the form being filled out. So now with a few "paste/copy" + clear cookies, you can name your prices faster than ever, and thus hit the "acceptance" threshold much closer, than ever before.</p> <p>Disclaimer: this post is not about hot to hack priceline.com but rather how to automate the bidding process, and therefore become a better price negotiator.</p> html5 video player tutorial http://davidpirek.com/blog/html5-video-player-tutorial Sep 15 2010 12:00AM <p>Despite of some shortcomings html5 video player seems to be a reality on the web. The idea of having just a simple HTML tag representing video sounds tempting, however the reality is that you will have to write special code to cater to different browser, and probably even encoding your video in different formats, in order to get the video tag working. </p> <p>What interests me is how JavaScript can make the video experience better competing with what the Flash players can do. <a href="http://ghinda.net/">Cristian Colceriu</a> (opera website) has published a nice <a href="http://dev.opera.com/articles/view/custom-html5-video-player-with-css3-and-jquery/">jQuery tutorial that shows how to add more controls to an HTML5 player</a>.</p> <p>This is how you can control the video tag using JavaScript: select the tag as an element, automatically have the video control methods</p> <pre> var videoElm = $("video"); videoElm.paly(); </pre> <p>So once you know how to do this, you can use your typical jQuery click biding magic to create any control you want.</p> Mozilla's Bespin Source code under the hood http://davidpirek.com/blog/mozillas-bespin-source-code-under-the-hood Sep 13 2010 12:00AM <p>I am considering Mozilla's Bespin as the pinnacle of JavaScript engineering, so I decided to read through the source code to get some new ideas for JavaScript patterns. Bespin runs as a HTML5 canvas tag, therefore all you see is pixel drawn, all your mouse events are x/y detected and matched to the "drawn" objects inside of the canvas element. What's amazing is how fast this thing runs. The paradox is that canvas was not really intended to be a text input element, yet it performs better than current DOM based UIs. Looking through the code, here are some of my findings:</p> <ul class="list"> <li>There seems to be a strong connecting between <a href="http://www.sproutcore.com">sproutcore</a> and bespin, much of the code uses sproutcore code, and the code is structured using sproutcore implementation of commonjs.</li> <li>all the code is wrapped arround <a href="http://www.commonjs.org">commonjs</a> SproutCore 1.1/Tiki implementation</li> <li>the code also has jQuery and sizzle included (not sure why, since I don't see it used later, and most websites will have it cashed as a separate file</li> <li>there are many browser hacks (for some reason I believed for a while that HTML5 would bring the end of hacking)</li> <li>there are many other "borrowed" code snippets from other frameworks (DOJO)</li> <li>as a result of using several third party framework, some of the utility functions overlap (not a big deal)</li> <li>on-demand scrip loading is widely used</li> </ul> sproutcore javascript library review http://davidpirek.com/blog/sproutcore-javascript-library-review Sep 13 2010 12:00AM <p>sproutcore seems to be a new kid on the JavaScript framework block with lots of ambitions. Unlike ExtJs that tries to mimic Microsoft type of complexity in it's implementation and richness of widgets, sproutcore follows the apple philosophy. As I mentioned in my previous post, there seems to be some connection betweek sproutcore and Mozilla, which really puts this framework on the map, and somehow gives is a promising future.</p> <p>It's no secret that a lot of the developers are from/in apple. The two guys that oversee this open source framework work at http://www.eloqua.com a Vienna, VA based company.</p> <p>I would live to actually start playing with this, but sproutcore is really married to Apple, so unless you are running Mac OSX, you are out of luck.</p> <p>Also, would like to mention, that sproutcore is being marketed as the HTML5 JavaScript framework, which more of a buzzword... I have not seen seen a radically different HTML5 like approach compared to other frameworks.</p> html5 canvas clickable elements tutorial http://davidpirek.com/blog/html5-canvas-clickable-elements-tutorial Sep 10 2010 12:00AM <p>I have been playing with HTML5 canvas elements creating some jQuery charting plug-ins, and one thing I was missing was to create "element like" objects (also in flash) that I could create, move around, assign events to atc.. So here is a little function that I wrote to compliment my drawRectangle function, now being able to draw "object-like" rectangles while singing events to them:</p> <pre> var createClickMap = function(canvas, x, y, w, h, f){ var m = function(x, y, w, h, mx, my){ var xEnd = (x+w); yEnd = (y+h); //x params if(mx > x && mx < xEnd){ //y params if(my > y && my < yEnd){ return true; } } return false; }; $(canvas).click(function(v){ var mX = v.pageX - this.offsetLeft, mY = v.pageY - this.offsetTop; if(m(x, y, w, h, mX, mY)){ f(); } }); } var drawRectangle = function(context, x, y, w, h, f) { //draw shape context.beginPath(); context.rect(x, y, w, h); context.closePath(); context.fill(); //add event to a rectangle if(typeof f == "function"){ createClickMap (canvas, x, y, w, h, f); } } </pre> <p>This mechanism could not be used for hovers for now, since this tracks the click of the canvas element, and the position within. I thinking about writing a little canvas toolkit that would somehow mimic the way we are used to coding in the DOM or flash, so I suppose, this function would be first building block... </p> html5 canvas vs svg http://davidpirek.com/blog/html5-canvas-vs-svg Sep 3 2010 12:00AM <p>Much has been written and said about the differences of HTML5 canvas and SVG, so I will not repeat all the categorization, but after working with these two new APIs I would like to offer some of my early findings. These findings are in the context to find a good HTML5 charting framework/solution, for one of my new <a href="http://www.lizinka.cz">financial websites</a>.</p> <p>SVG just seem more out of control, than canvas. I left like I was getting more consistency with canvas.</p> <p>With SVG doing onclicks seemed much easier, where with canvas, x/y tracking was needed to determine where the user was clicking.</p> <p>To my surprise, canvas did not deliver the same rendering across browsers when using fonts, so I was getting some inconsistent overlapping... oh I just had a glimpse of faith for a while that cross-browser tweaks would be gone in the HTML5 decade, but looks like UI developers will still have a job.</p> <p>Scripting canvas is still very raw experience. There is no "object-like" approach you find in flash, which makes doing anything just a lot of work. Since SVG uses tags, I get a feeling that the "object oriented" nature might be there, however I have not seen anything comprehensive yet.</p> HTML5 canvas pie chart jQuery plug-in http://davidpirek.com/blog/html5-canvas-pie-chart-jquery-plug-in Sep 2 2010 12:00AM <p>I have been googeling around looking for some cool HTML5 charting solution, and ran across this <a href="http://www.elated.com/articles/snazzy-animated-pie-chart-html5-jquery/">tutorial</a>. First, I started to taking the code apart to learn little bit from it, and then I wanted to use it in one of my projects, but realized that it's not really written as a plug-in and can be fed JSON data, so I spend a couple of days rewriting it, as into a jQuery plug-in where the data is provided in the JSON format, rather than grabbed from an HTML table, so here is what I came up with:</p> <pre> (function($){ //get total value var getTotal = function(x){ var t = 0; $.each(x, function(i, d) { t += parseFloat(d.value); }); return t; }; $.fn.pieChart = function(data, settings) { // Config settings var defaults = { chartSizePercent:55, sliceBorderWidth:1, sliceBorderStyle:"#fff", sliceGradientColour:"#ddd", maxPullOutDistance:25, pullOutFrameStep:4, pullOutFrameInterval:40, pullOutLabelPadding:65, pullOutLabelFont:"bold 16px 'Trebuchet MS', Verdana, sans-serif", pullOutValueFont:"bold 12px 'Trebuchet MS', Verdana, sans-serif", pullOutValuePrefix:"$", pullOutShadowColour:"rgba( 0, 0, 0, .5 )", pullOutShadowOffsetX:5, pullOutShadowOffsetY:5, pullOutShadowBlur:5, pullOutBorderWidth:1, pullOutBorderStyle:"#333" }; $.extend(defaults, settings); return this.each(function(){ var canvas = this, currentPullOutSlice = -1, currentPullOutDistance = 0, animationId = 0, totalValue = getTotal(data), startPullOut = function(canvas, slice, d){ // Exit if we're already pulling out this slice if ( currentPullOutSlice == slice ) return; // Record the slice that we're pulling out, clear any previous animation, then start the animation currentPullOutSlice = slice; currentPullOutDistance = 0; clearInterval( animationId ); animationId = setInterval(function(){ animatePullOut(canvas, slice, d); }, defaults.pullOutFrameInterval); }, toggleSlice = function(canvas, slice, d) { if(slice == currentPullOutSlice ) { pushIn(canvas, d); }else{ startPullOut(canvas, slice, d); } }, pushIn = function(canvas, d) { currentPullOutSlice = -1; currentPullOutDistance = 0; clearInterval( animationId ); drawChart(d, canvas); }, drawChart = function(d, canvas) { context = canvas.getContext('2d'); context.clearRect(0, 0, canvas.width, canvas.height); for(var slice in d){ if(slice != currentPullOutSlice){ drawSlice(canvas, d, context, slice); } } if(currentPullOutSlice != -1 ){ drawSlice(canvas, d, context, currentPullOutSlice); } }, drawSlice = function(canvas, d, context, slice) { var centreX = canvas.width / 2, centreY = canvas.height / 2, chartRadius = Math.min( canvas.width, canvas.height ) / 2 * ( defaults.chartSizePercent / 100 ), chartStartAngle = -.5 * Math.PI; // Start the chart at 12 o'clock instead of 3 o'clock; var s = d[slice], // Compute the adjusted start and end angles for the slice startAngle = s['startAngle'] + chartStartAngle, endAngle = s['endAngle'] + chartStartAngle, easeOut = function(ratio, power){ return ( Math.pow ( 1 - ratio, power ) + 1 ); }; if(slice == currentPullOutSlice) { var midAngle = (startAngle + endAngle) / 2, actualPullOutDistance = currentPullOutDistance * easeOut( currentPullOutDistance/defaults.maxPullOutDistance, .8 ); startX = centreX + Math.cos(midAngle) * actualPullOutDistance; startY = centreY + Math.sin(midAngle) * actualPullOutDistance; context.fillStyle = 'rgb(' + s.color.join(',') + ')'; context.textAlign = "center"; context.font = defaults.pullOutLabelFont; context.fillText(s['label'], centreX + Math.cos(midAngle) * ( chartRadius + defaults.maxPullOutDistance + defaults.pullOutLabelPadding ), centreY + Math.sin(midAngle) * ( chartRadius + defaults.maxPullOutDistance + defaults.pullOutLabelPadding ) ); context.font = defaults.pullOutValueFont; context.fillText( defaults.pullOutValuePrefix + s['value'] + " (" + ( parseInt( s['value'] / totalValue * 100 + .5 ) ) + "%)", centreX + Math.cos(midAngle) * ( chartRadius + defaults.maxPullOutDistance + defaults.pullOutLabelPadding ), centreY + Math.sin(midAngle) * ( chartRadius + defaults.maxPullOutDistance + defaults.pullOutLabelPadding ) + 20 ); context.shadowOffsetX = defaults.pullOutShadowOffsetX; context.shadowOffsetY = defaults.pullOutShadowOffsetY; context.shadowBlur = defaults.pullOutShadowBlur; } else { // This slice isn't pulled out, so draw it from the pie centre startX = centreX; startY = centreY; } // Set up the gradient fill for the slice var sliceGradient = context.createLinearGradient( 0, 0, canvas.width*.75, canvas.height*.75 ); sliceGradient.addColorStop( 0, defaults.sliceGradientColour ); sliceGradient.addColorStop( 1, 'rgb(' + s.color.join(',') + ')' ); // Draw the slice context.beginPath(); context.moveTo( startX, startY ); context.arc(startX, startY, chartRadius, startAngle, endAngle, false); context.lineTo( startX, startY ); context.closePath(); context.fillStyle = sliceGradient; context.shadowColor = ( slice == currentPullOutSlice ) ? defaults.pullOutShadowColour : "rgba( 0, 0, 0, 0 )"; context.fill(); context.shadowColor = "rgba( 0, 0, 0, 0 )"; // Style the slice border appropriately if(slice == currentPullOutSlice){ context.lineWidth = defaults.pullOutBorderWidth; context.strokeStyle = defaults.pullOutBorderStyle; } else { context.lineWidth = defaults.sliceBorderWidth; context.strokeStyle = defaults.sliceBorderStyle; } // Draw the slice border context.stroke(); }, animatePullOut = function(canvas, slice, d){ // Pull the slice out some more currentPullOutDistance += defaults.pullOutFrameStep; // If we've pulled it right out, stop animating if ( currentPullOutDistance >= defaults.maxPullOutDistance ) { clearInterval( animationId ); return; } // Draw the frame drawChart(d, canvas); }, addAngles = function(d){ var currentPos = 0; // The current position of the slice in the pie (from 0 to 1) for(var slice in d) { d[slice]['startAngle'] = 2 * Math.PI * currentPos; d[slice]['endAngle'] = 2 * Math.PI * ( currentPos + ( d[slice]['value'] / totalValue ) ); currentPos += d[slice]['value'] / totalValue; } return d; }, bindPieClicks = function(e, d){ var centreX = e.width / 2, centreY = e.height / 2, chartRadius = Math.min( e.width, e.height ) / 2 * ( defaults.chartSizePercent / 100 ); $(e).click(function(clickEvent){ var chartStartAngle = -.5 * Math.PI, mouseX = clickEvent.pageX - this.offsetLeft, mouseY = clickEvent.pageY - this.offsetTop, xFromCentre = mouseX - centreX, yFromCentre = mouseY - centreY, distanceFromCentre = Math.sqrt( Math.pow( Math.abs( xFromCentre ), 2 ) + Math.pow( Math.abs( yFromCentre ), 2 ) ); if(distanceFromCentre <= chartRadius){ var clickAngle = Math.atan2( yFromCentre, xFromCentre ) - chartStartAngle; if ( clickAngle < 0 ) clickAngle = 2 * Math.PI + clickAngle; for(var slice in d){ if (clickAngle >= d[slice]['startAngle'] && clickAngle <= d[slice]['endAngle'] ) { // Slice found. Pull it out or push it in, as required. toggleSlice(e, slice, d); return; } } } pushIn(e, d); }); }; // Exit if the browser isn't canvas-capable if(typeof canvas.getContext === 'undefined'){ return; } data = addAngles(data); //add angles to the data chart drawChart(data, canvas); //draw chart bindPieClicks(canvas, data); //bind pie clicks }); }; })(jQuery); </pre> <p>Here is how you can use the plug-in:</p> <pre> $("#chart").pieChart([ { label: "SuperWidget", color: ["13", "160", "104"], value: "1862.12" }, { label: "HyperWidget", color: ["25", "78", "156"], value: "1316.00" }, { label: "SuperWidget", color: ["237", "156", "19"], value: "712.49" }, { label: "SuperWidget", color: ["237", "87", "19"], value: "3236.27" }, { label: "SuperWidget", color: ["5", "114", "73"], value: "6122.06" } ], { chartSizePercent:55, sliceBorderWidth:1, sliceBorderStyle:"#fff", sliceGradientColour:"#ddd", maxPullOutDistance:25, pullOutFrameStep:4, pullOutFrameInterval:40, pullOutLabelPadding:65, pullOutLabelFont:"bold 16px 'Trebuchet MS', Verdana, sans-serif", pullOutValueFont:"bold 12px 'Trebuchet MS', Verdana, sans-serif", pullOutValuePrefix:"$", pullOutShadowColour:"rgba( 0, 0, 0, .5 )", pullOutShadowOffsetX:5, pullOutShadowOffsetY:5, pullOutShadowBlur:5, pullOutBorderWidth:1, pullOutBorderStyle:"#333" }); </pre> <p>where the jQuery plug-in selector $("#chart") refers to the canvas element.</p> HTML5 examples demos and tutorials http://davidpirek.com/blog/html5-examples-demos-and-tutorials Aug 31 2010 12:00AM <p>I have been doing a lot of HTML 5 research recently, so I decided to start a post, where I would like to compile my findings. My main focus so far has been to find a good charting solution.</p> <p>To start with here is a great list of <a href="http://www.splashnology.com/blog/javascripts/290.html">HTML5 charting frameworks</a>. I will be working through the list, evaluating each of these frameworks, and drawing my final conclusion. Here is my take on a few of them so far:</p> <p><a href="http://g.raphaeljs.com/">gRapha&euml;l's</a> charts: it looks really promising but after using it for a few days, I found it kind of buggy. Maybe this was just the way the charting code was written.</p> <p><a href="http://processingjs.org/">Processing.js</a>: the performance and the author really impresses me, but why do I have to write JAVA in the browser to get this working?</p> <p><a href="http://www.danvk.org">http://danvk.org</a>: first impressions are great, but could not find a pie chart for some reason.</p> <p>In conclusion, I find it really hard to work with xy axis, setting offsets for charts to fit different texts and labels just seems a lot of work. Maybe it's just because I have been working in CSS too long and pixel drawing that the flash guys are use to is just not native to me. <a href="http://danvk.org">danvk.org</a> charting framework get's around the text issue by doing the labels in HTML/CSS and slapping them on and around the canvas. The user experience is really good of this solution, but just hope this will be stable enough for all kinds of crazy scenarios.</p> asp.net mvc cms list http://davidpirek.com/blog/aspnet-mvc-cms-list Aug 23 2010 12:00AM <p>It looks like ASP.NET MVC is gaining a great momentum which could bring more exposure to my <a href="http://www.aspnetcmsdemo.com/">free ASP.NET MVC CMS</a>. I have been keeping an eye on the competition ASP.NET CMS to see if there is anything out there that is going the same direction as my product, and here is a list of the current landscape with some comments how I think these products compare to what I am trying to accomplish.</p> <p>Again, let me restate my goal: I wan to have a small light yet powerful CMS using the latest ASP.NET technology that would be a great ASP.NET alternative to Wordpress.</p> <p>Here is the list and my comments about each products:</p> <ul class="list"> <li><strong>Kentico CMS for ASP.NET</strong><br /> very complex, not really free, and in ASP.NET MVC, so from my perspective using outdated technology</li> <li><strong>Telerik Sitefinity</strong><br /> big fancy product in the spirit of Telerik, not free, not MVC, not simple</li> <li><strong>DotNetNuke</strong><br /> the good news: it's free and open source, otherwise an overkill for a blog site</li> <li><strong>mojoPortal</strong><br /> looks outdated and complicated</li> <li><strong>Oxite</strong><br /> the other side of the spectrum: very complex and not free </li> <li><strong>N2 Open Source ASP.NET CMS</strong><br /> the sample page looks like a spam site, so I guess the developer decided just to make some adsence money of his code </li> <li><strong>BlogEngine.NET</strong><br /> looks OK but since it's not in MVC I would call it an outdated technology...</li> <li><strong>umbraco</strong><br /> this is definitely a mature product, however would be a total overkill for a small website, and is not free either. This is the other side of the spectrum that my CMS is trying to target: simple websites, and blogs.</li> <li><strong>mvccms.com</strong><br /> does not look like a finished product, more like a sandbox code </li> <li><strong>Graffiti CMS</strong><br /> this one is probably to closest to what I am trying to do, has the simplicity, and decent UI, however does not run on ASP.NET MVC so I would call it outdated.</li> </ul> asp.net mvc jquery widgets http://davidpirek.com/blog/aspnet-mvc-jquery-widgets Aug 20 2010 5:55AM <p>Microsoft decided to attack head on frameworks like EXTJS (sencha.com) and DOJO (supported by IBM) by developing its own jQuery plugins, and pairing them with ASP.NET MVC. This is currently being done under the radar, so not many people know about it. Such as news made me really excited as being in many ways a pioneer in building rich internet applications (RIA or ajax apps). I have been hacking .NET and single page AJAX apps for a few years now, and when I started, to many it seamed strange that I would take such an UI (XHTML) abusive framework, and pair it with jQuery. I was able to do so, by stripping ASP.NET off all the post backs and view states, so when MVC came about with it's clean approach, it seemed like exactly what I was waiting for to make my jQuery magic run perfect... and now seeing that this "marriage" of the two technologies got official blessing, and being pitched to big corporation as a perfect HTML5/RIA technology... I feel like a prophet. DOJO has become a similar player when it got endorsed by IBM (JAVA on the back end) and now jQuery is getting Microsoft endorsement... just have a feeling, there will be a lot of .NET MVC + jQuery OO JavaScript jobs out there in the coming years.</p> Foreign Transaction Fees: none with CapitalOne http://davidpirek.com/blog/foreign-transaction-fees-none-with-capitalone Aug 5 2010 7:33PM <p>So I am using this European hosting company that does a great job on hosting my Czech websites, and I noticed that every time I pay hosting, I am bing charged almost a dollar of fees. Well, this amount was really under the threshold of my laziness to do something, however last week I got tired getting invoices every month so I paid for the whole year, and my mint.com account alerts me that I am bing charged $10 in fees, which made me upset... this is my lunch money, and chase just ate my lunch! So I did little searching around and this is what I found: Apparently CapitalOne is the only company that does not charge Foreign Transaction Fees! So here we go, CapitalOne is my new international card, and you bed that's the only card I will ever use, on my foreign vacations. Screw you all you other banks. I am not paying 3% tax on my overseas fun.</p> ajax get request caching fix http://davidpirek.com/blog/ajax-get-request-caching-fix Aug 2 2010 5:07PM <p>This used to be problem only for IE but now I have seen this all over the place, so just to be save, I recommend applying this fix to all of your ajax get requests. The idea is to add a unique query string parameter to fool the browser into thinking that you are making different requests each time, so adding a random number will do it. To make this robust, you want to detect if the link already has a query string param, so that you are not appending "?" to already existing query string. The "url.indexOf('?')" will do the job of "contains()" in other languages. So here we go, here is the final code: </p> <pre> if(url.indexOf('?') < 0){ url = url + "?_r=" + Math.random() * 5; } else{ url = url + "&_r=" + Math.random() * 5; } </pre> AmpliTube iRig alternative cabel under $10 http://davidpirek.com/blog/amplitube-irig-alternative-cabel Jul 16 2010 11:02PM <p>I was pretty impressed by what AmpliTube did with its guitar software. Being a geek, I could not get over the fact that their are selling iRig cable for $39.99 + shipping. I know this is just a cable, and usually order cables for under $5 from Amazon.com. I am a casual guitar player, so I don't really need the professional durability... and I also would like to do little more with this setup that just plug it in headset (mix board, my living room amp atc...)</p> <img src="http://ecx.images-amazon.com/images/I/51t%2BY3RQ1hL._SS500_.jpg" alt="AmpliTube iRig" /> <p>So with little search around here is the hack: it appears that older video cables (used for video cameras like Sonny) use the same 5 contact jack, as the iphone, so for $5 including shipping you can get this <a href="http://www.amazon.com/gp/offer-listing/B000I1D0BW/ref=dp_olp_new?ie=UTF8&qid=1279313068&sr=8-2&condition=new"> 6FT 3.5 mm to 3 RCA AV Camcorder Video Cable Sony JVC 6</a>. Then all you need is a <a href="http://www.amazon.com/inch-Mono-Female-Male-Adaptor/dp/B000I98ZNC/ref=sr_1_1?s=electronics&ie=UTF8&qid=1279313955&sr=1-1">1/4 inch Mono Female / RCA Male Adaptor</a> (plug it in the red cable, which goes in the guitar). The the other two cables are your left and right signal, which can go to your mix board or other amp you have laying around.</p> <p>Update: I have found another alternative. It's called " iPhone Adapter 3.5mm 4 conductor TRRS Male to 3.5mm TRS Headphone Jack and 1/4in Microphone Jack" and <a href="http://www.kvconnection.com/product-p/km-iphone-43f35f.htm">www.kvconnection.com for $23.39</a>. Error calling method on NPObject! Flash js error http://davidpirek.com/blog/error-calling-method-on-npobject-flash-js-error Jul 14 2010 11:11PM <p>This is by far one of the weirdest bugs I have seen. I appears that flash has some kind of security feature for detecting what is the origin of object variables that are being passed to the JavaScript API. If such variable comes from untrusted source, in my case this was same domain iframe, it troughs this error: Error calling method on NPObject! Not being a flash guy, this kind of made me hate adobe, for not having a better error message, and have me waist few hours trying to figuring out what's gong on.... oh and since there is such a simple hack for it, this "security" feature is a joke anyway!!</p> <p>Solution: I tried just to sign the passed variable to a local variable but that did not work, so what I had to do was loop through each member of the array, and re-assign it one by one, so this is the work around hack.</p> <p>Here is a nice utility function using does the job for you:</p> <pre> var hideOrigin = function(list){ var list2 = []; $.each(list, function(i, d) { list2[i] = d; }); return list2; } </pre> <p>So when you are passing a "dirty" object to the flash movie, you can just do something like this:</p> <pre> thisMovie("swfId").external(hideOrigin(suspiciousObject)); </pre> javascript sorting of integer arrays http://davidpirek.com/blog/javascript-sorting-of-integer-arrays Jun 28 2010 10:22PM <p>This is famous gotcha in JavaScript: How to sort an array of two digit integers. JavaScript will treat the values in the array as strings when sorting, so there is a special trick that needs to be performed: call back function as a .sort() parameter.</p> <pre> function numberSorter(a, b) { return a - b; } var numbers = [20, 7, 65, 10, 3, 0, 8, -60]; numbers.sort(numberSorter); </pre> LINQ to SQL C# html li/ul nested tree list http://davidpirek.com/blog/linq-to-sql-html-nested-tree-list Jun 23 2010 9:36PM <p><strong>Problem:</strong> I wanted to create a database model that would allow me to store tree structure of categories and sub categories. This flat table I wanted to render as a UL/LI tree list, so that style it with CSS and use jQuery to add the tree view like behavioral. So here is what I came up with.</p> <p>Let's start with the DB desgin:</p> <p>The DB columns are: id, name, parentId; where if parentId is null, there is no it means this row is the parent.</p> <p>Now comes the C# LINQ code:</p> <pre> public class categoryTree { public IQueryable&lt;Category&gt; categories; public string getTree() { StringBuilder sb = new StringBuilder(); sb.Append("&lt;ul class=\"list\"&gt;"); foreach (var s in this.categories) { if (s.parentId == null) { sb.Append("&lt;li&gt;"); sb.Append(s.name); sb.Append(this.getChildren(s.id)); sb.Append("&lt;/li&gt;"); } } sb.Append("&lt;/ul&gt;"); return sb.ToString(); } //child self referencing loop private string getChildren(int parentId) { StringBuilder sb = new StringBuilder(); //child categories var childCategories = from m in this.categories where m.parentId == parentId orderby m.name select m; //check if there are nay children if (childCategories.Count() > 0) { sb.Append("&lt;ul&gt;"); foreach (var s in childCategories) { sb.Append("&lt;li&gt;"); sb.Append(s.name); //call itself for children sb.Append(this.getChildren(s.id)); sb.Append("&lt;/li&gt;"); } sb.Append("&lt;/ul&gt;"); } return sb.ToString(); } } </pre> <p>And this is how you can use this code:</p> <pre>var allCategories = from m in dataContext.Categories orderby m.name select m; categoryTree q = new categoryTree(); q.categories = allCategories; string tree = q.getTree(); </pre> Facebook Graph API tutorial: JavaScript & jQuery http://davidpirek.com/blog/facebook-graph-api-tutorial-javascript-jQuery Jun 16 2010 7:24AM <p>Last night I spend a couple hours playing with the Facebook new Graph JavaScript API, and could not fall to sleep afterward how excited I was about the potential of this new service. I have started a few social networking sites, and seeing them all loosing to Facebook has been troubling me for a while. Now that Facebook has gotten so big, I know I cannot but join it. Yet it seems that through the ability to tap into its services that actually can open a whole new world of social networking, while making development easier. In addition, what comes to my advantage is that I am JavaScript developer, and don't have a problem creating full apps only in JavaScript. I feel like a I am at sweet spot, knowing enough about social networking and being master at the "new language" of social networking: JavaScript. </p> <p>Anyway, let's dive in the code. I will show you here how to do a simple hello world JavaScript app using the Facebook Graph API.</p> <p>First, you have to have a domain, and get it approved as a Facebook application, and get the application ID, which matches the domain (otherwise your app will not work) I know this sounds simple but I wasted at least an hour trying to get this running on a local host :)</p> <p>Next, load the Graph API JavaScript. (you can put it on the bottom of the page), and let's also add our jQuery reference.</p> <pre>&lt;script src="js/jquery.js"&gt;&lt;/script&gt;<br />&lt;script src="http://connect.facebook.net/en_US/all.js"&gt;&lt;/script&gt;<br />&lt;script&gt;</pre> <p>Now, the HTML part: include an Graph API tag for the login button. Note that the bottom has some important properties, that will determine how much can your app access the Facebook data. For now, let's just allow read and publish to the news stream.</p> <pre>&lt;fb:login-button perms="read_stream,publish_stream"&gt;<br /> Grant Permissions to make more examples work<br />&lt;/fb:login-button&gt;</pre> <p>Let's test this now, click on the login button, which should prompt you to allow this application into your Facebook account. You may have to go back to your app settings (on the Facebook website) to add redirect URL, to let the app know where to go after the login. (I was getting some error before I did that.</p> <p>Now, let's see if we can pull some data:</p> <pre>FB.api('/me', function(response) {<br /> console.log("user id: " + response.id);<br />});</pre> <p>If that works, let's try to post some data to the wall. Add this below your login button (or wherever in the doc you have your HTML)</p> <pre>&lt;div id="content"&gt;&lt;/div&gt;<br />&lt;form&gt;<br /> &lt;textarea name="text" id="comment"&gt;&lt;/textarea&gt;<br /> &lt;a href="#" id="submit_comment"&gt;submit&lt;/a&gt;<br />&lt;/form&gt;</pre> <p>And add this JavaScript:</p> <pre>$("#submit_comment").click(function(){<br /> FB.api('/me/feed', 'post', { message: $("#comment").val() }, function(response) {<br /><br /> if (!response || response.error) {<br /> alert('Error occured');<br /> } <br /> else {<br /> console.log('Message was posted! (post id: ' + response.id + ")");<br /> }<br /> });<br /><br /> return false;<br />});</pre> <p>If all works, you should be able to post right to your wall from here.</p> <p>What makes me most excited, is the viral traffic potential of hooking my social networking websites into the. All the content that a user posts on a website gets posted on their wall, and all their Facebook friends will see this. I use to rely mostly on SEO in the past for promoting my apps, but this has so much more potential! Social networking companies are scared of Facebook, but I see this as a great opportunity. Yes this is a bit paradigm shift, but I believe now it's definitely in my favor.</p> HTML5 jQuery JavaScript plugins http://davidpirek.com/blog/html5-jquery-javascript-plugins Jun 15 2010 3:24AM <p>I was looking around how to utilize jQuery for all the HTML5 features that are coming to great popularity, so here is what I have found:</p> <p><a href="http://demo.paranoidr.de/jVideo/"> JQuery HTML5 video player</a><br /><a href="http://plugins.jquery.com/project/oiplayer"> oiplayer is another HTML5 player</a><br /><a href="http://www.orangesoda.net/jquery.dataset.html"> JQuery dataset</a><br /> <a href="http://i-create.org/JavaScript/jqPanoramic/"> jQuery HTML5 panoramic photo rotation</a></p> <p>Must say, there is not much there at this point. Despite of all the buzz that HTML5 is getting these days, JavaScript being it's language, I would expect more people showing off what this new technology can do.</p> ASP.NET CMS Themes http://davidpirek.com/blog/aspnet-cms-themes Jun 12 2010 5:13AM <p>I have been working on my ASP.NET CMS on and off in recent days. The newest big feature will be "themes" much like wordpress has, that can be defined in a single folder, and installed from the admin. This way the current wordpress themes can be relatively easily converted. I still don't expect to attract a big community of designers, but for my internal purposes, this should work just fine.</p> <p>With the next release, I have also addressed some of the existing bugs and shortcomings, so the CMS should run much smoother.</p> define (create) namespace javascript jQuery http://davidpirek.com/blog/define-namespace-in-javascript-jquery-and-extjs Jun 10 2010 8:29PM <p>There are a few different ways you can create namespace in JavaScript. The most simple way is just defining an object:</p> <pre> window.MyNamespace = {}; </pre> <p>In the next step you can make this little more robust by checking if the namespace has been created before, so that you don't overwrite any existing "methods" with an empty object:</p> <pre> if (typeof MyNamespace == "undefined" || !MyNamespace ) { window.MyNamespace = {}; }; </pre> <p>If you create a lot of namespaces, it might be a good idea to create some kind of "namespace constructor" function, which allows you to create many different namespaces on the fly, while still having the "overwrite protection" build in. This is an example code from ExtJs:</p> <pre> function namespace(){ var o, d; Ext.each(arguments, function(v) { d = v.split("."); o = window[d[0]] = window[d[0]] || {}; Ext.each(d.slice(1), function(v2){ o = o[v2] = o[v2] || {}; }); }); return o; } </pre> <p>Can be used this way:</p> <pre> namespace("MyNamespace.class", "MyNamespace.anotherClass"); MyNamespace.anotherClass = function(){ //do something } </pre> <p>Lastly, for those of you that rather prefer jQuery, here is the jQ version of the "namespace" creator:</p> <pre> CreateNs: function(){ var o, d; $.each(arguments, function(v) { d = arguments[1].split("."); o = window[d[0]] = window[d[0]] || {}; $.each(d.slice(1), function(v2){ o = o[arguments[1]] = o[arguments[1]] || {}; }); }); return o; } </pre> Click through rate (CTR) optimization dilemma http://davidpirek.com/blog/click-through-rate-ctr-optimization-dilemma Jun 8 2010 4:52PM <p>I have recently figured out a way how to really optimize CTR on some of my MFA (made for&nbsp;ad-sense) type of websites. The dilemma I have know if I should these techniques on my other websites that have loyal community. How much will invasive advertising turn off my users where I will lose money long term and decrease the value of my ports? Thanks a question worth a lot of money to me now...&nbsp;</p> <p>On the other hand media websites for example do have many ads all over, but I think it is kind of expected that an online magazine will have many ads, but how much can you&nbsp;pollute&nbsp;someones profile page with ads before they get turned off?</p> Show case ajax aplication: beebole.com http://davidpirek.com/blog/show-case-ajax-aplication-beebolecom May 25 2010 10:48PM <p>I have ran across beebole.com time management ajax app, and peeking under the hood a little with firebug, I have discovered a nice server/client interaction pattern, that beebole.com uses: the way the app talks to the server, is through ajax posts to the same url with single key (data) where they pack a "stringified" JSON, which contains all the data, methods, keys. The main advantage I see in this approach is that, you can really mainstream your code both on the client and on the server. </p> <p>For example, here is one such JSON that was send to the server as a result of clicking "collapse" on one of the widgets:</p> <pre> { "service": "user.update_doc", "etype": "user", "eid": 1, "uKey": "doc.app.screens.wgroups", "doc": { "home": [{ "s": [25, 40, 35] }, [{ "name": "addCompany", "cl": true }, { "name": "lastUsed", "cl": true }], { "name": "timeSheet", "calId": "wcal" }, { "name": "googleVisualization", "groupBy": "drill", "chartType": "pieChart", "period": { "k": "w" } }] }, "applyAll": true, "wid": false, "session": "110342d05fa560257af4d7030c98db83c2291d81" } </pre> <p>Now these guys obviously live JSON, since the same company is responsible for <a href="http://beebole.com/pure/"> pure templatign JavaScript library</a>, so if JSON is your thing, this is the way to go. For some reason, I still have problem letting some templating code generate my layouts. I feel that it might be slow or inflexible, but this approach is starting to appearing in other libraries (EXTJS), so maybe HTML coding as we know it might be dead in rich internet applications (RIA). As for my ideal approach, I still feel that baking the HTML markup on the server might save a few of milliseconds in rendering, and that the server has more power and tools, to create structured presentation code.</p> Ext.state.Manager server backup session provider http://davidpirek.com/blog/extstatemanager-server-backup-session-provider May 25 2010 9:16PM <p>Here is the scenario: You want need to have some kind of session mechanism, that will allow you to store data across different views in you Ajax application. Let's say you go from form A to form B and there is some data that form B needs from form A, but form A is no longer in the DOM, so you cannot grab it from there. EXTJS has a build session manager, that in the core has two "providers" (storage mechanisms) one just stores the data in JavaScript variable, which in case of reloading the page will get lost, and the other stores the data in a cookie. One of these two "providers" seem to be robust enough (cookies have character limit, and don't seem to be a good storage mechanism for larger data). I decided to take the default Ext.state.Manager provider, and add a "backup" method, that sends the data to the serve, when somebody hits F5, and puts the data back to the JavaScript variable, when the page loads again. (save on unload, retrieve on load)</p> <p>So enough of theory, here is the is the code:</p> <pre> (function() { //namespace creation and create local shortcut var m = Ext.namespace("MyLib.Session"); var settings = { sessionUrl: "/Ajax/GetSession", sessionPostUrl: "/Ajax/SetSession" }; var session = {}; //converts object to string var objectToString = function(o){ var parse = function(_o){ var a = [], t; for(var p in _o){ if(_o.hasOwnProperty(p)){ t = _o[p]; if(t && typeof t == "object"){ a[a.length]= p + ":{ " + arguments.callee(t).join(", ") + "}"; } else { if(typeof t == "string"){ a[a.length] = [ p+ ": \"" + t.toString() + "\"" ]; } else{ a[a.length] = [ p+ ": " + t.toString()]; } } } } return a; } return "{" + parse(o).join(", ") + "}"; }; var saveSession = function(){ var o = session; var sSession = objectToString(o); Ext.Ajax.request({ method: "POST", params: {clientState : sSession}, url: settings.sessionPostUrl }); } window.onunload = saveSession; //load data from the server on load var loadSession = (function(){ Ext.Ajax.request({ method: "GET", url: settings.sessionUrl, success: function(jsonResult) { //reloads session session = Ext.decode(jsonResult.responseText); } }); })(); //connecting Lib.session to Ext.state.Manager var provider = function(config){ return{ get : function(name){ return session[name]; }, set : function(name, value){ session[name] = value; }, clear : function(){ session = {}; } } }; //set provider Ext.extend(provider, Ext.state.Provider, {}); Ext.state.Manager.setProvider(provider()); })(); </pre> How to convert JavaScript object to string http://davidpirek.com/blog/object-to-string-how-to-deserialize-json May 17 2010 5:19PM <p>I have run across the problem that I want to store my JavaScript objects on the server, for session purposes. In order to do so, I need to convert the object to strong, send it to the server, and get back, eval() it, so that it becomes object again.</p> <p>Here is the js function, that with little inspiration from the web, I came up with:</p> <pre> function objectToString(o){ var parse = function(_o){ var a = [], t; for(var p in _o){ if(_o.hasOwnProperty(p)){ t = _o[p]; if(t && typeof t == "object"){ a[a.length]= p + ":{ " + arguments.callee(t).join(", ") + "}"; } else { if(typeof t == "string"){ a[a.length] = [ p+ ": \"" + t.toString() + "\"" ]; } else{ a[a.length] = [ p+ ": " + t.toString()]; } } } } return a; } return "{" + parse(o).join(", ") + "}"; } </pre> <p>This is how you can test it:</p> <pre> console.log(objectToString({parent: {child: "value"}})); </pre> <p>Oh and you can through in functions (methods) to the object too, and it works!</p> <p>I don't really recommend this one, but you could add this to the "Object" object as a method:</p> <pre> Object.prototype.toString(o){ var parse = function(_o){ var a = [], t; for(var p in _o){ if(_o.hasOwnProperty(p)){ t = _o[p]; if(t && typeof t == "object"){ a[a.length]= p + ":{ " + arguments.callee(t).join(", ") + "}"; } else { if(typeof t == "string"){ a[a.length] = [ p+ ": \"" + t.toString() + "\"" ]; } else{ a[a.length] = [ p+ ": " + t.toString()]; } } } } return a; } return "{" + parse(o).join(", ") + "}"; } </pre> <p>Here are some additional thoughts: for some, the idea that we are converting object to string as it is may sound too wild, so there is an option to encode the object to a JSON. This will strip out all functions, make all values "strings" (then you have to deal with stuff such as type conversion, which opens another can of worms). Such parser/encoder would be little more complicated, so I will refer you to the one ExtJs has: Ext.util.JSON.encode(). If that's what you want. Here is the <a href="http://www.extjs.com/deploy/dev/docs/source/JSON.html#cls-Ext.util.JSON">ExtJs JSON encode source code</a>.</p> extjs single page javascript ajax application http://davidpirek.com/blog/extjs-single-page-javascript-ajax-application May 13 2010 4:40PM <p>ExtJs is a JavaScript widget framework that came to great popularity in recent years, especially at large companies that build complex web applications. This vision of rich component toolkit that allows for consistent UI look and feel, with the power of ajax interactions that beat the performance of traditional post pack mechanisms.</p> <p>While there are many examples, and event showcase applications, I have not seen any model how to create an JavaScript applications, that can go from "screen" A to totally different "screen" B, where the screen specifications (structure + logic) would come from a server request. (If you have a large JavaScript app that has 100 screens, reloading all the screen definitions would be a waist, and going from page to page (reloading all the Js, css, images, almost a 1 meg of resources) would be event bigger waste.</p> <p>So let's get into the example: first we will create a custom object that will be returned from the ajax call. This object, packed in a closure contains both, logic and layout definition of our screens, and when decoded (evaled) becomes a ExtJs screen object, that you can just render on the page.</p> <pre> (function(){ //app logic goes here var clickHandler = function(){ alert("Helo world!"); }; //view json goes here var items = { "xtype":"panel", "title":"My Panel", "layout":"table", "items" : [ { "xtype":"container", "autoEl":"div", "html":"Helo world!" }, { "xtype":"button", "text":"Click Me", "handler": clickHandler } ] }; //adding a callback return new Ext.Container({ items: items }); })() </pre> <p>Next, here comes the magic file. Our "ajax url" will be stored in our "hash" (#ajax/myScreen). This allows us to have "history/back button" support, and also ability to maintain an ajax application session (to an extend). By the way, this is trick that facebook currently uses to "ajaxtify" its pages</p> <pre> (function(){ //load page function var loadPage = function(url){ Ext.Ajax.request({ url: url, success: function(jsonResult) { //turn ajax response into js object var jsonData = Ext.decode(jsonResult.responseText); //render page jsonData.render(Ext.getBody()); }, failure: function() { alert("something went wrong!"); } }); } //get hash function var getHashValue = function() { var arr = window.location.hash.split("#"); var hasValue = arr[1]; //sets default if (typeof hasValue == "undefined") { return false; } var hashLen = hasValue.indexOf("?"); if(hashLen>0){ hasValue = hasValue.substring(0,hashLen); } return hasValue; } //hash change event listerner var onHashChange = function(event) { //last hash var lastHash = getHashValue(); //checker (function watchHash() { var hash = getHashValue(); if (hash !== lastHash) { event(); lastHash = hash; } var t = setTimeout(watchHash, 100); })(); } //on hash change trigger onHashChange(function() { //your on-change code here var url = getHashValue(); loadPage(url); }); })(); </pre> jQuery CodePress Plugin http://davidpirek.com/blog/jquery-codepress-plugin May 4 2010 9:56PM <p>I have been using the <a href="http://sourceforge.net/projects/codepress/">CodePress code editor</a> for some time, so I thought that it would be cool to create a jQuery interface for it. At the time when I am writing this post, there is already <a href="http://www.fyneworks.com/jquery/Codepress/">similar plugging</a> released, however the site seems to be down, so not sure if the author is still supporting it.</p> <p>What I did was just slightly modified the existing code, while giving it the full power of jQuery:</p> <pre> // plugin definition $.fn.codePress = function(op) { // build main options before element iteration var _op = $.extend({}, $.fn.codePress.defaults, op); //set path CodePress.path = _op.path; var iframe = {}; // iterate and reformat each matched element return this.each(function() { var elm = $(this); //selected element elm.addClass( _op.language); //get id var id = elm.attr("id"); //change id to tep id elm.attr("id", id + "_cp"); iframe[id] = CodePress(elm[0]); //insert masking iframe elm.before(iframe[id]); }); }; //obtions object $.fn.codePress.defaults = { path: "/content/js/framework/codepress/", language: "html" }; </pre> <p>The full modified codepress code can be <a href="http://davidpirek.com/content/files/documents/c76c65f8-956a-45ae-86c8-39624fb48dc1.js">downloaded here</a>.</p> ASP.NET MVC paging (digg-style) http://davidpirek.com/blog/aspnet-mvc-paging-digg-style May 4 2010 9:38PM <p>In ASP.NET MVC there are not rich user controls, like we were used to in web forms, so things like the lack of paging grid may be a surprise to some. For a code control freek like me, this is exactly what I always wanted, so now finally I can create controls the way I want :)</p> <p>Here is an HTML helper-style static function that will return exactly the same type of paging you see on this blog:</p> <pre>public static string links(int intCurrentPage, int intPerPage, int intNumberofItems, string pageNumberPrefix, string previousText, string nextText)<br /> {<br /><br /> string strPreviousText = previousText;<br /> string strNextText = nextText;<br /><br /> StringBuilder sb = new StringBuilder();<br /><br /> string strPageFileName = pageNumberPrefix; //adds prefix before the paging (useful for ajax by adding '#')<br /><br /> if (intCurrentPage &lt; 1)<br /> {<br /> intCurrentPage = 1;<br /> }<br /><br /> int number_of_pages = (intNumberofItems / (intPerPage + 1));<br /><br /> int i = 0;<br /><br /> //hide paging if only one page<br /> if (number_of_pages &gt;= 1)<br /> {<br /> sb.Append("&lt;div class='clearfix paging'&gt;");<br /> //previous record<br /> if (!(intCurrentPage == 1))<br /> {<br /> sb.Append(" &lt;a href='" + strPageFileName.ToString() + (intCurrentPage - 1).ToString() + "' class='p'&gt;&laquo; " + strPreviousText + "&lt;/a&gt;");<br /> }<br /><br /> if (number_of_pages &lt; 20)<br /> {<br /> // if there are less than 20 record no paging breaking<br /><br /> //numbers<br /> for (i = 0; i &lt;= number_of_pages; i++)<br /> {<br /> if (!(i == intCurrentPage - 1))<br /> {<br /> sb.Append(" &lt;a href='" + strPageFileName + (i + 1) + "'&gt; " + (i + 1) + " &lt;/a&gt;");<br /> }<br /> else<br /> {<br /> sb.Append("&lt;span&gt;" + (i + 1) + " &lt;/span&gt;");<br /> }<br /><br /> }<br /> }<br /> else<br /> {<br /><br /> if (intCurrentPage &lt;= 10)<br /> {<br /><br /> //numbers<br /> for (i = 0; i &lt;= 10; i++)<br /> {<br /> if (!(i == intCurrentPage - 1))<br /> {<br /> sb.Append(" &lt;a href='" + strPageFileName + (i + 1) + "'&gt; " + (i + 1) + " &lt;/a&gt;");<br /> }<br /> else<br /> {<br /> sb.Append("&lt;span&gt;" + (i + 1) + " &lt;/span&gt;");<br /> }<br /> }<br /><br /> sb.Append("&lt;span class='pg_dots'&gt;...&lt;/span&gt;");<br /><br /> //numbers<br /> for (i = number_of_pages - 2; i &lt;= number_of_pages; i++)<br /> {<br /> if (!(i == intCurrentPage - 1))<br /> {<br /> sb.Append(" &lt;a href='" + strPageFileName + (i + 1) + "'&gt; " + (i + 1) + " &lt;/a&gt;");<br /> }<br /> else<br /> {<br /> sb.Append("&lt;span&gt;" + (i + 1) + " &lt;/span&gt;");<br /> }<br /><br /> }<br /> }<br /><br /> else if (intCurrentPage &gt;= number_of_pages - 8)<br /> {<br /> //last 10 numbers scenerio<br /> //numbers<br /> for (i = 0; i &lt;= 3; i++)<br /> {<br /> if (!(i == intCurrentPage - 1))<br /> {<br /> sb.Append(" &lt;a href='" + strPageFileName + (i + 1) + "'&gt; " + (i + 1) + " &lt;/a&gt;");<br /> }<br /> else<br /> {<br /> sb.Append("&lt;span&gt;" + (i + 1) + " &lt;/span&gt;");<br /> }<br /> }<br /><br /> sb.Append("&lt;span class='pg_dots'&gt;...&lt;/span&gt;");<br /><br /> //numbers<br /> for (i = number_of_pages - 10; i &lt;= number_of_pages; i++)<br /> {<br /> if (!(i == intCurrentPage - 1))<br /> {<br /> sb.Append(" &lt;a href='" + strPageFileName + (i + 1) + "'&gt; " + (i + 1) + " &lt;/a&gt;");<br /> }<br /> else<br /> {<br /> sb.Append("&lt;span&gt;" + (i + 1) + " &lt;/span&gt;");<br /> }<br /> }<br /> }<br /> else<br /> {<br /> //numbers<br /> for (i = 0; i &lt;= 3; i++)<br /> {<br /> if (!(i == intCurrentPage - 1))<br /> {<br /> sb.Append(" &lt;a href='" + strPageFileName + (i + 1) + "'&gt; " + (i + 1) + " &lt;/a&gt;");<br /> }<br /> else<br /> {<br /> sb.Append("&lt;span&gt;" + (i + 1) + " &lt;/span&gt;");<br /> }<br /> }<br /><br /> sb.Append("&lt;span class='pg_dots'&gt;...&lt;/span&gt;");<br /><br /> //numbers<br /> for (i = intCurrentPage - 5; i &lt;= intCurrentPage + 3; i++)<br /> {<br /> if (!(i == intCurrentPage - 1))<br /> {<br /> sb.Append(" &lt;a href='" + strPageFileName + (i + 1) + "'&gt; " + (i + 1) + " &lt;/a&gt;");<br /> }<br /> else<br /> {<br /> sb.Append("&lt;span&gt;" + (i + 1) + " &lt;/span&gt;");<br /> }<br /> }<br /><br /> sb.Append("&lt;span class='pg_dots'&gt;...&lt;/span&gt;");<br /><br /> //numbers<br /> for (i = number_of_pages - 3; i &lt;= number_of_pages; i++)<br /> {<br /> if (!(i == intCurrentPage - 1))<br /> {<br /> sb.Append(" &lt;a href='" + strPageFileName + (i + 1) + "'&gt; " + (i + 1) + " &lt;/a&gt;");<br /> }<br /> else<br /> {<br /> sb.Append("&lt;span&gt;" + (i + 1) + " &lt;/span&gt;");<br /> }<br /><br /> }<br /> }<br /> }<br /><br /> //next record<br /> if (!(intCurrentPage == number_of_pages + 1))<br /> {<br /> sb.Append(" &lt;a href='" + strPageFileName + (intCurrentPage + 1) + "' class='n'&gt;" + strNextText + " &raquo;&lt;/a&gt;");<br /> }<br /> sb.Append("&lt;/div&gt;&lt;!-- end of 'paging' --&gt;");<br /><br /> //builds string<br /> return sb.ToString();<br /> }<br /> else<br /> {<br /> return "";<br /><br /> }<br /> }<br /></pre> On Hash Change JavaScript Listener http://davidpirek.com/blog/on-hash-change-javascript-listener May 4 2010 6:01PM <p>Here is another very useful piece of JavaScript, that you will not be able live without, when you create your first JavaScript App.</p> <pre> var onHashChange = function(event) { //get hash function var getHashValue = function() { var arr = window.location.hash.split("#"); var hasValue = arr[1]; //sets default if (typeof hasValue == "undefined") { return false; } var hashLen = hasValue.indexOf("?"); if(hashLen>0){ hasValue = hasValue.substring(0,hashLen); } return hasValue; } //last hash var lastHash = getHashValue(); //checker (function watchHash() { var hash = getHashValue(); if (hash !== lastHash) { event(); lastHash = hash; } var t = setTimeout(watchHash, 100); })(); } onHashChange(function() { //your on-change code here }); </pre> <p>So what is this good for? Well let's say you have an AJAX app, that goes from view to view, but all these views are just JavaScript functions running and updating your DOM. You can change your hash with each view, so when the user hits the "back" button, the hash goes to the previous hash, and now since you have a listener, you can reload/re-render your previous view. Sounds familiar? Actually, facebook.com uses this trick.</p> <p>onhashchange is actually <a href="http://msdn.microsoft.com/en-us/library/cc288209(VS.85).aspx">supported in IE8</a> so this code could improved a little by using the native IE8 support.</a> JavaScript keystroke listener http://davidpirek.com/blog/javascript-keystroke-listener May 4 2010 5:33PM <p>Here is nice piece of JavaScript that allows you to add keystroke listener to your JavaScript application</p> <pre> //keystroke listener var keyStroker = function(f, k, e){ document[e] = function(event){ var keycode; //IE hack if ( typeof event == "undefined" ) { event = window.event; } //IE different event object if ( typeof event.which == "undefined" ) { keycode = event.keyCode; } else{ keycode = event.which; } //run callback if particular key was clicked if(keycode==k){ f(); } }; }; keyStroker(function(){ //write code here }, 192, "onkeyup"); </pre> cross-domain AJAX with dynamic script tag http://davidpirek.com/blog/cross-domain-ajax-with-dynamic-script-tag May 4 2010 5:10PM <p>This code is right from the book "High Performance JavaScript." It lets you do AJAX-like requests cross-domain, and since you can by-pass eval of JSON, by using P-JSON (padded JSON), it actually performs much faster than classic AJAX.</p> <pre> var loadScript = function(url, callBack){ //create element var script = document.createElement("script"); script.type = "text/javascript"; if(script.readyState){ //for IE script.onreadystatechange = function(){ if(script.readyState == "loaded" || script.readyState == "complete"){ script.onreadystatechange = null; callBack(); } } } else{ //other browsers script.onload = function(){ callBack(); } } script.src = url; document.getElementsByTagName("head")[0].appendChild(script); }; </pre> High Performance JavaScript: book review http://davidpirek.com/blog/high-performance-javascript-book-review May 4 2010 4:52PM <p>I have catches this YUI theater video a few days ago, promoting a new JavaScript book. The podcast is great, and the High Performance JavaScript book is a must have for any serious JavaScript developer</p> <p>Although I knew about 70% of the tricks the authors describe here, I still gladly spend the $35 at Barnes & Noble to own this book as a reference. It is a nice overview, of what really matters, in optimizing your JavaScript app. I like the fact that it is written from several perspectives, and each author really gives his best.</p> <p>Here are the chapters</p> <ul class="list"> <li>Chapter 1 Loading and Execution</li> <li>Chapter 2 Data Access</li> <li>Chapter 3 DOM Scripting</li> <li>Chapter 4 Algorithms and Flow Control</li> <li>Chapter 5 Strings and Regular Expressions</li> <li>Chapter 6 Responsive Interfaces</li> <li>Chapter 7 Ajax</li> <li>Chapter 8 Programming Practices</li> <li>Chapter 9 Building and Deploying High-Performance JavaScript Applications</li> <li>Chapter 10 Tools</li> </ul> <div><object width="576" height="324"><param name="movie" value="http://d.yimg.com/m/up/ypp/default/player.swf"></param><param name="flashVars" value="vid=19241148&"></param><param name="allowfullscreen" value="true"></param><param name="wmode" value="transparent"></param><embed width="576" height="324" allowFullScreen="true" src="http://d.yimg.com/m/up/ypp/default/player.swf" type="application/x-shockwave-flash" flashvars="vid=19241148&"></embed></object></div> extjs column layout not working in chrome http://davidpirek.com/blog/extjs-column-layout-not-w