You searched for: 'native'

Monday, July 28th, 2008

What’s the Fastest Way to Code a Loop in JavaScript?

Category: Browsers, JavaScript, Performance

Gregory Reimer, frontend engineer for, has written a barrage of tests to answer the question What’s the Fastest Way to Code a Loop in JavaScript? specifically for large data sets:

I built a loop benchmarking test suite for different ways of coding loops in JavaScript. There are a few of these out there already, but I didn’t find any that acknowledged the difference between native arrays and HTML collections. Since the underlying implementations are different (HTML collections for example lack the pop() and slice() methods, etc), benchmarks that don’t test against both are probably missing important information.

My suspicions were confirmed. Accessing the length property is more expensive on HTML collections than on arrays, depending on the browser. In those cases, caching it made a huge difference. However, HTML collections are live, so a cached value may fail if the underlying DOM is modified during looping. On the other hand, HTML collections will never be sparse, so the best way to loop an HTML collection might just be to ignore the length property altogether and combine the test with the item lookup, since you have to do that anyway:


  1. // looping a dom html collection
  2. for (var i=0, node; node = hColl[i++];) {
  3.     // do something with node
  4. }

If you take a look at the results you will see that in general, reverse while loops are the fastest way to iterate a basic collection, e.g.:


  1. var i = arr.length; while (i--) {}

Take a peak at the test suite.

Posted by Dion Almaer at 10:15 am

4.2 rating from 32 votes

Greg Murray, Ryan Johnson join Aptana to work on Jaxer; ORM and MVC are starter projects

Category: Aptana

Paul Colton posted that Aptana has a couple of new recruits in Greg Murray and Ryan Johnson.

Greg Murray was the Ajax guy at Sun, created jMaki, and did a lot of work in organizations such as the Open Ajax Alliance. Ryan Johnson is the creator of the livepipe and object.event Ajax libraries. What are they doing?

Greg’s primary role will be creating increasingly robust application frameworks for Jaxer, Aptana’s open source Ajax server product. Ryan has already been working on MVC concepts for Jaxer and will be collaborating with Greg and the rest of the Jaxer team to drive that and other great concepts for Jaxer forward.

The timing could not be better. Greg and Ryan have joined up just before we get the Jaxer 1.0 release candidate out the door to the whole community (it’s just a matter of days now). This puts us in a great position to start working on some of the next things that’ll be in store for Jaxer in the coming months — and there are some great ideas brewing. For example, Greg and Ryan have already been collaborating with the Jaxer team to create an ActiveRecord-like JavaScript ORM for Jaxer that promises to make working with JavaScript data a pleasure — since it’ll all be native JavaScript!

Posted by Dion Almaer at 10:07 am

3.5 rating from 26 votes

Friday, July 25th, 2008

HTML 5 Linking all around the horn

Category: HTML, Standards

Eric Meyer has been working on an HTML 5 linking proposal and has put together a demonstration of his proposal.

His linking demo shows how you could put href="..." on paragraphs, table rows, cells, and many other places.

This demo is done with simple JavaScript for now, with the obvious hope that browsers natively support these features in the future.

Posted by Dion Almaer at 7:51 am

3.7 rating from 19 votes

Wednesday, July 23rd, 2008

Getting to know GWT JSNI; Including talking to GWT code from JavaScript

Category: GWT, JavaScript

Bruce Johnson has written an expansive post on understanding the GWT JavaScript Native Interface (JSNI). It starts out with the piece that some people know about, namely inlining native JavaScript such as this:

  1. // Java method declaration...
  2. native String flipName(String name) /*-{
  3.   // ...implemented with JavaScript
  4.   var re = /(\w+)\s(\w+)/;
  5.   return name.replace(re, '$2, $1');
  6. }-*/;

But what about calling back out to Java from within native land?

  1. package;
  2. public class Flipper {
  3.   public native void flipName(String name) /*-{
  4.     var re = /(\w+)\s(\w+)/;
  5.     var s = name.replace(re, '$2, $1');
  7.   }-*/;
  9.   private void onFlip(String flippedName) {
  10.     // do something useful with the flipped name
  11.   }
  12. }

You can also access any JavaScript, loaded from a script source or however via:

  1. // A Java method using JSNI
  2. native void sayHelloInJava(String name) /*-{
  3.   $wnd.sayHello(name); // $wnd is a JSNI synonym for 'window'
  4. }-*/;

But finally, what about if you wrote a bunch of Java code for GWT, and you want JavaScript to call that? Simply link the code back through the $wnd world:

  1. package org.example.yourcode.format.client;
  2. public class DateFormatterLib implements EntryPoint {
  4.   // Expose the following method into JavaScript.
  5.   private static String formatAsCurrency(double x) {
  6.     return NumberFormat.getCurrencyFormat().format(x);
  7.   }
  9.   // Set up the JS-callable signature as a global JS function.
  10.   private native void publish() /*-{
  11.     $wnd.formatAsCurrency =
  12.       @org.example.yourcode.format.client.DateFormatterLib::formatAsCurrency(D);
  13.   }-*/;
  15.   // Auto-publish the method into JS when the GWT module loads.
  16.   public void onModuleLoad() {
  17.     publish();
  18.   }
  19. }

For more, check out this talk on the topic given by the GWT architect Scott Blum:

Posted by Dion Almaer at 6:43 am
Comment here

3.9 rating from 21 votes

Friday, July 18th, 2008

Reduce, Reuse, Recycle…. your code

With a young son, I often listen to the “Reduce, Reuse, Recycle” song by Jack Johnson for the Curious George movie. Edgar Hassler has taken that axiom and applied it to code.

Edgar goes into a lot of detail, including the following issue that he ran into:

The problem I kept having was that I needed to pass information from PHP’s environment to JavaScript imported into an XHTML document, and in that JavaScript document, I needed to reference other resources that were relative to the script file. CSS gets this right in that references to images in external CSS documents are read relative to the CSS document path. JavaScript, surprise, does no such thing. Plus, some scripts need to occur at certain locations in the document (which can be surmounted by the event stack in IE or the event queue in Firefox; if you feel the urge to cut yourself, you’re doing it right.) Finally, some scripts must be included before others, but I didn’t want to throw errors when someone imported one component before another.

We built something called Axon, which, for lack of a better description, allows for something like Java’s import statements. A package definition file (there can be any number of them) describes resource names like “Synapse.Forms.Validation” and maps them to required resource names (dependencies) and resource paths associated to channels. A channel was the end resource type, such as “CSS” or “JavaScript”, and mapped onto the Loader to manage which things are imported. Further, Axon recursively met the dependencies to ensure things entered the environment in the proper order. Lastly, we added a service definition so that a resource could describe itself as providing, say, “sendEmail”, and allowed the loader to be queried for a service.

Axon “worked” in that it made using our messaging bus system and form validation code easy. We used it everywhere. It failed in that we never used services, and no one remembers if caching is on or off, causing comically bad situations where bug fixes don’t work or, alternatively, performance tanks. Both great options.

and then concludes:

For PHP developers especially, notice that as we make our code more meaningful ad face, we may be sacrificing speed on the processor but we’re gaining huge amounts of development time, bug tracking time and fees for busted keyboards and restraining order hearings. That’s the hard sell—as a developer we have to deal with costs, and as PHP developers we have the advantage of being on an already “slow” interpreted language rather than writing assembly into our C++ for speed.

By focusing on reuse we gain savings directly and indirectly, as reusable components serve as a vocabulary to discuss a project.  We all feel the need for it, but it’s difficult to secure a way to ensure reuse. In an effort to sum up, I am suggesting requiring the following in your own projects:

  • Require that concerns be addressed one domain at a time, and composite those solutions as needed
  • Focus on writing semantic code (for which the meaning is obvious from its structure)
  • Document your code and include example use cases
  • Documentation should always include a description of what you are trying to achieve as well as what you are actually achieving with your design.  Be honest
  • Use composition over inheritence when adding features to multiple classes from the past, present and future (mainly when spanning different codebases)
  • Allow for high level uses via factories
  • Allow for low level uses via constructors and factories.
    (When PHP 5.3 lands, don’t let your constructors be public)
  • When something will be constant for a long time, provide a place to put that data once and reuse it, don’t demand it each time
  • Talk to people who will be developing with this piece of code, or pretend you are them, or actually be them and learn what concerns they have
  • Imagine refactoring an older project to use this code. Ask yourself what problems you forsee, and how can you make design decisions now to prevent these problems?
  • Release your code to a community that can use it (and ensure they can use it by releasing it at GPL3 or better.) Encourage feedback and participation
  • Drink a lot of vodka and red bull and black out when you code so that you can approach it with a fresh mind the following day
  • Request comments at every stage,  examine the gap between what they expect and what you’re planning on building, but ignore feature requests that would cause you to drift off target.  Do not try to complete components all at once, rather let it be something to which you come back.  Don’t rush it, just let it happen, baby
  • Do not fear trading small amounts of performance for semantic structure. You’re not developing mainframes or nuclear stockpile simulators, you’re building web apps in PHP and more often than not the most expensive thing is you, so pamper yourself
  • Be able to say that something is not a good candidate for re-usability.  If, over the course of time, you begin to notice yourself re-writing it again and again in a way that can be abstracted, then go for it, but it’s okay if not everything is part of a library
  • Be willing to use (or willing to consider using) existing libraries for reusable components.  It will get you in the mindset of your community and provides you with a bunch of tools you don’t have to build yourself

If you do these things, you’ll get rich quick working from home.  No, not really, you won’t, at least you probably won’t, because the results I’ve cited are not typical.

In fact, in the spirit of this article I hope any reader who makes it to this point writes a post on their own experience—what’s worked and what’s failed.  My psychic abilities tell me that several of you read this and said “whoa, that’s a recipie for a performance disaster,”  and when projects explode in popularity this criticism is most certainly true.  My “Highly Available Enterprise Application Architecture with PHP” post is twice as long and three times as ornery.  Also it is entirely written in Tamarian allegory regarding Darmok and Jalad.  But with Thrift, Smarty-like templates, EC2, a smart caching strategy, APC, memcached and a little shuffling of resources you can rebound.

As he says, what are your thoughts with respect to your code?

Posted by Dion Almaer at 7:08 am

4.1 rating from 20 votes

Thursday, July 17th, 2008

IEPNGFix 2: Now supports CSS background position and repeat

Category: CSS, IE

Ah the age old IEPNGFix solution to the problem that we had with IE 5.5 / 6.0 not supporting alpha transparency. The first IEPNGFix solved the problem:

This script adds near-native PNG support with alpha opacity to IE 5.5 and 6. Now you can have full translucency and no more ugly grey borders! It requires only one line in your CSS file, and no changes to your website HTML. <IMG> tags and background images are both supported.

Now we have a new version that adds the ability to use CSS1 compatible background position and repeat.

Posted by Dion Almaer at 4:20 am

4.5 rating from 42 votes

Friday, July 11th, 2008

Remember the Web Apps; Don’t forget the first iPhone baby today

Category: iPhone, JavaScript

We see the birth of the second baby when it comes to building and running apps on the iPhone. People have already spent almost $100k in the first few hours of the AppStore not being open, so tomorrow is likely to be a great day for Apple, and developers, as people run around clicking on buy without thinking of the price.

I spent some time running the applications, and thinking about how different they are to Web versions. For example, when I launch Twitterific vs. a web based Twitter it actually was faster for the Web page to load Safari up with everything. Twitterific also had a strange feature for loading images. As I moved up and down the list the images looked like they were being pushed out of cache which made for a weird experience.

What if the iPhone offered the ability to aggressively cache “applications”. When you open the application it opens its own instance of Safari instead of just linking over to Safari. What if it had access to local storage APIs in WebKit?

The native applications do have benefits. They have access to the camera, addressbook, … well wait, those could be JavaScript APIs too.

There is the tool chain. You can have fine grained performance knowledge of your application with the Objective-C tools, but with SquirrelFish Apple is getting better and better there too. Other nice tooling could work well when constraining the Web interfaces to the iPhone form factor.

What about games? You couldn’t do super monkey ball, or could you if you had a really solid Flash, or feature complete canvas.

The native apps are great, but I am still betting on the Web as a great platform for mobile applications too.

Posted by Dion Almaer at 10:01 am

3 rating from 26 votes

A safety fence for your property lookups

Category: JavaScript, Tip

Michal Till posted a little JavaScript tip that he uses to create a safety fence for accessing properties:

As we all know, null not only does not have any properties, but their existence also can not be tested. So retuns error instead of undefined.

You can end up with something like this:


  1. if (
  2. (node) &&
  3. (node.nextSibling) &&
  4. (node.nextSibling.className == ...)
  5. ... ) {
  6.    ...

There is a neat trick that can simplify the boolean expression. We might supply an empty object {} or zero (0) as an alternative:


  1. if ( next = (node || 0).nextSibling) ) {

Posted by Dion Almaer at 6:39 am

3 rating from 32 votes

Tuesday, July 8th, 2008

Ext GWT 1.0: GWT 1.5 support, new APIs, performance, and docs

Category: GWT, Sencha

Darrell Meyer has announced Ext GWT 1.0. This is the first fully stable release of the product and it includes a lot of goodies including:

  • Documentation: new screencasts of the various steps
  • GWT 1.5 support: “Ext GWT is a 100% native GWT application written in Java. Ext GWT does not wrap any 3rd party JavaScript and does not use any external JavaScript files. Ext GWT fully leverages the GWT API including the widget lifecylce, events, listeners, messaging, and RPC.”
  • “Performance was a high priority item for the Ext GWT 1.0 release. Many changes were made since the first beta releases. Initial rendering times are quicker and the new layout code reacts quicker to window resizing. Improvements can easily be seen in the Explorer demo.”
  • Advanced Form Layouts
  • Improved Data Loading, Store, Binder, and Field API

Looks like a very solid release indeed. Congrats to the team.

Posted by Dion Almaer at 8:15 am

4.3 rating from 81 votes

Monday, July 7th, 2008

LLVM and running C as well as Python in the browser

Category: JavaScript, Python

Dan Morrill doesn’t like JavaScript 2. His reasoning is a little like the folks who don’t want Java.Next to try to copy features from every other language, but to just be the best static language, and let other languages like Scala, Groovy, JRuby (and the hundreds of others like Fan) go in different directions on the same Java platform.

You could argue the same for the browser platform. Why push JavaScript 2 further than cleaning it up, and instead allow other languages to augment it.

This is where technology such as IronMonkey come in, as well as the work that Scott Peterson is doing, written up here:

Scott Petersen from Adobe gave a talk at Mozilla on a toolchain he’s been creating—soon to be open-sourced—that allows C code to be targeted to the Tamarin virtual machine. Aside from being a really interesting piece of technology, I thought its implications for the web were pretty impressive.

If I followed his presentation right, Petersen’s toolchain works something like this:

  1. A special version of the GNU C Compiler—possibly llvm-gcc—compiles C code into instructions for the Low Level Virtual Machine.
  2. The LLVM instructions are converted into opcodes for a custom Virtual Machine that runs in ActionScript, a variant of ECMAScript and sibling of JavaScript.
  3. The ActionScript is automatically compiled into Tamarin bytecode by Adobe Flash, which may be further compiled into native machine language by Tamarin’s Just-in-Time (JIT) compiler.

The toolchain includes lots of other details, such as a custom POSIX system call API and a C multimedia library that provides access to Flash. And there’s some things that Petersen had to add to Tamarin, such as a native byte array that maps directly to RAM, thereby allowing the VM’s “emulation” of memory to have only a minor overhead over the real thing.

The end result is the ability to run a wide variety of existing C code in Flash at acceptable speeds. Petersen demonstrated a version of Quake running in a Flash app, as well as a C-based Nintendo emulator running Zelda; both were eminently playable, and included sound effects and music.

Posted by Dion Almaer at 8:50 am

4 rating from 23 votes

Thursday, July 3rd, 2008

ratproxy: Rat out those security issues in your Web app

Category: Security

Michal Zalewski, of Google, has released ratproxy, a tool to test your Web application against attacks such as XSS and XSRF:

Ratproxy is a semi-automated, largely passive web application security audit tool. It is meant to complement active crawlers and manual proxies more commonly used for this task, and is optimized specifically for an accurate and sensitive detection, and automatic annotation, of potential problems and security-relevant design patterns based on the observation of existing, user-initiated traffic in complex web 2.0 environments. The approach taken with ratproxy offers several important advantages over more traditional methods:

What about other solutions?

There are numerous alternative proxy tools meant to aid security auditors – most notably WebScarab, Paros, Burp, ProxMon, and Pantera. Stick with whatever suits your needs, as long as you get the data you need in the format you like.

That said, ratproxy is there for a reason. It is designed specifically to deliver concise reports that focus on prioritized issues of clear relevance to contemporary web 2.0 applications, and to do so in a hands-off, repeatable manner. It should not overwhelm you with raw HTTP traffic dumps, and it goes far beyond simply providing a framework to tamper with the application by hand.

Ratproxy implements a number of fairly advanced and unique checks based on our experience with these applications, as well as all the related browser quirks and content handling oddities. It features a sophisticated content-sniffing functionality capable of distinguishing between stylesheets and Javascript code snippets, supports SSL man-in-the-middle, on the fly Flash ActionScript? decompilation, and even offers an option to confirm high-likelihood flaw candidates with very lightweight, a built-in active testing module.

Last but not least, if you are undecided, the proxy may be easily chained with third-party security testing proxies of your choice.

Posted by Dion Almaer at 10:49 am
Comment here

3.8 rating from 19 votes

JavaScript Plugins; The beauty of loosely coupled code

Category: JavaScript

James Coglan wrote a piece on There is no such thing as a JavaScript plugin that uses jQuery as a use case for how simple it is to have a plugin contract.

When you think about plugins in many environments, you have strict contracts through interfaces that you have to implement. With jQuery, you can just add on to the jQuery prototype, normally seen through the alias $.fn.

James shows the simple example of:


  1. $.fn.makeThemRed = function() {
  2.   this.css({color: 'red'});
  3.   return this;
  4. };
  6. $('p').makeThemRed();

I read this as the power of the simplicity, but John Resig, again in his three parter saw it a little different, I think by thinking too much about the title :)

I will contend that such a thing as plugins exist and are logically distinct from “random JavaScript code that manipulates other JavaScript code” as long as the following points are met:

  1. There have to be explicit points upon which a plugin can attach. James notes the most common one in jQuery (jQuery.fn) but we have tons more – events, animations, selectors – all over the board for developers to snap in to.
  2. Even more importantly: Those points have to be documented or, at the very least, be under some sort of agreement that they will be treated like a normal piece of the user-facing API. In jQuery we treat all plugin extension points as “user-facing API” and only ever change them in major releases (if at all) and always provide an alternative for authors to use.
  3. Finally, there has to be some sort of repository for navigating these plugins. This is a huge differentiator. Simply referring to “code in the wild” as plugins doesn’t really cut it if there’s no commitment to hosting them and keeping their documentation and examples alive.

Posted by Dion Almaer at 10:16 am

3.5 rating from 23 votes

Tuesday, July 1st, 2008

State of Ajax for June 2008: Apple flexes Open Web muscles

Category: Roundup

June was a great month for the Open Web. First, Apple delivered a one-two punch with showing Mobile Me, powered by the native Web and SproutCore, and showing SquirrelFish as JavaScript starts to get a loooot faster on browsers. Firefox had a party as millions of people downloaded Firefox 3 final release, and immediately talked about 3.1 coming soon. The flywheel is moving. Opera 9.5 is also there, and IE 8 beta 2 is coming in August.

Velocity, the performance conference, also showed the interest in making the Web faster, as many tools were announced to help out us devs. We also saw a lot of cool uses of Canvas/SVG, as developers delve low level and see that they actually work very well.

So, we sit at the crux of two paths. On the one hand, browsers are getting faster and faster and adding great new technology for us (including small things like CSS variables. finally!). On the other hand, we are creating more compelling user experiences (e.g. 280 Slides, Mobile Me). These forces work with each other. As we do cooler apps that push the boundaries, the browsers have to come back with better performance and tools to match. Expectations are changing, and we need to match them.

Here is the full roundup:










Gears, AIR, and more

Design: CSS, SVG, Canvas



Posted by Dion Almaer at 5:07 pm

3.7 rating from 18 votes

MooWheel and MooCirclePack for visualizations

Category: JavaScript, MooTools

MooWheel, the JavaScript connections visualization library, has been updated to version 0.2.

Updates include:

  • New data format
  • Text can now be hovered over, in addition to the dot
  • Images can be added for each item

You can see the popular Twitter example
(thanks to Augsto Becciu, creator of TweetWheel).

Also, MooCirclePack has just been released:

MooCirclePack is another stunning visualization library that brings circle packing to JavaScript. It is great for data that can be represented by size (eg: an alternative to a bar graph), or data that can be represented amorphously.

There is a non-Ajax demo, and an Ajax one.


Posted by Dion Almaer at 11:02 am
Comment here

3.8 rating from 30 votes

Wednesday, June 25th, 2008

flXHR: Flash based XHR from flensed

Category: Ajax, Flash, JavaScript, Library

Kyle Simpson has announced a new family of opensource projects called flensed and the first project being flXHR which “utilizes javascript+flash to create a complete, literal drop-in replacement (by being API identical) for the native browser XHR (Ajax) communication mechanism. However, flXHR uses Flash Player’s security model to enable direct cross-domain communication, and also has a number of other very helpful extensions.”

There are a number of demos which illustrate how it easy it is to take the API-compatible flXHR and swap it to any of your favorite JS frameworks (Dojo, Prototype, YUI, ExtJS, etc) in place of its usage of native XHR… once the simple adapt/swap happens, everything else about the framework library communication works the same, because flXHR speaks the same familiar protocol and API, and so it really is what I like to call “set it and forget it” good.

Hasn’t this been done before?

There have been several other attempts at similar things before, including SWFHttpRequest, FlashXMLHttpRequest, Fjax, and F4A. However, all those fell short of the mark in different ways. On my site, there are comparison charts and detailed FAQ’s which show how flXHR stands up to these predecessors, and exceeds them in very important and powerful ways. I believe flXHR has accomplished its goal, which was to be *the* complete solution for SWF-based Ajax calls as an identical API-compatible drop-in replacement for native XHR, not to mention many helpful improvements including robust error callback handling, timeouts, and convenience configuration functions, to name a few.

Posted by Dion Almaer at 9:53 am

4.1 rating from 45 votes

Thursday, June 19th, 2008

iWidgets Public Beta: Impressive Widget Builder

Category: Examples, jQuery, Social Networks, Widgets

Joining with the Web 2.0 “go-meta” business model that’s so popular these days, iWidgets provides a service that lets you build widgets once and deploy them to various popular widget APIs and platforms.

At the heart of iWidgets is a “PowerPoint-like” widget builder that takes strong design cues from Yahoo! Pipes, but as Peter Yared (CEO of iWidgets) says:

then again Pipes looks a lot like Prograph (the original dataflow programming company, where I worked from 1992-1995)

The site transforms the widget you build into:

a native FBML or GoogleGadget/OpenSocial, which are two completely different architectures (serverside vs. clientside), and then call the destination-specific API’s for things like persistence whenever possible.

The builder was constructed using jQuery and like Pipes renders the connection lines using <canvas>. Peter shared some of the back story behind building the application:

I wrote the initial builder in [another framework] and found it obtuse. After spending literally a week trying to turn the date picker into a color picker, I threw in the towel. A friend of mine turned me on to jQuery and I fell in love with how clean and fast it was, the way it separates the HTML from JavaScript is beautiful. So I rewrote the builder we had at the time in jQuery in a two week coding session! Soon after that I got funding from Opus Capital, and when I looked to hire people, I found 3 out of 4 of our engineers through the jQuery mailing list. It’s funny how things like that work out; I ended up finding total rockstars because they were playing with a cool new library.

Peter also shared some details on the overall development process:

It took us 10 months to build the site, we have had one guy (Richard Hensel) full time on the builder UI with another guy (Jeremy Stanton) on it about 1/3 time, with the rest of his time on the overall site doing the wizards and getting things like the %#$# back button to work. Then there are two guys working on the backend, one on FBML and the other on Gadget/OpenSocial, and we just added a junior AJAX guy. We contribute back where we can, for example Jeremy contributed to the Selectable in jQuery UI. The iWidgets site itself is completely AJAX, data from the server is sent as JSON, and page fragments are rendered with Wicket. We persist the widgets you build as JSON and then transform them on the server into native Facebook, MySpace, iGoogle, etc.

The team has some future plans building on top of an already very cool site:

We have a bunch of features we are rolling out, such as calling JSON and piping the results into different components, repeating table, paging, cut/copy/paste, z-order control, live dataflows in the builder, a background process at amazon that sends out notifications like “joe just shot 2 over par at pebble beach” to newsfeeds to drive virality, more platforms such as the iPhone, etc.

I wonder if this should somehow integrate with Yahoo Widgets, Apple’s Dashboard, and other desktop widget platforms… maybe emit a bundle you can download and install into these services?

Posted by Ben Galbraith at 6:00 am

4.2 rating from 46 votes