diff --git a/Gruntfile.js b/Gruntfile.js index 6ad5763..5ef4473 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -35,7 +35,10 @@ module.exports = function(grunt) { , qunit: { all: { options: { - urls: ['http://localhost:3000/test/browser/int17.html'] + urls: [ + 'http://localhost:3000/test/browser/int17.html' + , 'http://localhost:3000/test/browser/jquery.html' + ] } } } diff --git a/README.md b/README.md index ecac67e..20a31db 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ It can be used normally in any browser as well as in the [node.js][] environment * [Miscellaneous](#miscellaneous-1) * [Locale Files](#locale-files) * [Attributes](#attributes) +* [jQuery](#jquery) * [Express](#express) * [Bugs](#bugs) * [Questions](#questions) @@ -692,6 +693,27 @@ The attribute value contains semi-colon separated values.

``` +## jQuery + +Due to the huge popularity of [jQuery][] it deserves our full support, especially it can really +simplify common usage. Take the JavaScript in the original browser example: + +``` javascript +$.int17({ locale: 'en-GB' }).done(function () { + $(document).int17(); +}); +``` + +As you may have noticed, we're using [jQuery.Deferred][] here, but you can also use simple callback +functions as well. For convenience, the [Internationalization](#internationalization) instance that +is created by the call to `$.int17` is passed as an argument to whatever callback mechanism is +used. + +`$.int17` accepts the same [options](#options) as the core initialization methods, but it also +supports an additional `int17` option which, when specified, will result in the plugin reusing that +instance. However, you must ensure that the value of `int17` is initialized before calling +`$.fn.int17`. + ## Express If you love [Express][] as much as I do, you'll be happy to know that you don't have to do any @@ -762,5 +784,7 @@ http://neocotic.com/int17 [express]: http://expressjs.com [int17]: http://neocotic.com/int17 [internationalization and localization]: http://en.wikipedia.org/wiki/Internationalization_and_localization +[jquery]: http://jquery.com +[jquery.deferred]: http://api.jquery.com/category/deferred-object/ [json]: http://www.json.org [node.js]: http://nodejs.org diff --git a/lib/jquery.int17.js b/lib/jquery.int17.js index f158c59..3539897 100644 --- a/lib/jquery.int17.js +++ b/lib/jquery.int17.js @@ -4,8 +4,6 @@ // For all details and documentation: // -// TODO: Create unit tests - (function () { 'use strict'; @@ -16,32 +14,66 @@ // Convient shortcuts to the global `jQuery` and `int17` instances. var $ = this.jQuery , int17 = this.int17 - // Quick reference to core prototype functions. - , slice = Array.prototype.slice; + // Scoped reference to the current instance. + , i18n = null; + + // Plugin + // ------ // None shall pass!.. unless jQuery has been loaded. if (!$) return; - // Plugin - // ------ + // Save the previous values of jQuery's `$.int17` and `$.fn.int17` extensions. + var old = $.int17 + , oldFn = $.fn.int17; - // TODO: Document - // TODO: Add more actions - var actions = { - init: function(options) { - // TODO: Complete - // TODO: Support named instances - // TODO: Support async (deferred?) - int17.create().initSync(options); - return this; + // Create and initialize a new instance asynchronously with the specified `options`. + // `$.Deferred` and a `callback` function are both supported methods of being notified upon + // completion of the initialization. + // However, if `options` contains `int17`, then that instance will be used. + $.int17 = function(options, callback) { + var deferred = $.Deferred(); + if (typeof options === 'object' && options && options.int17) { + i18n = typeof options.int17 === 'string' ? int17.create(options.int17) : options.int17; + if (callback) callback(null, i18n); + deferred.resolve(i18n); + } else { + i18n = int17.create(); + i18n.init(options, function (err) { + if (err) { + if (callback) callback(err); + deferred.reject(err); + } else { + if (callback) callback(null, i18n); + deferred.resolve(i18n); + } + }); } + return deferred.promise(); + }; + + // Run the int17 jQuery plugin in *noConflict* mode, returning jQuery's `$.int17` extension to + // it's previous owner. + // Returns a reference to `$.int17`. + $.int17.noConflict = function() { + $.int17 = old; + return this; + }; + + // Traverse the children of each selected element and handle all recognized int17 attributes + // accordingly. + $.fn.int17 = function() { + return this.each(function () { + i18n.traverse(this); + }); }; - // TODO: Document - $.fn.int17 = function(action) { - action = actions[action]; - var args = slice.call(arguments, 1); - return action ? action.apply(this, args) : this; + // Run the int17 jQuery plugin in *noConflict* mode, returning jQuery's `$.fn.int17` extension to + // it's previous owner. + // Returns a reference to `$.fn.int17`. + $.fn.int17.noConflict = function() { + $.fn.int17 = oldFn; + return this; }; }).call(this); diff --git a/test/browser/jquery.html b/test/browser/jquery.html new file mode 100644 index 0000000..afb3d6a --- /dev/null +++ b/test/browser/jquery.html @@ -0,0 +1,38 @@ + + + + + jQuery Plugin Test Suite + + + + + + + + + +

jQuery Plugin Test Suite

+

+
+

+
    +
    + + + \ No newline at end of file diff --git a/test/browser/jquery_test.js b/test/browser/jquery_test.js new file mode 100644 index 0000000..645ffd4 --- /dev/null +++ b/test/browser/jquery_test.js @@ -0,0 +1,144 @@ +'use strict'; + +asyncTest('jQuery:callback', 12, function (test) { + var opts = { + clean: true + , encoding: 'UTF-8' + , extension: '.js' + , fallback: false + , fileName: 'msgs' + , folders: true + , ignoreCase: false + , locale: ['fr', 'BE'] + , path: '../fixtures/locales3' + }; + $.int17(opts, function (err, inst) { + test.ok(!err, 'Error was thrown'); + test.ok(inst, 'Instance was not created'); + test.ok(inst.messenger.messages, 'No messages were loaded'); + helpers.strictContains(test, inst.messenger, opts, 'Options were not set correctly'); + start(); + }); +}); + +asyncTest('jQuery:callback:int17', 2, function (test) { + var inst = int17.create() + , opts = { + extension: '.js' + , fileName: 'msgs' + , folders: true + , locale: ['fr', 'BE'] + , path: '../fixtures/locales3' + }; + inst.initSync(opts); + $.int17({ int17: inst }, function (err, i18n) { + test.ok(!err, 'Error was thrown'); + test.strictEqual(i18n, inst, 'Instance was not reused'); + start(); + }); +}); + +asyncTest('jQuery:callback:int17:name', 2, function (test) { + var inst = int17.create('foo') + , opts = { + extension: '.js' + , fileName: 'msgs' + , folders: true + , locale: ['fr', 'BE'] + , path: '../fixtures/locales3' + }; + inst.initSync(opts); + $.int17({ int17: 'foo' }, function (err, i18n) { + test.ok(!err, 'Error was thrown'); + test.strictEqual(i18n, inst, 'Instance was not reused'); + start(); + }); +}); + +asyncTest('jQuery:deferred', 11, function (test) { + var opts = { + clean: true + , encoding: 'UTF-8' + , extension: '.js' + , fallback: false + , fileName: 'msgs' + , folders: true + , ignoreCase: false + , locale: ['fr', 'BE'] + , path: '../fixtures/locales3' + }; + $.int17(opts).done(function (inst) { + test.ok(inst, 'Instance was not created'); + test.ok(inst.messenger.messages, 'No messages were loaded'); + helpers.strictContains(test, inst.messenger, opts, 'Options were not set correctly'); + start(); + }); +}); + +asyncTest('jQuery:deferred:int17', 1, function (test) { + var inst = int17.create() + , opts = { + extension: '.js' + , fileName: 'msgs' + , folders: true + , locale: ['fr', 'BE'] + , path: '../fixtures/locales3' + }; + inst.initSync(opts); + $.int17({ int17: inst }).done(function (i18n) { + test.strictEqual(i18n, inst, 'Instance was not reused'); + start(); + }); +}); + +asyncTest('jQuery:deferred:int17:name', 1, function (test) { + var inst = int17.create('foo') + , opts = { + extension: '.js' + , fileName: 'msgs' + , folders: true + , locale: ['fr', 'BE'] + , path: '../fixtures/locales3' + }; + inst.initSync(opts); + $.int17({ int17: 'foo' }).done(function (i18n) { + test.strictEqual(i18n, inst, 'Instance was not reused'); + start(); + }); +}); + +asyncTest('jQuery.fn', 5, function (test) { + var reset = helpers.resetter('.test1'); + $.int17({ locale: 'en', path: '../fixtures/locales1' }).done(function () { + $('#int17 .test1').int17(); + helpers.htmlEqual(test, '.test1 .e1', 'test1m'); + helpers.htmlEqual(test, '.test1 .e2', '' + + 'test2m $1 $1 $2'); + helpers.htmlEqual(test, '.test1 .e3', '' + + 'test2m a1 a1 a2'); + helpers.htmlEqual(test, '.test1 .e4', 'test1m'); + helpers.htmlEqual(test, '.test1 .e5', ''); + reset(); + start(); + }); +}); + +asyncTest('jQuery.fn:clean', 5, function (test) { + var reset = helpers.resetter('.test2'); + $.int17({ clean: true, locale: 'en', path: '../fixtures/locales1' }).done(function () { + $('#int17 .test2').int17(); + helpers.htmlEqual(test, '.test2 .e1', 'test1m'); + helpers.htmlEqual(test, '.test2 .e2', 'test2m $1 $1 $2'); + helpers.htmlEqual(test, '.test2 .e3', 'test2m a1 a1 a2'); + helpers.htmlEqual(test, '.test2 .e4', 'test1m'); + helpers.htmlEqual(test, '.test2 .e5', ''); + reset(); + start(); + }); +});