Having fun with Mooml & Twitter

Feb 19 2010 Published by Eneko Alonso under uncategorized

Mooml is a templating engine for Mootools. With it, you can create HTML from Javascript using a very clean syntax. Plus, it is extremely useful when you have to generate repeating elements, since Mooml automatically renders arrays of data multiple times. Let’s see an example.

Searching Twitter

Nowadays there are a lot of APIs out there that support JSON or JSONP, which we can use directly from the front end using Javascript. Also, there are other APIs that return tons of JS data, like Google Maps. In this example we are going to be searching Twitter and displaying the search results in a container div, properly rendered as HTML.

The url for searching Twitter is http://search.twitter.com/search.json?q=mootools&show_user=true (searching for Mootools in this case). Check the output:

  1. {
  2.   "results":[
  3.     {
  4.       "profile_image_url":"http://a3.twimg.com/profile_images/547672997/twitterProfilePhoto_normal.jpg",
  5.       "created_at":"Fri, 19 Feb 2010 17:08:55 +0000",
  6.       "from_user":"digitalr3bel",
  7.       "to_user_id":1212494,
  8.       "text":"@davidwalshblog Hi David – im a big fan of your site! Can you recommend a good MooTools tutorial for someone who can already program? thx",
  9.       "id":9343656026,
  10.       "from_user_id":23704390,
  11.       "to_user":"davidwalshblog",
  12.       "geo":null,
  13.       "iso_language_code":"en",
  14.       "source":"<a href="http://apiwiki.twitter.com/" rel="nofollow">API</a>"
  15.     }
  16.     // More results here…
  17.   ],
  18.   "max_id":9343656026,
  19.   "since_id":0,
  20.   "refresh_url":"?since_id=9343656026&q=mootools",
  21.   "next_page":"?page=2&max_id=9343656026&q=mootools",
  22.   "results_per_page":15,
  23.   "page":1,
  24.   "completed_in":0.0352710000000001,
  25.   "query":"mootools"
  26. }

We want to render each result inside out container div. On this example, we will just render the user name, user image and message text. Here is our Mooml template:

  1. Mooml.register('twitter-search-result', function(entry) {
  2.   div({id: "tweet-" + entry.id},
  3.     img({src: entry.profile_image_url, alt: entry.from_user}),
  4.     span(entry.from_user),
  5.     p(entry.text)
  6.   );
  7. });

Since all results are returned into an array, we can pass it directly to Mooml which will render all of the elements in it:

  1. // Data is the JSON response from Twitter
  2. Mooml.render('twitter-search-result', data.results);

So, let’s do a live test. Type something you want to search for and hit the search button to see Mooml in action :)

Search:

How cool is that?

The code

Ok, so here is the whole code I have used in this post for this mini-demo:

  1. <script type="text/javascript" src="http://enekoalonso.com/lib/mootools-1.2.4-core-nc.js"></script>
  2. <script type="text/javascript" src="http://enekoalonso.com/lib/mooml.js"></script>
  3. <script type="text/javascript">
  4. Mooml.register('twitter-search-result', function(entry) {
  5.   div({id: "tweet-" + entry.id},
  6.     img({src: entry.profile_image_url, alt: entry.from_user}),
  7.     span(entry.from_user),
  8.     p(entry.text)
  9.   );
  10. });
  11.  
  12. function searchTwitter() {
  13.   new Element('script', {
  14.     type:'text/javascript',
  15.     src:'http://search.twitter.com/search.json?q={text}&show_user=true&callback=renderData'.substitute({
  16.       text:$('twitter-mooml-search').get('value')
  17.     })
  18.   }).inject(document.head);
  19. }
  20.  
  21. function renderData(data) {
  22.   $('mooml-twitter-demo').empty().adopt(Mooml.render('twitter-search-result', data.results));
  23. }
  24.  
  25. $('twitter-mooml-search-btn').addEvent('click', searchTwitter);
  26. searchTwitter();

As you can see, the code is very basic and so is the Mooml template. The coolest thing here is that you don’t need to iterate over the results to render each element.

Have fun!

No responses yet

Repulsive force in Javascript with Mootools draggables

Jan 04 2009 Published by Eneko Alonso under uncategorized

A couple of days ago I was reading about the Boids demo made by Ben from Coderholic. I though that was very interesting, specially since I hadn’t heard about boids before.

I wanted to create my own version but instead I decided to play only with the repulsive force, making it interactive using Mootools drag & drop functionality. When the demo loads, 20 elements will be created and they will start moving on their own, trying to keep the minimum distance with any other element. This distance is configurable, as it is the option to keep the element speed or to make them slow down until they stop. At any time, the user can move elements on the screen dragging them with the mouse.

Repulsive Force Demo

See the demo in action.

Source code

  1. var Item = new Class({
  2.   Implements: Options,
  3.  
  4.   options: {
  5.     radius: 10,
  6.     position: { x: window.clientWidth/2, y: window.clientHeight/|>2 },
  7.     speed: { x: 0, y: 0 },
  8.     color: '#333'
  9.   },
  10.  
  11.   initialize: function(options) {
  12.     this.setOptions(options);
  13.     this.addToDOM();
  14.    
  15.     var self = this;
  16.     this.dragController = new Drag(this.element, {
  17.       onStart: function() {
  18.         self.dragging = true;
  19.       },
  20.       onDrag: function() {
  21.         self.options.position = self.element.getPosition();
  22.       },
  23.       onComplete: function() {
  24.         self.options.position = self.element.getPosition();
  25.         self.speed = {x:0,y:0};
  26.         self.dragging = false;
  27.       }
  28.     });
  29.     this.dragging = false;
  30.   },
  31.  
  32.   destroy: function() {
  33.     this.element.destroy();
  34.     delete this;
  35.   },
  36.  
  37.   addToDOM: function() {
  38.     this.element = new Element('div', { 'class': 'item' });
  39.     this.element.setStyles({
  40.       background: this.options.color,
  41.       height: 2*this.options.radius,
  42.       width:  2*this.options.radius,
  43.       left: this.options.position.x,
  44.       top:  this.options.position.y,
  45.       '-moz-border-radius': Math.round(this.options.radius) + 'px',
  46.       '-webkit-border-radius': Math.round(this.options.radius) + 'px',
  47.       'border-radius': Math.round(this.options.radius) + 'px'
  48.     }).inject(document.body);
  49.   },
  50.  
  51.   moveTo: function(x, y) {
  52.     if (this.dragging) return;
  53.  
  54.     this.options.position.x = x;
  55.     this.options.position.y = y;
  56.  
  57.     // Check bounds
  58.     if (this.options.position.x < 0) {
  59.       this.options.position.x = 0;
  60.       this.options.speed.x = 0;
  61.     }
  62.     if (this.options.position.x > window.getWidth()2*this.options.radius) {
  63.       this.options.position.x = window.getWidth()2*this.options.radius;
  64.       this.options.speed.x = 0;
  65.     }
  66.     if (this.options.position.y < $('header').getHeight()) {
  67.       this.options.position.y = $('header').getHeight();
  68.       this.options.speed.y = 0;
  69.     }
  70.     if (this.options.position.y > window.getHeight()2*this.options.radius) {
  71.       this.options.position.y = window.getHeight()2*this.options.radius;
  72.       this.options.speed.y = 0;
  73.     }
  74.  
  75.     // Slow down
  76.     if ($('chk-slowdown').get('checked')) {
  77.       this.options.speed.x *= 0.7;
  78.       this.options.speed.y *= 0.7;
  79.     }
  80.    
  81.     if (this.options.speed.x < 0.001) this.options.speed.x = 0;
  82.     if (this.options.speed.y < 0.001) this.options.speed.y = 0;
  83.   },
  84.  
  85.   draw: function() {
  86.     if (this.dragging) return;
  87.     this.element.setStyles({
  88.       left: Math.round(this.options.position.x),
  89.       top:  Math.round(this.options.position.y)
  90.     });
  91.   }
  92. })
  93.  
  94. var ItemController = new Class({
  95.   Extends: Thread,
  96.   items: [],
  97.  
  98.   initialize: function(options) {
  99.     this.parent(options);
  100.   },
  101.  
  102.   addItem: function(options) {
  103.     this.items.push(new Item(options));
  104.   },
  105.  
  106.   execute: function(){
  107.     this.compute();
  108.     this.draw();
  109.   },
  110.  
  111.   compute: function() {
  112.     var self = this;
  113.     self.items.each(function(item1, index1) {
  114.       self.items.each(function(item2, index2) {
  115.         if (index1 != index2) {
  116.           var distance = {}
  117.           distance.x = (item2.options.position.x + item2.options.radius) -
  118.                  (item1.options.position.x + item1.options.radius);
  119.           distance.y = (item2.options.position.y + item2.options.radius) -
  120.                   (item1.options.position.y + item1.options.radius);
  121.  
  122.           distance.total = Math.sqrt(Math.pow(distance.x, 2) + Math.pow(distance.y, 2));
  123.           if (distance.total < $('distance').get('value')) {
  124.        
  125.             var angle = Math.atan2(distance.y, distance.x);
  126.             var speed = 1000 / distance.total;
  127.  
  128.             item1.options.speed.x += -speed * Math.cos(angle);
  129.             item1.options.speed.y += -speed * Math.sin(angle);
  130.           }
  131.         }
  132.       });
  133.      
  134.       if (item1.options.speed.x || item1.options.speed.y) {
  135.         item1.moveTo(item1.options.position.x + item1.options.speed.x * (self.options.interval/1000),
  136.                item1.options.position.y + item1.options.speed.y * (self.options.interval/1000));
  137.       }
  138.     });
  139.   },
  140.  
  141.   draw: function() {
  142.     this.items.each(function(item) { item.draw(); });
  143.   },
  144.  
  145.   addRandom: function() {
  146.     this.addItem({
  147.       position: { x: Random.get(100,500), y: Random.get(100,400) },
  148.       color: 'rgb('+Random.get(0,255)+','+Random.get(0,255)+','+Random.get(0,255)+')'
  149.     });
  150.   }
  151. });
  152.  
  153. window.addEvent('domready', function() {
  154.   window.system = new ItemController({autostart: true, interval: 50});
  155.   for (var i=0;i<20;i++)
  156.     system.addRandom();
  157.  
  158.   $('btnAddRandom').addEvent('click', function(){
  159.     system.addRandom();
  160.   });
  161. });

The code is very similar to the Gravity demo, although in this case the attraction becomes repulsion.

3 responses so far