Skip to content

Commit

Permalink
completed jquery plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
neocotic committed Jun 10, 2013
1 parent ae918d8 commit 0b4fd53
Show file tree
Hide file tree
Showing 5 changed files with 262 additions and 21 deletions.
5 changes: 4 additions & 1 deletion Gruntfile.js
Expand Up @@ -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'
]
}
}
}
Expand Down
24 changes: 24 additions & 0 deletions README.md
Expand Up @@ -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)
Expand Down Expand Up @@ -692,6 +693,27 @@ The attribute value contains semi-colon separated values.
<p i18n-subs="World" i18n-content="welcome"></p>
```

## 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
Expand Down Expand Up @@ -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
72 changes: 52 additions & 20 deletions lib/jquery.int17.js
Expand Up @@ -4,8 +4,6 @@
// For all details and documentation:
// <http://neocotic.com/int17>

// TODO: Create unit tests

(function () {

'use strict';
Expand All @@ -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);
38 changes: 38 additions & 0 deletions test/browser/jquery.html
@@ -0,0 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>jQuery Plugin Test Suite</title>
<link rel="stylesheet" href="vendor/qunit.css" media="screen">
<script src="vendor/qunit.js"></script>
<script src="vendor/jquery.min.js"></script>
<script src="../../lib/int17.js"></script>
<script src="../../lib/jquery.int17.js"></script>
<script src="../helpers.js"></script>
<script src="jquery_test.js"></script>
</head>
<body>
<h1 id="qunit-header">jQuery Plugin Test Suite</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="qunit-fixture"></div>
<div id="int17" style="display: none">
<div class="test1">
<a class="e1" i18n-content="test1"></a>
<a class="e2" data-i18n-content="test2"></a>
<a class="e3" i18n-subs="a1;a2" i18n-content="test2"></a>
<a class="e4" data-i18n-subs="a1;a2" i18n-values="title:test2;.style.direction:dir;.innerHTML:test4"></a>
<select class="e5" i18n-options="testOpt1:-1;testOpt2;testOpt3"></select>
</div>
<div class="test2">
<a class="e1" data-i18n-content="test1"></a>
<a class="e2" i18n-content="test2"></a>
<a class="e3" data-i18n-subs="a1;a2" data-i18n-content="test2"></a>
<a class="e4" i18n-subs="a1;a2" data-i18n-values="title:test2;.style.direction:dir;.innerHTML:test4"></a>
<select class="e5" data-i18n-options="testOpt1:-1;testOpt2;testOpt3"></select>
</div>
</div>
</body>
</html>
144 changes: 144 additions & 0 deletions 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', '<a class="e1" i18n-content="test1">test1m</a>');
helpers.htmlEqual(test, '.test1 .e2', '<a class="e2" data-i18n-content="test2">' +
'test2m $1 $1 $2</a>');
helpers.htmlEqual(test, '.test1 .e3', '<a class="e3" i18n-subs="a1;a2" i18n-content="test2">' +
'test2m a1 a1 a2</a>');
helpers.htmlEqual(test, '.test1 .e4', '<a class="e4" data-i18n-subs="a1;a2" ' +
'i18n-values="title:test2;.style.direction:dir;.innerHTML:test4" title="test2m a1 a1 a2" ' +
'style="direction: ltr; "><span i18n-content="test1">test1m</span></a>');
helpers.htmlEqual(test, '.test1 .e5', '<select class="e5" ' +
'i18n-options="testOpt1:-1;testOpt2;testOpt3"><option value="-1">option1</option>' +
'<option>option2</option><option>option3</option></select>');
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', '<a class="e1">test1m</a>');
helpers.htmlEqual(test, '.test2 .e2', '<a class="e2">test2m $1 $1 $2</a>');
helpers.htmlEqual(test, '.test2 .e3', '<a class="e3">test2m a1 a1 a2</a>');
helpers.htmlEqual(test, '.test2 .e4', '<a class="e4" title="test2m a1 a1 a2" ' +
'style="direction: ltr; "><span>test1m</span></a>');
helpers.htmlEqual(test, '.test2 .e5', '<select class="e5"><option value="-1">option1</option>' +
'<option>option2</option><option>option3</option></select>');
reset();
start();
});
});

0 comments on commit 0b4fd53

Please sign in to comment.