NodeJS library for loading Wireless Universal Resource File (wurfl) xml file efficiently into memory for use in applications that need to lookup mobile device capabilities by their user agent.
wurfl.js is a node.js library to make it easy to use wurfl. What is wurfl? From the site http://wurfl.sourceforge.net/:
WURFL is a Device Description Repository (DDR), i.e. a softwarecomponent which contains the descriptions of thousands of mobiledevices. In its simplest incarnation, WURFL is an XMLconfiguration file plus a set of programming APIs to access thedata in real-time environments.
npm install wurfl
Basic usage allows getting up and running quickly.
var wurfl = require'wurfl';wurflloadSync;// wurfl xml file is finished loading into a memory-efficient structurevar userAgent = 'Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; O2 Xda 2s;PPC;240x320; PPC; 240x320)';var deviceInfo = wurflgetuserAgent;console.logdeviceInfoproduct_infomodel_name; // "Xda IIs"
You may want to use your own (read "updated") wurfl.xml file. This is easy:
var wurflFile = __dirname + '/wurfl.xml';wurflloadSyncwurflFile;
Loading and parsing a large xml file can be take awhile and be CPU-heavy. You may use load() to asynchronously load the xml file:
wurflload// ready to use, wurfl.get(userAgent);;
Or with your own xml file:
wurflloadmyWurflFile// my own file;
When using your own file, it would be great to update the file without needing to restart the node.js server:
When updating using watch it will always be asynchronous. The data structure will be updated automatically when the file is changed. You may also be notified when that happens if you want to know when it gets updated:
wurflwatchmyWurflFileconsole.log'XML file updated';;
If you want to reduce memory usage even further, you can specify the the groups you are interested in, the rest will be left out completely. For example, if you only care about the "display" capabilities:
wurflloadSync groups: 'display' ;
When specifying groups the "file" parameter needs to be an options hash with
groups and optionally
file. You can use this in loadSync(options), load(options, callback), and watch(options, callback).
var options = file: __dirname + '/wurfl.xml' groups: 'display' ;wurflloadSyncoptions;wurflwatchoptions;
The most common use-case is to load the file up when your script starts synchronously since you need it to exist before you start querying against it, but then add a watch to asynchronously update it later:
var wurfl = require'wurfl';var wurflFile = __dirname + '/wurfl.xml';wurflloadSyncwurflFile;wurflwatchmyWurflFile;var userAgent = 'Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; O2 Xda 2s;PPC;240x320; PPC; 240x320)';var deviceInfo = wurflgetuserAgent;console.logdeviceInfomodel_name; // "Xda IIs"
That's all for the API:
To see what properties are available in the info object you get back from wurfl.get() look at the wurfl.xml file. Each group is an object on info (e.g. info.css is a group) and each capability is a property on the group (e.g. info.css.css_spriting is a capability).
The devices in the wurfl.xml file "inherit" from other devices, specified in the XML by the fall_back attribute. In order to optimize for memory and not duplicate the base properties over and over again for every single device, I've structured the groups to have their own class that extends the fall_back group class. I've set all the capabilites for a group on its prototype object. And for groups that weren't overridden, the device just points to its fall_back device's group. This way, when you access a group's capability it will pull it from the group's prototype object or from further up the prototype chain back to the top-level group instance.
In addition to optimizing for memory I've also added a worker using node-webworkers to load and parse the XML file in a separate process from the main script. This allows the wurfl data to be loaded and/or updated without blocking a large chunk of time from your main script. This can be up to several seconds to load and parse the XML file. The worker is lazily created however, so if you only use loadSync() and get() it won't create the worker.