From 4e926aa58185094ae7e37456823e6edeba8caad2 Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 1 Feb 2013 12:37:11 +0100 Subject: [PATCH 1/4] reformatted code --- README.markdown | 325 ++++++++++++++++++------------------- build/README | 7 - build/jquery.localize.js | 178 -------------------- builder | 2 - src/jquery.localize.coffee | 130 --------------- src/jquery.localize.js | 285 ++++++++++++++++++++++++++++++++ 6 files changed, 445 insertions(+), 482 deletions(-) delete mode 100644 build/README delete mode 100644 build/jquery.localize.js delete mode 100755 builder delete mode 100644 src/jquery.localize.coffee create mode 100644 src/jquery.localize.js diff --git a/README.markdown b/README.markdown index e31065c..c4b4255 100644 --- a/README.markdown +++ b/README.markdown @@ -1,165 +1,160 @@ -# jquery.localize.js - -## a jQuery plugin that makes it easy to i18n your static web site. - -## Synopsis - -* Lazily loads JSON translation files based on a simple naming convention. -* By default, applies the translations to your document based on simple attribute convention. -* Recently updated for jQuery 1.7 (tests use jQuery 1.7.2) - -## Basic Usage - -## Step 0. Load the jquery-localize plugin on your page. - -It's the file located at `build/jquery.localize.js` - -## Step 1. Mark up tags whose content you want to be translated - -Somewhere in your html: - -

Hello!

- -## Step 2. Provide a JSON language file that has translations: - -example-fr.json: - - { - "greeting": "Bonjour!" - } - -## Step 3. Use the localize plugin. - - // In a browser where the language is set to French - $("[data-localize]").localize("example") - - // You can also override the language detection, and pass in a language code - $("[data-localize]").localize("example", { language: "fr" }) - -# Gory Details - -## Language file loading - -The first argument of the localize method is the name of the language pack. You might have a different language pack for different parts of your website. - -Here's an example of loading several language packs: - - $("[data-localize]") - .localize("header") - .localize("sidebar") - .localize("footer") - - -If the language of the browser were set to "fr", then the plugin would try to load: - -* header-fr.json -* sidebar-fr.json -* footer-fr.json - -if the language of the browser also had a country code, like "fr-FR", then the plugin would ALSO try to load: - -* header-fr-FR.json -* sidebar-fr-FR.json -* footer-fr-FR.json - -This let's you define partial language refinements for different regions. For instance, you can have the base language translation file for a language that translates 100 different phrases, and for countries were maybe a some of those phrases would be out of place, you can just provide a country-specific file with _just those special phrases_ defined. - -## Skipping Languages (aka Optimizing for My Language) - -This is useful if you've got a default language. For example, if all of your content is served in english, then you probably don't want the overhead of loading up unecessary (and probably non-existant) english langauge packs (foo-en.json) - -You can tell the localize plugin to always skip certain languages using the skipLanguage option: - - # using a string will skip ONLY if the language code matches exactly - # this would prevent loading only if the language was "en-US" - $("[data-localize]").localize("example", { skipLanguage: "en-US" }) - - # using a regex will skip if the regex matches - # this would prevent loading of any english language translations - $("[data-localize]").localize("example", { skipLanguage: /^en/ }) - - # using an array of strings will skip if any of the strings matches exactly - $("[data-localize]").localize("example", { skipLanguage: ["en", "en-US"] }) - -## Applying the language file - -If you rely on the default callback and use the "data-localize" attribute then the changes will be applied for you. - -## Examples: - -**HTML:** - -

Tracker Pro XT Deluxe

-

Search...

-

Go!

-

Use at your own risk.

-

Dashboard

-

Bug List

-

Logout

- -**application-es.json (fake spanish)** - - { - "title": "Tracker Pro XT Deluxo", - "search": { - "placeholder": "Searcho...", - "button": "Vamos!" - }, - "footer": { - "disclaimer": "Bewaro." - }, - "menu": { - "dashboard": "Dashboardo", - "list": "Bug Listo", - "logout": "Exito" - } - } - -**Localize it!** - - $("[data-localize]").localize("application", { language: "es" }) - -## Callbacks - -You can provide a callback if you want to augment or replace the default callback provided by the plugin. Your callback should take at least 1 argument: the language data (contents of your json file). It can optionally accept a second argument, which is a reference to the default callback function. This is handy if you still want the default behavior, but also need to do something else with the language data. - - $("[data-localize]").localize("application", { - language: "es", - callback: function(data, defaultCallback){ - data.title = data.title + currentBugName(); - defaultCallback(data) - } - }) - -See the test/samples for working examples. - -# Contributing - -This plugin is written in [CoffeeScript](http://jashkenas.github.com/coffee-script/). -The included `builder` script will run `coffee` with the necessary flags to -automatically update the compiled javascript in the build/ directory any time you -save changes to the coffee code under src/. - -If you're interested in contributing, please fork the [repository](https://github.com/coderifous/jquery-localize), -make your changes, and send pull-requests. - -Tests use QUnit. Run them by serving from the root of the project and -navigating to the test/ directory, which has an index.html that takes it from -there. - -Learn more about [how to fork](http://help.github.com/fork-a-repo/) and -[pull-requests](http://help.github.com/pull-requests/). - -# Credits & Licensing - -Copyright (c) Jim Garvin (http://github.com/coderifous), 2008. - -Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses. - -Written by Jim Garvin (@coderifous) for use on LMGTFY.com. -Please use it, and contribute changes. - -http://github.com/coderifous/jquery-localize - -Based off of Keith Wood's Localisation jQuery plugin. -http://keith-wood.name/localisation.html +# jquery.localize.js + +## a jQuery plugin that makes it easy to i18n your static web site. + +## Synopsis + +* Lazily loads JSON translation files based on a simple naming convention. +* By default, applies the translations to your document based on simple attribute convention. +* Recently updated for jQuery 1.7 (tests use jQuery 1.7.2) + +## Basic Usage + +## Step 0. Load the jquery-localize plugin on your page. + +It's the file located at `build/jquery.localize.js` + +## Step 1. Mark up tags whose content you want to be translated + +Somewhere in your html: + +

Hello!

+ +## Step 2. Provide a JSON language file that has translations: + +example-fr.json: + + { + "greeting": "Bonjour!" + } + +## Step 3. Use the localize plugin. + + // In a browser where the language is set to French + $("[data-localize]").localize("example") + + // You can also override the language detection, and pass in a language code + $("[data-localize]").localize("example", { language: "fr" }) + +# Gory Details + +## Language file loading + +The first argument of the localize method is the name of the language pack. You might have a different language pack for different parts of your website. + +Here's an example of loading several language packs: + + $("[data-localize]") + .localize("header") + .localize("sidebar") + .localize("footer") + + +If the language of the browser were set to "fr", then the plugin would try to load: + +* header-fr.json +* sidebar-fr.json +* footer-fr.json + +if the language of the browser also had a country code, like "fr-FR", then the plugin would ALSO try to load: + +* header-fr-FR.json +* sidebar-fr-FR.json +* footer-fr-FR.json + +This let's you define partial language refinements for different regions. For instance, you can have the base language translation file for a language that translates 100 different phrases, and for countries were maybe a some of those phrases would be out of place, you can just provide a country-specific file with _just those special phrases_ defined. + +## Skipping Languages (aka Optimizing for My Language) + +This is useful if you've got a default language. For example, if all of your content is served in english, then you probably don't want the overhead of loading up unecessary (and probably non-existant) english langauge packs (foo-en.json) + +You can tell the localize plugin to always skip certain languages using the skipLanguage option: + + # using a string will skip ONLY if the language code matches exactly + # this would prevent loading only if the language was "en-US" + $("[data-localize]").localize("example", { skipLanguage: "en-US" }) + + # using a regex will skip if the regex matches + # this would prevent loading of any english language translations + $("[data-localize]").localize("example", { skipLanguage: /^en/ }) + + # using an array of strings will skip if any of the strings matches exactly + $("[data-localize]").localize("example", { skipLanguage: ["en", "en-US"] }) + +## Applying the language file + +If you rely on the default callback and use the "data-localize" attribute then the changes will be applied for you. + +## Examples: + +**HTML:** + +

Tracker Pro XT Deluxe

+

Search...

+

Go!

+

Use at your own risk.

+

Dashboard

+

Bug List

+

Logout

+ +**application-es.json (fake spanish)** + + { + "title": "Tracker Pro XT Deluxo", + "search": { + "placeholder": "Searcho...", + "button": "Vamos!" + }, + "footer": { + "disclaimer": "Bewaro." + }, + "menu": { + "dashboard": "Dashboardo", + "list": "Bug Listo", + "logout": "Exito" + } + } + +**Localize it!** + + $("[data-localize]").localize("application", { language: "es" }) + +## Callbacks + +You can provide a callback if you want to augment or replace the default callback provided by the plugin. Your callback should take at least 1 argument: the language data (contents of your json file). It can optionally accept a second argument, which is a reference to the default callback function. This is handy if you still want the default behavior, but also need to do something else with the language data. + + $("[data-localize]").localize("application", { + language: "es", + callback: function(data, defaultCallback){ + data.title = data.title + currentBugName(); + defaultCallback(data) + } + }) + +See the test/samples for working examples. + +# Contributing + +If you're interested in contributing, please fork the [repository](https://github.com/alexwebgr/jquery-localize), +make your changes, and send pull-requests. + +Tests use QUnit. Run them by serving from the root of the project and +navigating to the test/ directory, which has an index.html that takes it from +there. + +Learn more about [how to fork](http://help.github.com/fork-a-repo/) and +[pull-requests](http://help.github.com/pull-requests/). + +# Credits & Licensing + +Copyright (c) Jim Garvin (http://github.com/coderifous), 2008. + +Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses. + +Written by Jim Garvin (@coderifous) for use on LMGTFY.com. +Please use it, and contribute changes. + +http://github.com/coderifous/jquery-localize + +Based off of Keith Wood's Localisation jQuery plugin. +http://keith-wood.name/localisation.html diff --git a/build/README b/build/README deleted file mode 100644 index f395898..0000000 --- a/build/README +++ /dev/null @@ -1,7 +0,0 @@ -Files in this directory are generated. - -Do not edit any files in this directory. - -These files are only checked in to git for the convenience of people that want -to use the javascript library without having to download the repository and -build it on their machine. diff --git a/build/jquery.localize.js b/build/jquery.localize.js deleted file mode 100644 index 439c6e2..0000000 --- a/build/jquery.localize.js +++ /dev/null @@ -1,178 +0,0 @@ -// Generated by CoffeeScript 1.3.3 -(function() { - var $, normaliseLang; - - $ = jQuery; - - normaliseLang = function(lang) { - lang = lang.replace(/_/, '-').toLowerCase(); - if (lang.length > 3) { - lang = lang.substring(0, 3) + lang.substring(3).toUpperCase(); - } - return lang; - }; - - $.defaultLanguage = normaliseLang(navigator.language || navigator.userLanguage); - - $.localize = function(pkg, options) { - var defaultCallback, fileExtension, intermediateLangData, jsonCall, lang, loadLanguage, localizeElement, localizeForSpecialKeys, localizeImageElement, localizeInputElement, localizeOptgroupElement, notifyDelegateLanguageLoaded, regexify, setAttrFromValueForKey, setTextFromValueForKey, valueForKey, wrappedSet; - if (options == null) { - options = {}; - } - wrappedSet = this; - intermediateLangData = {}; - fileExtension = options.fileExtension || "json"; - loadLanguage = function(pkg, lang, level) { - var file; - if (level == null) { - level = 1; - } - switch (level) { - case 1: - intermediateLangData = {}; - if (options.loadBase) { - file = pkg + ("." + fileExtension); - return jsonCall(file, pkg, lang, level); - } else { - return loadLanguage(pkg, lang, 2); - } - break; - case 2: - if (lang.length >= 2) { - file = "" + pkg + "-" + (lang.substring(0, 2)) + "." + fileExtension; - return jsonCall(file, pkg, lang, level); - } - break; - case 3: - if (lang.length >= 5) { - file = "" + pkg + "-" + (lang.substring(0, 5)) + "." + fileExtension; - return jsonCall(file, pkg, lang, level); - } - } - }; - jsonCall = function(file, pkg, lang, level) { - var ajaxOptions, successFunc; - if (options.pathPrefix != null) { - file = "" + options.pathPrefix + "/" + file; - } - successFunc = function(d) { - $.extend(intermediateLangData, d); - notifyDelegateLanguageLoaded(intermediateLangData); - return loadLanguage(pkg, lang, level + 1); - }; - ajaxOptions = { - url: file, - dataType: "json", - async: false, - timeout: options.timeout != null ? options.timeout : 500, - success: successFunc - }; - if (window.location.protocol === "file:") { - ajaxOptions.error = function(xhr) { - return successFunc($.parseJSON(xhr.responseText)); - }; - } - return $.ajax(ajaxOptions); - }; - notifyDelegateLanguageLoaded = function(data) { - if (options.callback != null) { - return options.callback(data, defaultCallback); - } else { - return defaultCallback(data); - } - }; - defaultCallback = function(data) { - $.localize.data[pkg] = data; - return wrappedSet.each(function() { - var elem, key, value; - elem = $(this); - key = elem.data("localize"); - key || (key = elem.attr("rel").match(/localize\[(.*?)\]/)[1]); - value = valueForKey(key, data); - return localizeElement(elem, key, value); - }); - }; - localizeElement = function(elem, key, value) { - if (elem.is('input')) { - localizeInputElement(elem, key, value); - } else if (elem.is('img')) { - localizeImageElement(elem, key, value); - } else if (elem.is('optgroup')) { - localizeOptgroupElement(elem, key, value); - } else if (!$.isPlainObject(value)) { - elem.html(value); - } - if ($.isPlainObject(value)) { - return localizeForSpecialKeys(elem, value); - } - }; - localizeInputElement = function(elem, key, value) { - if (elem.is("[placeholder]")) { - return elem.attr("placeholder", value); - } else { - return elem.val(value); - } - }; - localizeForSpecialKeys = function(elem, value) { - setAttrFromValueForKey(elem, "title", value); - return setTextFromValueForKey(elem, "text", value); - }; - localizeOptgroupElement = function(elem, key, value) { - return elem.attr("label", value); - }; - localizeImageElement = function(elem, key, value) { - setAttrFromValueForKey(elem, "alt", value); - return setAttrFromValueForKey(elem, "src", value); - }; - valueForKey = function(key, data) { - var keys, value, _i, _len; - keys = key.split(/\./); - value = data; - for (_i = 0, _len = keys.length; _i < _len; _i++) { - key = keys[_i]; - value = value != null ? value[key] : null; - } - return value; - }; - setAttrFromValueForKey = function(elem, key, value) { - value = valueForKey(key, value); - if (value != null) { - return elem.attr(key, value); - } - }; - setTextFromValueForKey = function(elem, key, value) { - value = valueForKey(key, value); - if (value != null) { - return elem.text(value); - } - }; - regexify = function(string_or_regex_or_array) { - var thing; - if (typeof string_or_regex_or_array === "string") { - return "^" + string_or_regex_or_array + "$"; - } else if (string_or_regex_or_array.length != null) { - return ((function() { - var _i, _len, _results; - _results = []; - for (_i = 0, _len = string_or_regex_or_array.length; _i < _len; _i++) { - thing = string_or_regex_or_array[_i]; - _results.push(regexify(thing)); - } - return _results; - })()).join("|"); - } else { - return string_or_regex_or_array; - } - }; - lang = normaliseLang(options.language ? options.language : $.defaultLanguage); - if (!(options.skipLanguage && lang.match(regexify(options.skipLanguage)))) { - loadLanguage(pkg, lang, 1); - } - return wrappedSet; - }; - - $.fn.localize = $.localize; - - $.localize.data = {}; - -}).call(this); diff --git a/builder b/builder deleted file mode 100755 index 5c0fcc2..0000000 --- a/builder +++ /dev/null @@ -1,2 +0,0 @@ -coffee -wco build src/*.coffee & -coffee -wc test/*.coffee & diff --git a/src/jquery.localize.coffee b/src/jquery.localize.coffee deleted file mode 100644 index 7eb5cdf..0000000 --- a/src/jquery.localize.coffee +++ /dev/null @@ -1,130 +0,0 @@ -# Copyright (c) Jim Garvin (http://github.com/coderifous), 2008. -# Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses. -# Written by Jim Garvin (@coderifous) for use on LMGTFY.com. -# http://github.com/coderifous/jquery-localize -# Based off of Keith Wood's Localisation jQuery plugin. -# http://keith-wood.name/localisation.html - -$ = jQuery - -# Ensures language code is in the format aa-AA. -normaliseLang = (lang) -> - lang = lang.replace(/_/, '-').toLowerCase() - if lang.length > 3 - lang = lang.substring(0, 3) + lang.substring(3).toUpperCase() - lang - -# Mozilla uses .language, IE uses .userLanguage -$.defaultLanguage = normaliseLang(navigator.language || navigator.userLanguage) - -$.localize = (pkg, options = {}) -> - wrappedSet = this - intermediateLangData = {} - fileExtension = options.fileExtension || "json" - - loadLanguage = (pkg, lang, level = 1) -> - switch level - when 1 - intermediateLangData = {} - if options.loadBase - file = pkg + ".#{fileExtension}" - jsonCall(file, pkg, lang, level) - else - loadLanguage(pkg, lang, 2) - when 2 - if lang.length >= 2 - file = "#{pkg}-#{lang.substring(0, 2)}.#{fileExtension}" - jsonCall(file, pkg, lang, level) - when 3 - if lang.length >= 5 - file = "#{pkg}-#{lang.substring(0, 5)}.#{fileExtension}" - jsonCall(file, pkg, lang, level) - - jsonCall = (file, pkg, lang, level) -> - file = "#{options.pathPrefix}/#{file}" if options.pathPrefix? - successFunc = (d) -> - $.extend(intermediateLangData, d) - notifyDelegateLanguageLoaded(intermediateLangData) - loadLanguage(pkg, lang, level + 1) - ajaxOptions = - url: file - dataType: "json" - async: false - timeout: if options.timeout? then options.timeout else 500 - success: successFunc - # hack to work with serving from local file system. - # local file:// urls won't work in chrome: - # http://code.google.com/p/chromium/issues/detail?id=40787 - if window.location.protocol == "file:" - ajaxOptions.error = (xhr) -> successFunc($.parseJSON(xhr.responseText)) - $.ajax(ajaxOptions) - - notifyDelegateLanguageLoaded = (data) -> - if options.callback? - options.callback(data, defaultCallback) - else - defaultCallback(data) - - defaultCallback = (data) -> - $.localize.data[pkg] = data - wrappedSet.each -> - elem = $(this) - key = elem.data("localize") - key ||= elem.attr("rel").match(/localize\[(.*?)\]/)[1] - value = valueForKey(key, data) - localizeElement(elem, key, value) - - localizeElement = (elem, key, value) -> - if elem.is('input') then localizeInputElement(elem, key, value) - else if elem.is('img') then localizeImageElement(elem, key, value) - else if elem.is('optgroup') then localizeOptgroupElement(elem, key, value) - else unless $.isPlainObject(value) then elem.html(value) - localizeForSpecialKeys(elem, value) if $.isPlainObject(value) - - localizeInputElement = (elem, key, value) -> - if elem.is("[placeholder]") - elem.attr("placeholder", value) - else - elem.val(value) - - localizeForSpecialKeys = (elem, value) -> - setAttrFromValueForKey(elem, "title", value) - setTextFromValueForKey(elem, "text", value) - - localizeOptgroupElement = (elem, key, value) -> - elem.attr("label", value) - - localizeImageElement = (elem, key, value) -> - setAttrFromValueForKey(elem, "alt", value) - setAttrFromValueForKey(elem, "src", value) - - valueForKey = (key, data) -> - keys = key.split(/\./) - value = data - for key in keys - value = if value? then value[key] else null - value - - setAttrFromValueForKey = (elem, key, value) -> - value = valueForKey(key, value) - elem.attr(key, value) if value? - - setTextFromValueForKey = (elem, key, value) -> - value = valueForKey(key, value) - elem.text(value) if value? - - regexify = (string_or_regex_or_array) -> - if typeof(string_or_regex_or_array) == "string" - "^" + string_or_regex_or_array + "$" - else if string_or_regex_or_array.length? - (regexify(thing) for thing in string_or_regex_or_array).join("|") - else - string_or_regex_or_array - - lang = normaliseLang(if options.language then options.language else $.defaultLanguage) - loadLanguage(pkg, lang, 1) unless (options.skipLanguage && lang.match(regexify(options.skipLanguage))) - - wrappedSet - -$.fn.localize = $.localize -$.localize.data = {} diff --git a/src/jquery.localize.js b/src/jquery.localize.js new file mode 100644 index 0000000..28fc1aa --- /dev/null +++ b/src/jquery.localize.js @@ -0,0 +1,285 @@ +/** + * @url https://github.com/alexwebgr/jquery-localize + * @desc simple plugin for localization using data-localize attributes and json files + */ +(function () +{ + var $,normaliseLang; + + $ = jQuery; + + normaliseLang = function (lang) + { + lang = lang.replace(/_/, '-').toLowerCase(); + + if (lang.length > 3) + lang = lang.substring(0, 3) + lang.substring(3).toUpperCase(); + + return lang; + }; + + $.defaultLanguage = normaliseLang(navigator.language || navigator.userLanguage); + + $.localize = function (pkg, options) + { + var defaultCallback, + fileExtension, + intermediateLangData, + jsonCall, + lang, + loadLanguage, + localizeElement, + localizeForSpecialKeys, + localizeImageElement, + localizeInputElement, + localizeOptgroupElement, + notifyDelegateLanguageLoaded, + regexify, + setAttrFromValueForKey, + setTextFromValueForKey, + valueForKey, + wrappedSet + ; + + if (options == null) + { + options = {}; + } + wrappedSet = this; + intermediateLangData = {}; + fileExtension = options.fileExtension || "json"; + loadLanguage = function (pkg, lang, level) + { + var file; + + if (level == null) + level = 1; + + switch (level) + { + case 1: + intermediateLangData = {}; + if (options.loadBase) + { + file = pkg + ("." + fileExtension); + + return jsonCall(file, pkg, lang, level); + } + else + { + return loadLanguage(pkg, lang, 2); + } + break; + case 2: + if (lang.length >= 2) + { + file = "" + pkg + "-" + (lang.substring(0, 2)) + "." + fileExtension; + + return jsonCall(file, pkg, lang, level); + } + break; + case 3: + if (lang.length >= 5) + { + file = "" + pkg + "-" + (lang.substring(0, 5)) + "." + fileExtension; + + return jsonCall(file, pkg, lang, level); + } + break; + } + }; + + jsonCall = function (file, pkg, lang, level) + { + var ajaxOptions, successFunc; + + if (options.pathPrefix != null) + file = "" + options.pathPrefix + "/" + file; + + successFunc = function (d) + { + $.extend(intermediateLangData, d); + notifyDelegateLanguageLoaded(intermediateLangData); + + return loadLanguage(pkg, lang, level + 1); + }; + + ajaxOptions = + { + url:file, + dataType:"json", + async:false, + timeout:options.timeout != null ? options.timeout : 500, + success:successFunc + }; + + if (window.location.protocol === "file:") + { + ajaxOptions.error = function (xhr) + { + return successFunc($.parseJSON(xhr.responseText)); + }; + } + + return $.ajax(ajaxOptions); + }; + + notifyDelegateLanguageLoaded = function (data) + { + if (options.callback != null) + return options.callback(data, defaultCallback); + else + return defaultCallback(data); + }; + + defaultCallback = function (data) + { + $.localize.data[pkg] = data; + + return wrappedSet.each(function () + { + var elem, key, value; + + elem = $(this); + key = elem.data("localize"); + key || (key = elem.attr("rel").match(/localize\[(.*?)\]/)[1]); + value = valueForKey(key, data); + + return localizeElement(elem, key, value); + }); + }; + + localizeElement = function (elem, key, value) + { + if (elem.is('input')) + { + localizeInputElement(elem, key, value); + } + else if (elem.is('img')) + { + localizeImageElement(elem, key, value); + } + else if (elem.is('optgroup')) + { + localizeOptgroupElement(elem, key, value); + } + else if (!$.isPlainObject(value)) + { + elem.html(value); + } + if ($.isPlainObject(value)) + { + return localizeForSpecialKeys(elem, value); + } + }; + + localizeInputElement = function (elem, key, value) + { + if (elem.is("[placeholder]")) + { + return elem.attr("placeholder", value); + } + else + { + return elem.val(value); + } + }; + + localizeForSpecialKeys = function (elem, value) + { + setAttrFromValueForKey(elem, "title", value); + + return setTextFromValueForKey(elem, "text", value); + }; + + localizeOptgroupElement = function (elem, key, value) + { + return elem.attr("label", value); + }; + + localizeImageElement = function (elem, key, value) + { + setAttrFromValueForKey(elem, "alt", value); + + return setAttrFromValueForKey(elem, "src", value); + }; + + valueForKey = function (key, data) + { + var keys, value, _i, _len; + + keys = key.split(/\./); + value = data; + + for (_i = 0, _len = keys.length; _i < _len; _i++) + { + key = keys[_i]; + value = value != null ? value[key] : null; + } + + return value; + }; + + setAttrFromValueForKey = function (elem, key, value) + { + value = valueForKey(key, value); + + if (value != null) + { + return elem.attr(key, value); + } + }; + + setTextFromValueForKey = function (elem, key, value) + { + value = valueForKey(key, value); + + if (value != null) + { + return elem.text(value); + } + }; + + regexify = function (string_or_regex_or_array) + { + var thing; + + if (typeof string_or_regex_or_array === "string") + { + return "^" + string_or_regex_or_array + "$"; + } + else if (string_or_regex_or_array.length != null) + { + return ((function () + { + var _i, _len, _results; + + _results = []; + + for (_i = 0, _len = string_or_regex_or_array.length; _i < _len; _i++) + { + thing = string_or_regex_or_array[_i]; + _results.push(regexify(thing)); + } + + return _results; + })()).join("|"); + } + else + { + return string_or_regex_or_array; + } + }; + + lang = normaliseLang(options.language ? options.language : $.defaultLanguage); + + if (!(options.skipLanguage && lang.match(regexify(options.skipLanguage)))) + loadLanguage(pkg, lang, 1); + + return wrappedSet; + }; + + $.fn.localize = $.localize; + $.localize.data = {}; + +}).call(this); From 3bd21966f1500da2e536adfc3fc6777afe0f7ff6 Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 1 Feb 2013 14:41:57 +0100 Subject: [PATCH 2/4] refactored examples --- README.markdown | 4 +- src/jquery.localize.js | 57 +- test/examples/absolute_prefix_path.html | 56 +- test/examples/basic_usage.html | 72 +- test/examples/custom_callback_usage.html | 71 +- test/examples/language_and_country.html | 54 +- test/examples/skip_language.html | 68 +- test/index.html | 66 +- test/lang/custom/test-fo.json | 4 +- test/lang/test-en-US.json | 4 +- test/lang/test-en.json | 2 +- test/lang/test-ja-XX.json | 2 +- test/lang/test-ja.json | 36 +- test/localize_test.coffee | 274 +- test/vendor/qunit.css | 526 ++-- test/vendor/qunit.js | 3052 ++++++++++++---------- 16 files changed, 2284 insertions(+), 2064 deletions(-) diff --git a/README.markdown b/README.markdown index c4b4255..ed5649b 100644 --- a/README.markdown +++ b/README.markdown @@ -1,6 +1,6 @@ # jquery.localize.js -## a jQuery plugin that makes it easy to i18n your static web site. +## a jQuery plugin that makes it easy to localize your static web site. ## Synopsis @@ -12,7 +12,7 @@ ## Step 0. Load the jquery-localize plugin on your page. -It's the file located at `build/jquery.localize.js` +It's the file located at `src/jquery.localize.js` ## Step 1. Mark up tags whose content you want to be translated diff --git a/src/jquery.localize.js b/src/jquery.localize.js index 28fc1aa..a21daf9 100644 --- a/src/jquery.localize.js +++ b/src/jquery.localize.js @@ -4,22 +4,16 @@ */ (function () { - var $,normaliseLang; - + var $, normaliseLang; $ = jQuery; - normaliseLang = function (lang) { lang = lang.replace(/_/, '-').toLowerCase(); - if (lang.length > 3) lang = lang.substring(0, 3) + lang.substring(3).toUpperCase(); - return lang; }; - $.defaultLanguage = normaliseLang(navigator.language || navigator.userLanguage); - $.localize = function (pkg, options) { var defaultCallback, @@ -40,7 +34,6 @@ valueForKey, wrappedSet ; - if (options == null) { options = {}; @@ -51,10 +44,8 @@ loadLanguage = function (pkg, lang, level) { var file; - if (level == null) level = 1; - switch (level) { case 1: @@ -62,48 +53,40 @@ if (options.loadBase) { file = pkg + ("." + fileExtension); - return jsonCall(file, pkg, lang, level); } else { return loadLanguage(pkg, lang, 2); } - break; + break; case 2: if (lang.length >= 2) { file = "" + pkg + "-" + (lang.substring(0, 2)) + "." + fileExtension; - return jsonCall(file, pkg, lang, level); } - break; + break; case 3: if (lang.length >= 5) { file = "" + pkg + "-" + (lang.substring(0, 5)) + "." + fileExtension; - return jsonCall(file, pkg, lang, level); } - break; + break; } }; - jsonCall = function (file, pkg, lang, level) { var ajaxOptions, successFunc; - if (options.pathPrefix != null) file = "" + options.pathPrefix + "/" + file; - successFunc = function (d) { $.extend(intermediateLangData, d); notifyDelegateLanguageLoaded(intermediateLangData); - return loadLanguage(pkg, lang, level + 1); }; - ajaxOptions = { url:file, @@ -112,7 +95,6 @@ timeout:options.timeout != null ? options.timeout : 500, success:successFunc }; - if (window.location.protocol === "file:") { ajaxOptions.error = function (xhr) @@ -120,10 +102,8 @@ return successFunc($.parseJSON(xhr.responseText)); }; } - return $.ajax(ajaxOptions); }; - notifyDelegateLanguageLoaded = function (data) { if (options.callback != null) @@ -131,24 +111,19 @@ else return defaultCallback(data); }; - defaultCallback = function (data) { $.localize.data[pkg] = data; - return wrappedSet.each(function () { var elem, key, value; - elem = $(this); key = elem.data("localize"); key || (key = elem.attr("rel").match(/localize\[(.*?)\]/)[1]); value = valueForKey(key, data); - return localizeElement(elem, key, value); }); }; - localizeElement = function (elem, key, value) { if (elem.is('input')) @@ -172,7 +147,6 @@ return localizeForSpecialKeys(elem, value); } }; - localizeInputElement = function (elem, key, value) { if (elem.is("[placeholder]")) @@ -184,66 +158,51 @@ return elem.val(value); } }; - localizeForSpecialKeys = function (elem, value) { setAttrFromValueForKey(elem, "title", value); - return setTextFromValueForKey(elem, "text", value); }; - localizeOptgroupElement = function (elem, key, value) { return elem.attr("label", value); }; - localizeImageElement = function (elem, key, value) { setAttrFromValueForKey(elem, "alt", value); - return setAttrFromValueForKey(elem, "src", value); }; - valueForKey = function (key, data) { var keys, value, _i, _len; - keys = key.split(/\./); value = data; - for (_i = 0, _len = keys.length; _i < _len; _i++) { key = keys[_i]; value = value != null ? value[key] : null; } - return value; }; - setAttrFromValueForKey = function (elem, key, value) { value = valueForKey(key, value); - if (value != null) { return elem.attr(key, value); } }; - setTextFromValueForKey = function (elem, key, value) { value = valueForKey(key, value); - if (value != null) { return elem.text(value); } }; - regexify = function (string_or_regex_or_array) { var thing; - if (typeof string_or_regex_or_array === "string") { return "^" + string_or_regex_or_array + "$"; @@ -253,15 +212,12 @@ return ((function () { var _i, _len, _results; - _results = []; - for (_i = 0, _len = string_or_regex_or_array.length; _i < _len; _i++) { thing = string_or_regex_or_array[_i]; _results.push(regexify(thing)); } - return _results; })()).join("|"); } @@ -270,16 +226,11 @@ return string_or_regex_or_array; } }; - lang = normaliseLang(options.language ? options.language : $.defaultLanguage); - if (!(options.skipLanguage && lang.match(regexify(options.skipLanguage)))) loadLanguage(pkg, lang, 1); - return wrappedSet; }; - $.fn.localize = $.localize; $.localize.data = {}; - }).call(this); diff --git a/test/examples/absolute_prefix_path.html b/test/examples/absolute_prefix_path.html index 7ab02fe..a01d37d 100644 --- a/test/examples/absolute_prefix_path.html +++ b/test/examples/absolute_prefix_path.html @@ -1,25 +1,31 @@ - - - - - - - Localize Test - - - - - -

Test localization...

-

puts 2 + 2

-

It failed :(

- - - - + + + + + + Localize Test + + + + + +

Test localization...

+ +

puts 2 + 2

+ +

It failed :(

+ + + + diff --git a/test/examples/basic_usage.html b/test/examples/basic_usage.html index 0b9c6b6..b7bd7ad 100644 --- a/test/examples/basic_usage.html +++ b/test/examples/basic_usage.html @@ -1,35 +1,37 @@ - - - - - - - Localize Test - - - - - -

Test localization...

-

puts 2 + 2

- - -

- a square ruby - Ruby image should be round. -

-

It failed :(

- - - - + + + + + + Localize Test + + + + + +

Test localization...

+ +

puts 2 + 2

+ + + +

+ a square ruby + Ruby image should be round. +

+ +

It failed :(

+ + + + diff --git a/test/examples/custom_callback_usage.html b/test/examples/custom_callback_usage.html index 96af267..480408d 100644 --- a/test/examples/custom_callback_usage.html +++ b/test/examples/custom_callback_usage.html @@ -1,31 +1,40 @@ - - - - - - - Localize Test - - - - - -

Test localization...

-

puts 2 + 2

-

It failed :(

-

Optional callback never happened.

- - - - + + + + + + Localize Test + + + + + +

Test localization...

+ +

puts 2 + 2

+ +

It failed :(

+ +

Optional callback never happened.

+ + + + + diff --git a/test/examples/language_and_country.html b/test/examples/language_and_country.html index 5fb0ab6..871302d 100644 --- a/test/examples/language_and_country.html +++ b/test/examples/language_and_country.html @@ -1,24 +1,30 @@ - - - - - - - Localize Test - - - - - -

Test localization...

-

It failed :(

- - - - + + + + + + Localize Test + + + + + +

Test localization...

+ +

It failed :(

+ + + + + diff --git a/test/examples/skip_language.html b/test/examples/skip_language.html index 54720d3..e678a87 100644 --- a/test/examples/skip_language.html +++ b/test/examples/skip_language.html @@ -1,34 +1,34 @@ - - - - - - - Localize Test - - - - - -

Test localization...

-

Should not load en lang file.

-

Should not load en-US lang file.

- - - - + + + + + + Localize Test + + + + + +

Test localization...

+ +

Should not load en lang file.

+ +

Should not load en-US lang file.

+ + + + + diff --git a/test/index.html b/test/index.html index 225ea16..ada9deb 100644 --- a/test/index.html +++ b/test/index.html @@ -1,32 +1,34 @@ - - - - - Localize Test Suite - - - - - - - - - - - - - - - - - - - -

QUnit Test Suite

-

-
-

-
    - - - + + + + + Localize Test Suite + + + + + + + + + + + + + + + + + + + +

    QUnit Test Suite

    + +

    + +
    +

    +
      + + + diff --git a/test/lang/custom/test-fo.json b/test/lang/custom/test-fo.json index e618232..a42b335 100644 --- a/test/lang/custom/test-fo.json +++ b/test/lang/custom/test-fo.json @@ -1 +1,3 @@ -{ "path_prefix": "pathPrefix success" } +{ + "path_prefix":"pathPrefix success" +} diff --git a/test/lang/test-en-US.json b/test/lang/test-en-US.json index 9c47329..da5fbd1 100644 --- a/test/lang/test-en-US.json +++ b/test/lang/test-en-US.json @@ -1 +1,3 @@ -{ "en_us_message": "en-US loaded" } +{ + "en_us_message":"en-US loaded" +} diff --git a/test/lang/test-en.json b/test/lang/test-en.json index 1ce198a..e6b238e 100644 --- a/test/lang/test-en.json +++ b/test/lang/test-en.json @@ -1 +1 @@ -{ "en_message": "en loaded" } +{ "en_message":"en loaded" } diff --git a/test/lang/test-ja-XX.json b/test/lang/test-ja-XX.json index d6a23f0..c6900ce 100644 --- a/test/lang/test-ja-XX.json +++ b/test/lang/test-ja-XX.json @@ -1 +1 @@ -{ "message": "country code success" } +{ "message":"country code success" } diff --git a/test/lang/test-ja.json b/test/lang/test-ja.json index 7ee33b7..b7d4644 100644 --- a/test/lang/test-ja.json +++ b/test/lang/test-ja.json @@ -1,18 +1,18 @@ -{ - "test": { - "nested": "nested success", - "input": "input success", - "optgroup": "optgroup success", - "option": "option success", - "ruby_image": { - "src": "ruby_round.gif", - "alt": "a round ruby", - "title": "A Round Ruby" - } - }, - "basic": "basic success", - "with_title": { - "text": "with_title text success", - "title": "with_title title success" - } -} +{ + "test":{ + "nested":"nested success", + "input":"input success", + "optgroup":"optgroup success", + "option":"option success", + "ruby_image":{ + "src":"ruby_round.gif", + "alt":"a round ruby", + "title":"A Round Ruby" + } + }, + "basic":"basic success", + "with_title":{ + "text":"with_title text success", + "title":"with_title title success" + } +} diff --git a/test/localize_test.coffee b/test/localize_test.coffee index 3aed237..4804153 100644 --- a/test/localize_test.coffee +++ b/test/localize_test.coffee @@ -1,137 +1,137 @@ -localizableTagWithRel = (tag, localizeKey, attributes) -> - t = $("<#{tag}>").attr("rel", "localize[#{localizeKey}]") - applyTagAttributes(t, attributes) - -localizableTagWithDataLocalize = (tag, localizeKey, attributes) -> - t = $("<#{tag}>").attr("data-localize", localizeKey) - applyTagAttributes(t, attributes) - -applyTagAttributes = (tag, attributes) -> - if attributes.text? - tag.text(attributes.text) - delete attributes.text - if attributes.val? - tag.val(attributes.val) - delete attributes.val - tag.attr(k,v) for k, v of attributes - tag - -module "Basic Usage" - -setup -> - @testOpts = language: "ja", pathPrefix: "lang" - -test "basic tag text substitution", -> - t = localizableTagWithRel("p", "basic", text: "basic fail") - t.localize("test", @testOpts) - equals t.text(), "basic success" - -test "basic tag text substitution using data-localize instead of rel", -> - t = localizableTagWithDataLocalize("p", "basic", text: "basic fail") - t.localize("test", @testOpts) - equals t.text(), "basic success" - -test "basic tag text substitution with nested key", -> - t = localizableTagWithRel("p", "test.nested", text: "nested fail") - t.localize("test", @testOpts) - equals t.text(), "nested success" - -test "basic tag text substitution for special title key", -> - t = localizableTagWithDataLocalize("p", "with_title", text: "with_title element fail", title: "with_title title fail") - t.localize("test", @testOpts) - equals t.text(), "with_title text success" - equals t.attr("title"), "with_title title success" - -test "input tag value substitution", -> - t = localizableTagWithRel("input", "test.input", val: "input fail") - t.localize("test", @testOpts) - equals t.val(), "input success" - -test "input tag placeholder substitution", -> - t = localizableTagWithRel("input", "test.input", placeholder: "placeholder fail") - t.localize("test", @testOpts) - equals t.attr("placeholder"), "input success" - -test "image tag src, alt, and title substitution", -> - t = localizableTagWithRel("img", "test.ruby_image", src: "ruby_square.gif", alt: "a square ruby", title: "A Square Ruby") - t.localize("test", @testOpts) - equals t.attr("src"), "ruby_round.gif" - equals t.attr("alt"), "a round ruby" - equals t.attr("title"), "A Round Ruby" - -test "chained call", -> - t = localizableTagWithRel("p", "basic", text: "basic fail") - t.localize("test", @testOpts).localize("test", @testOpts) - equals t.text(), "basic success" - -test "alternative file extension", -> - t = localizableTagWithRel("p", "basic", text: "basic fail") - t.localize("test", $.extend({ fileExtension: "foo" }, @testOpts)) - equals t.text(), "basic success foo" - -moreSetup -> - @t = $(' - - ') - -test "optgroup tag label substitution", -> - t = @t.find("optgroup") - t.localize("test", @testOpts) - equals t.attr("label"), "optgroup success" - -test "option tag text substitution", -> - t = @t.find("option") - t.localize("test", @testOpts) - equals t.text(), "option success" - -module "Options" - -test "pathPrefix loads lang files from custom path", -> - opts = language: "fo", pathPrefix: "/test/lang/custom" - t = localizableTagWithRel("p", "path_prefix", text: "pathPrefix fail") - t.localize("test", opts) - equals t.text(), "pathPrefix success" - -test "custom callback is fired", -> - opts = language: "ja", pathPrefix: "lang" - opts.callback = (data, defaultCallback) -> - data.custom_callback = "custom callback success" - defaultCallback(data) - t = localizableTagWithRel("p", "custom_callback", text: "custom callback fail") - t.localize("test", opts) - equals t.text(), "custom callback success" - -test "language with country code", -> - opts = language: "ja-XX", pathPrefix: "lang" - t = localizableTagWithRel("p", "message", text: "country code fail") - t.localize("test", opts) - equals t.text(), "country code success" - -module "Language optimization" - -test "skipping language using string match", -> - opts = language: "en", pathPrefix: "lang", skipLanguage: "en" - t = localizableTagWithRel("p", "en_message", text: "en not loaded") - t.localize("test", opts) - equals t.text(), "en not loaded" - -test "skipping language using regex match", -> - opts = language: "en-US", pathPrefix: "lang", skipLanguage: /^en/ - t = localizableTagWithRel("p", "en_us_message", text: "en-US not loaded") - t.localize("test", opts) - equals t.text(), "en-US not loaded" - -test "skipping language using array match", -> - opts = language: "en", pathPrefix: "lang", skipLanguage: ["en", "en-US"] - t = localizableTagWithRel("p", "en_message", text: "en not loaded") - t.localize("test", opts) - equals t.text(), "en not loaded" - - opts = language: "en-US", pathPrefix: "lang", skipLanguage: ["en", "en-US"] - t = localizableTagWithRel("p", "en_us_message", text: "en-US not loaded") - t.localize("test", opts) - equals t.text(), "en-US not loaded" +localizableTagWithRel = (tag, localizeKey, attributes) -> + t = $("<#{tag}>").attr("rel", "localize[#{localizeKey}]") + applyTagAttributes(t, attributes) + +localizableTagWithDataLocalize = (tag, localizeKey, attributes) -> + t = $("<#{tag}>").attr("data-localize", localizeKey) + applyTagAttributes(t, attributes) + +applyTagAttributes = (tag, attributes) -> + if attributes.text? + tag.text(attributes.text) + delete attributes.text + if attributes.val? + tag.val(attributes.val) + delete attributes.val + tag.attr(k, v) for k, v of attributes + tag + +module "Basic Usage" + +setup -> + @testOpts = language: "ja", pathPrefix: "lang" + +test "basic tag text substitution", -> + t = localizableTagWithRel("p", "basic", text: "basic fail") + t.localize("test", @testOpts) + equals t.text(), "basic success" + +test "basic tag text substitution using data-localize instead of rel", -> + t = localizableTagWithDataLocalize("p", "basic", text: "basic fail") + t.localize("test", @testOpts) + equals t.text(), "basic success" + +test "basic tag text substitution with nested key", -> + t = localizableTagWithRel("p", "test.nested", text: "nested fail") + t.localize("test", @testOpts) + equals t.text(), "nested success" + +test "basic tag text substitution for special title key", -> + t = localizableTagWithDataLocalize("p", "with_title", text: "with_title element fail", title: "with_title title fail") + t.localize("test", @testOpts) + equals t.text(), "with_title text success" + equals t.attr("title"), "with_title title success" + +test "input tag value substitution", -> + t = localizableTagWithRel("input", "test.input", val: "input fail") + t.localize("test", @testOpts) + equals t.val(), "input success" + +test "input tag placeholder substitution", -> + t = localizableTagWithRel("input", "test.input", placeholder: "placeholder fail") + t.localize("test", @testOpts) + equals t.attr("placeholder"), "input success" + +test "image tag src, alt, and title substitution", -> + t = localizableTagWithRel("img", "test.ruby_image", src: "ruby_square.gif", alt: "a square ruby", title: "A Square Ruby") + t.localize("test", @testOpts) + equals t.attr("src"), "ruby_round.gif" + equals t.attr("alt"), "a round ruby" + equals t.attr("title"), "A Round Ruby" + +test "chained call", -> + t = localizableTagWithRel("p", "basic", text: "basic fail") + t.localize("test", @testOpts).localize("test", @testOpts) + equals t.text(), "basic success" + +test "alternative file extension", -> + t = localizableTagWithRel("p", "basic", text: "basic fail") + t.localize("test", $.extend({ fileExtension: "foo" }, @testOpts)) + equals t.text(), "basic success foo" + +moreSetup -> + @t = $(' + + ') + +test "optgroup tag label substitution", -> + t = @t.find("optgroup") + t.localize("test", @testOpts) + equals t.attr("label"), "optgroup success" + +test "option tag text substitution", -> + t = @t.find("option") + t.localize("test", @testOpts) + equals t.text(), "option success" + +module "Options" + +test "pathPrefix loads lang files from custom path", -> + opts = language: "fo", pathPrefix: "/test/lang/custom" + t = localizableTagWithRel("p", "path_prefix", text: "pathPrefix fail") + t.localize("test", opts) + equals t.text(), "pathPrefix success" + +test "custom callback is fired", -> + opts = language: "ja", pathPrefix: "lang" + opts.callback = (data, defaultCallback) -> + data.custom_callback = "custom callback success" + defaultCallback(data) + t = localizableTagWithRel("p", "custom_callback", text: "custom callback fail") + t.localize("test", opts) + equals t.text(), "custom callback success" + +test "language with country code", -> + opts = language: "ja-XX", pathPrefix: "lang" + t = localizableTagWithRel("p", "message", text: "country code fail") + t.localize("test", opts) + equals t.text(), "country code success" + +module "Language optimization" + +test "skipping language using string match", -> + opts = language: "en", pathPrefix: "lang", skipLanguage: "en" + t = localizableTagWithRel("p", "en_message", text: "en not loaded") + t.localize("test", opts) + equals t.text(), "en not loaded" + +test "skipping language using regex match", -> + opts = language: "en-US", pathPrefix: "lang", skipLanguage: /^en/ + t = localizableTagWithRel("p", "en_us_message", text: "en-US not loaded") + t.localize("test", opts) + equals t.text(), "en-US not loaded" + +test "skipping language using array match", -> + opts = language: "en", pathPrefix: "lang", skipLanguage: ["en", "en-US"] + t = localizableTagWithRel("p", "en_message", text: "en not loaded") + t.localize("test", opts) + equals t.text(), "en not loaded" + + opts = language: "en-US", pathPrefix: "lang", skipLanguage: ["en", "en-US"] + t = localizableTagWithRel("p", "en_us_message", text: "en-US not loaded") + t.localize("test", opts) + equals t.text(), "en-US not loaded" diff --git a/test/vendor/qunit.css b/test/vendor/qunit.css index 3d08f1a..b9d93e5 100644 --- a/test/vendor/qunit.css +++ b/test/vendor/qunit.css @@ -1,226 +1,300 @@ -/** - * QUnit - A JavaScript Unit Testing Framework - * - * http://docs.jquery.com/QUnit - * - * Copyright (c) 2011 John Resig, Jörn Zaefferer - * Dual licensed under the MIT (MIT-LICENSE.txt) - * or GPL (GPL-LICENSE.txt) licenses. - */ - -/** Font Family and Sizes */ - -#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { - font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; -} - -#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } -#qunit-tests { font-size: smaller; } - - -/** Resets */ - -#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult { - margin: 0; - padding: 0; -} - - -/** Header */ - -#qunit-header { - padding: 0.5em 0 0.5em 1em; - - color: #8699a4; - background-color: #0d3349; - - font-size: 1.5em; - line-height: 1em; - font-weight: normal; - - border-radius: 15px 15px 0 0; - -moz-border-radius: 15px 15px 0 0; - -webkit-border-top-right-radius: 15px; - -webkit-border-top-left-radius: 15px; -} - -#qunit-header a { - text-decoration: none; - color: #c2ccd1; -} - -#qunit-header a:hover, -#qunit-header a:focus { - color: #fff; -} - -#qunit-banner { - height: 5px; -} - -#qunit-testrunner-toolbar { - padding: 0.5em 0 0.5em 2em; - color: #5E740B; - background-color: #eee; -} - -#qunit-userAgent { - padding: 0.5em 0 0.5em 2.5em; - background-color: #2b81af; - color: #fff; - text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; -} - - -/** Tests: Pass/Fail */ - -#qunit-tests { - list-style-position: inside; -} - -#qunit-tests li { - padding: 0.4em 0.5em 0.4em 2.5em; - border-bottom: 1px solid #fff; - list-style-position: inside; -} - -#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { - display: none; -} - -#qunit-tests li strong { - cursor: pointer; -} - -#qunit-tests li a { - padding: 0.5em; - color: #c2ccd1; - text-decoration: none; -} -#qunit-tests li a:hover, -#qunit-tests li a:focus { - color: #000; -} - -#qunit-tests ol { - margin-top: 0.5em; - padding: 0.5em; - - background-color: #fff; - - border-radius: 15px; - -moz-border-radius: 15px; - -webkit-border-radius: 15px; - - box-shadow: inset 0px 2px 13px #999; - -moz-box-shadow: inset 0px 2px 13px #999; - -webkit-box-shadow: inset 0px 2px 13px #999; -} - -#qunit-tests table { - border-collapse: collapse; - margin-top: .2em; -} - -#qunit-tests th { - text-align: right; - vertical-align: top; - padding: 0 .5em 0 0; -} - -#qunit-tests td { - vertical-align: top; -} - -#qunit-tests pre { - margin: 0; - white-space: pre-wrap; - word-wrap: break-word; -} - -#qunit-tests del { - background-color: #e0f2be; - color: #374e0c; - text-decoration: none; -} - -#qunit-tests ins { - background-color: #ffcaca; - color: #500; - text-decoration: none; -} - -/*** Test Counts */ - -#qunit-tests b.counts { color: black; } -#qunit-tests b.passed { color: #5E740B; } -#qunit-tests b.failed { color: #710909; } - -#qunit-tests li li { - margin: 0.5em; - padding: 0.4em 0.5em 0.4em 0.5em; - background-color: #fff; - border-bottom: none; - list-style-position: inside; -} - -/*** Passing Styles */ - -#qunit-tests li li.pass { - color: #5E740B; - background-color: #fff; - border-left: 26px solid #C6E746; -} - -#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } -#qunit-tests .pass .test-name { color: #366097; } - -#qunit-tests .pass .test-actual, -#qunit-tests .pass .test-expected { color: #999999; } - -#qunit-banner.qunit-pass { background-color: #C6E746; } - -/*** Failing Styles */ - -#qunit-tests li li.fail { - color: #710909; - background-color: #fff; - border-left: 26px solid #EE5757; -} - -#qunit-tests > li:last-child { - border-radius: 0 0 15px 15px; - -moz-border-radius: 0 0 15px 15px; - -webkit-border-bottom-right-radius: 15px; - -webkit-border-bottom-left-radius: 15px; -} - -#qunit-tests .fail { color: #000000; background-color: #EE5757; } -#qunit-tests .fail .test-name, -#qunit-tests .fail .module-name { color: #000000; } - -#qunit-tests .fail .test-actual { color: #EE5757; } -#qunit-tests .fail .test-expected { color: green; } - -#qunit-banner.qunit-fail { background-color: #EE5757; } - - -/** Result */ - -#qunit-testresult { - padding: 0.5em 0.5em 0.5em 2.5em; - - color: #2b81af; - background-color: #D2E0E6; - - border-bottom: 1px solid white; -} - -/** Fixture */ - -#qunit-fixture { - position: absolute; - top: -10000px; - left: -10000px; -} - +/** + * QUnit - A JavaScript Unit Testing Framework + * + * http://docs.jquery.com/QUnit + * + * Copyright (c) 2011 John Resig, Jörn Zaefferer + * Dual licensed under the MIT (MIT-LICENSE.txt) + * or GPL (GPL-LICENSE.txt) licenses. + */ + +/** Font Family and Sizes */ + +#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult +{ + font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; +} + +#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li +{ + font-size: small; +} + +#qunit-tests +{ + font-size: smaller; +} + +/** Resets */ + +#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult +{ + margin: 0; + padding: 0; +} + +/** Header */ + +#qunit-header +{ + padding: 0.5em 0 0.5em 1em; + + color: #8699a4; + background-color: #0d3349; + + font-size: 1.5em; + line-height: 1em; + font-weight: normal; + + border-radius: 15px 15px 0 0; + -moz-border-radius: 15px 15px 0 0; + -webkit-border-top-right-radius: 15px; + -webkit-border-top-left-radius: 15px; +} + +#qunit-header a +{ + text-decoration: none; + color: #c2ccd1; +} + +#qunit-header a:hover, +#qunit-header a:focus +{ + color: #fff; +} + +#qunit-banner +{ + height: 5px; +} + +#qunit-testrunner-toolbar +{ + padding: 0.5em 0 0.5em 2em; + color: #5E740B; + background-color: #eee; +} + +#qunit-userAgent +{ + padding: 0.5em 0 0.5em 2.5em; + background-color: #2b81af; + color: #fff; + text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; +} + +/** Tests: Pass/Fail */ + +#qunit-tests +{ + list-style-position: inside; +} + +#qunit-tests li +{ + padding: 0.4em 0.5em 0.4em 2.5em; + border-bottom: 1px solid #fff; + list-style-position: inside; +} + +#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running +{ + display: none; +} + +#qunit-tests li strong +{ + cursor: pointer; +} + +#qunit-tests li a +{ + padding: 0.5em; + color: #c2ccd1; + text-decoration: none; +} + +#qunit-tests li a:hover, +#qunit-tests li a:focus +{ + color: #000; +} + +#qunit-tests ol +{ + margin-top: 0.5em; + padding: 0.5em; + + background-color: #fff; + + border-radius: 15px; + -moz-border-radius: 15px; + -webkit-border-radius: 15px; + + box-shadow: inset 0px 2px 13px #999; + -moz-box-shadow: inset 0px 2px 13px #999; + -webkit-box-shadow: inset 0px 2px 13px #999; +} + +#qunit-tests table +{ + border-collapse: collapse; + margin-top: .2em; +} + +#qunit-tests th +{ + text-align: right; + vertical-align: top; + padding: 0 .5em 0 0; +} + +#qunit-tests td +{ + vertical-align: top; +} + +#qunit-tests pre +{ + margin: 0; + white-space: pre-wrap; + word-wrap: break-word; +} + +#qunit-tests del +{ + background-color: #e0f2be; + color: #374e0c; + text-decoration: none; +} + +#qunit-tests ins +{ + background-color: #ffcaca; + color: #500; + text-decoration: none; +} + +/*** Test Counts */ + +#qunit-tests b.counts +{ + color: black; +} + +#qunit-tests b.passed +{ + color: #5E740B; +} + +#qunit-tests b.failed +{ + color: #710909; +} + +#qunit-tests li li +{ + margin: 0.5em; + padding: 0.4em 0.5em 0.4em 0.5em; + background-color: #fff; + border-bottom: none; + list-style-position: inside; +} + +/*** Passing Styles */ + +#qunit-tests li li.pass +{ + color: #5E740B; + background-color: #fff; + border-left: 26px solid #C6E746; +} + +#qunit-tests .pass +{ + color: #528CE0; + background-color: #D2E0E6; +} + +#qunit-tests .pass .test-name +{ + color: #366097; +} + +#qunit-tests .pass .test-actual, +#qunit-tests .pass .test-expected +{ + color: #999999; +} + +#qunit-banner.qunit-pass +{ + background-color: #C6E746; +} + +/*** Failing Styles */ + +#qunit-tests li li.fail +{ + color: #710909; + background-color: #fff; + border-left: 26px solid #EE5757; +} + +#qunit-tests > li:last-child +{ + border-radius: 0 0 15px 15px; + -moz-border-radius: 0 0 15px 15px; + -webkit-border-bottom-right-radius: 15px; + -webkit-border-bottom-left-radius: 15px; +} + +#qunit-tests .fail +{ + color: #000000; + background-color: #EE5757; +} + +#qunit-tests .fail .test-name, +#qunit-tests .fail .module-name +{ + color: #000000; +} + +#qunit-tests .fail .test-actual +{ + color: #EE5757; +} + +#qunit-tests .fail .test-expected +{ + color: green; +} + +#qunit-banner.qunit-fail +{ + background-color: #EE5757; +} + +/** Result */ + +#qunit-testresult +{ + padding: 0.5em 0.5em 0.5em 2.5em; + + color: #2b81af; + background-color: #D2E0E6; + + border-bottom: 1px solid white; +} + +/** Fixture */ + +#qunit-fixture +{ + position: absolute; + top: -10000px; + left: -10000px; +} + diff --git a/test/vendor/qunit.js b/test/vendor/qunit.js index 5075036..1641f5f 100644 --- a/test/vendor/qunit.js +++ b/test/vendor/qunit.js @@ -1,1443 +1,1609 @@ -/** - * QUnit - A JavaScript Unit Testing Framework - * - * http://docs.jquery.com/QUnit - * - * Copyright (c) 2011 John Resig, Jörn Zaefferer - * Dual licensed under the MIT (MIT-LICENSE.txt) - * or GPL (GPL-LICENSE.txt) licenses. - */ - -(function(window) { - -var defined = { - setTimeout: typeof window.setTimeout !== "undefined", - sessionStorage: (function() { - try { - return !!sessionStorage.getItem; - } catch(e){ - return false; - } - })() -}; - -var testId = 0; - -var Test = function(name, testName, expected, testEnvironmentArg, async, callback) { - this.name = name; - this.testName = testName; - this.expected = expected; - this.testEnvironmentArg = testEnvironmentArg; - this.async = async; - this.callback = callback; - this.assertions = []; -}; -Test.prototype = { - init: function() { - var tests = id("qunit-tests"); - if (tests) { - var b = document.createElement("strong"); - b.innerHTML = "Running " + this.name; - var li = document.createElement("li"); - li.appendChild( b ); - li.className = "running"; - li.id = this.id = "test-output" + testId++; - tests.appendChild( li ); - } - }, - setup: function() { - if (this.module != config.previousModule) { - if ( config.previousModule ) { - QUnit.moduleDone( { - name: config.previousModule, - failed: config.moduleStats.bad, - passed: config.moduleStats.all - config.moduleStats.bad, - total: config.moduleStats.all - } ); - } - config.previousModule = this.module; - config.moduleStats = { all: 0, bad: 0 }; - QUnit.moduleStart( { - name: this.module - } ); - } - - config.current = this; - this.testEnvironment = extend({ - setup: function() {}, - teardown: function() {} - }, this.moduleTestEnvironment); - if (this.testEnvironmentArg) { - extend(this.testEnvironment, this.testEnvironmentArg); - } - - QUnit.testStart( { - name: this.testName - } ); - - // allow utility functions to access the current test environment - // TODO why?? - QUnit.current_testEnvironment = this.testEnvironment; - - try { - if ( !config.pollution ) { - saveGlobal(); - } - - this.testEnvironment.setup.call(this.testEnvironment); - } catch(e) { - QUnit.ok( false, "Setup failed on " + this.testName + ": " + e.message ); - } - }, - run: function() { - if ( this.async ) { - QUnit.stop(); - } - - if ( config.notrycatch ) { - this.callback.call(this.testEnvironment); - return; - } - try { - this.callback.call(this.testEnvironment); - } catch(e) { - fail("Test " + this.testName + " died, exception and test follows", e, this.callback); - QUnit.ok( false, "Died on test #" + (this.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e) ); - // else next test will carry the responsibility - saveGlobal(); - - // Restart the tests if they're blocking - if ( config.blocking ) { - start(); - } - } - }, - teardown: function() { - try { - checkPollution(); - this.testEnvironment.teardown.call(this.testEnvironment); - } catch(e) { - QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message ); - } - }, - finish: function() { - if ( this.expected && this.expected != this.assertions.length ) { - QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" ); - } - - var good = 0, bad = 0, - tests = id("qunit-tests"); - - config.stats.all += this.assertions.length; - config.moduleStats.all += this.assertions.length; - - if ( tests ) { - var ol = document.createElement("ol"); - - for ( var i = 0; i < this.assertions.length; i++ ) { - var assertion = this.assertions[i]; - - var li = document.createElement("li"); - li.className = assertion.result ? "pass" : "fail"; - li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed"); - ol.appendChild( li ); - - if ( assertion.result ) { - good++; - } else { - bad++; - config.stats.bad++; - config.moduleStats.bad++; - } - } - - // store result when possible - if ( QUnit.config.reorder && defined.sessionStorage ) { - if (bad) { - sessionStorage.setItem("qunit-" + this.module + "-" + this.testName, bad); - } else { - sessionStorage.removeItem("qunit-" + this.module + "-" + this.testName); - } - } - - if (bad == 0) { - ol.style.display = "none"; - } - - var b = document.createElement("strong"); - b.innerHTML = this.name + " (" + bad + ", " + good + ", " + this.assertions.length + ")"; - - var a = document.createElement("a"); - a.innerHTML = "Rerun"; - a.href = QUnit.url({ filter: getText([b]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") }); - - addEvent(b, "click", function() { - var next = b.nextSibling.nextSibling, - display = next.style.display; - next.style.display = display === "none" ? "block" : "none"; - }); - - addEvent(b, "dblclick", function(e) { - var target = e && e.target ? e.target : window.event.srcElement; - if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) { - target = target.parentNode; - } - if ( window.location && target.nodeName.toLowerCase() === "strong" ) { - window.location = QUnit.url({ filter: getText([target]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") }); - } - }); - - var li = id(this.id); - li.className = bad ? "fail" : "pass"; - li.removeChild( li.firstChild ); - li.appendChild( b ); - li.appendChild( a ); - li.appendChild( ol ); - - } else { - for ( var i = 0; i < this.assertions.length; i++ ) { - if ( !this.assertions[i].result ) { - bad++; - config.stats.bad++; - config.moduleStats.bad++; - } - } - } - - try { - QUnit.reset(); - } catch(e) { - fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset); - } - - QUnit.testDone( { - name: this.testName, - failed: bad, - passed: this.assertions.length - bad, - total: this.assertions.length - } ); - }, - - queue: function() { - var test = this; - synchronize(function() { - test.init(); - }); - function run() { - // each of these can by async - synchronize(function() { - test.setup(); - }); - synchronize(function() { - test.run(); - }); - synchronize(function() { - test.teardown(); - }); - synchronize(function() { - test.finish(); - }); - } - // defer when previous test run passed, if storage is available - var bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.module + "-" + this.testName); - if (bad) { - run(); - } else { - synchronize(run); - }; - } - -}; - -var QUnit = { - - // call on start of module test to prepend name to all tests - module: function(name, testEnvironment) { - config.currentModule = name; - config.currentModuleTestEnviroment = testEnvironment; - }, - - asyncTest: function(testName, expected, callback) { - if ( arguments.length === 2 ) { - callback = expected; - expected = 0; - } - - QUnit.test(testName, expected, callback, true); - }, - - test: function(testName, expected, callback, async) { - var name = '' + testName + '', testEnvironmentArg; - - if ( arguments.length === 2 ) { - callback = expected; - expected = null; - } - // is 2nd argument a testEnvironment? - if ( expected && typeof expected === 'object') { - testEnvironmentArg = expected; - expected = null; - } - - if ( config.currentModule ) { - name = '' + config.currentModule + ": " + name; - } - - if ( !validTest(config.currentModule + ": " + testName) ) { - return; - } - - var test = new Test(name, testName, expected, testEnvironmentArg, async, callback); - test.module = config.currentModule; - test.moduleTestEnvironment = config.currentModuleTestEnviroment; - test.queue(); - }, - - /** - * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. - */ - expect: function(asserts) { - config.current.expected = asserts; - }, - - /** - * Asserts true. - * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); - */ - ok: function(a, msg) { - a = !!a; - var details = { - result: a, - message: msg - }; - msg = escapeHtml(msg); - QUnit.log(details); - config.current.assertions.push({ - result: a, - message: msg - }); - }, - - /** - * Checks that the first two arguments are equal, with an optional message. - * Prints out both actual and expected values. - * - * Prefered to ok( actual == expected, message ) - * - * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." ); - * - * @param Object actual - * @param Object expected - * @param String message (optional) - */ - equal: function(actual, expected, message) { - QUnit.push(expected == actual, actual, expected, message); - }, - - notEqual: function(actual, expected, message) { - QUnit.push(expected != actual, actual, expected, message); - }, - - deepEqual: function(actual, expected, message) { - QUnit.push(QUnit.equiv(actual, expected), actual, expected, message); - }, - - notDeepEqual: function(actual, expected, message) { - QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message); - }, - - strictEqual: function(actual, expected, message) { - QUnit.push(expected === actual, actual, expected, message); - }, - - notStrictEqual: function(actual, expected, message) { - QUnit.push(expected !== actual, actual, expected, message); - }, - - raises: function(block, expected, message) { - var actual, ok = false; - - if (typeof expected === 'string') { - message = expected; - expected = null; - } - - try { - block(); - } catch (e) { - actual = e; - } - - if (actual) { - // we don't want to validate thrown error - if (!expected) { - ok = true; - // expected is a regexp - } else if (QUnit.objectType(expected) === "regexp") { - ok = expected.test(actual); - // expected is a constructor - } else if (actual instanceof expected) { - ok = true; - // expected is a validation function which returns true is validation passed - } else if (expected.call({}, actual) === true) { - ok = true; - } - } - - QUnit.ok(ok, message); - }, - - start: function() { - config.semaphore--; - if (config.semaphore > 0) { - // don't start until equal number of stop-calls - return; - } - if (config.semaphore < 0) { - // ignore if start is called more often then stop - config.semaphore = 0; - } - // A slight delay, to avoid any current callbacks - if ( defined.setTimeout ) { - window.setTimeout(function() { - if ( config.timeout ) { - clearTimeout(config.timeout); - } - - config.blocking = false; - process(); - }, 13); - } else { - config.blocking = false; - process(); - } - }, - - stop: function(timeout) { - config.semaphore++; - config.blocking = true; - - if ( timeout && defined.setTimeout ) { - clearTimeout(config.timeout); - config.timeout = window.setTimeout(function() { - QUnit.ok( false, "Test timed out" ); - QUnit.start(); - }, timeout); - } - } -}; - -// Backwards compatibility, deprecated -QUnit.equals = QUnit.equal; -QUnit.same = QUnit.deepEqual; - -// Maintain internal state -var config = { - // The queue of tests to run - queue: [], - - // block until document ready - blocking: true, - - // by default, run previously failed tests first - // very useful in combination with "Hide passed tests" checked - reorder: true, - - noglobals: false, - notrycatch: false -}; - -// Load paramaters -(function() { - var location = window.location || { search: "", protocol: "file:" }, - params = location.search.slice( 1 ).split( "&" ), - length = params.length, - urlParams = {}, - current; - - if ( params[ 0 ] ) { - for ( var i = 0; i < length; i++ ) { - current = params[ i ].split( "=" ); - current[ 0 ] = decodeURIComponent( current[ 0 ] ); - // allow just a key to turn on a flag, e.g., test.html?noglobals - current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true; - urlParams[ current[ 0 ] ] = current[ 1 ]; - if ( current[ 0 ] in config ) { - config[ current[ 0 ] ] = current[ 1 ]; - } - } - } - - QUnit.urlParams = urlParams; - config.filter = urlParams.filter; - - // Figure out if we're running the tests from a server or not - QUnit.isLocal = !!(location.protocol === 'file:'); -})(); - -// Expose the API as global variables, unless an 'exports' -// object exists, in that case we assume we're in CommonJS -if ( typeof exports === "undefined" || typeof require === "undefined" ) { - extend(window, QUnit); - window.QUnit = QUnit; -} else { - extend(exports, QUnit); - exports.QUnit = QUnit; -} - -// define these after exposing globals to keep them in these QUnit namespace only -extend(QUnit, { - config: config, - - // Initialize the configuration options - init: function() { - extend(config, { - stats: { all: 0, bad: 0 }, - moduleStats: { all: 0, bad: 0 }, - started: +new Date, - updateRate: 1000, - blocking: false, - autostart: true, - autorun: false, - filter: "", - queue: [], - semaphore: 0 - }); - - var tests = id( "qunit-tests" ), - banner = id( "qunit-banner" ), - result = id( "qunit-testresult" ); - - if ( tests ) { - tests.innerHTML = ""; - } - - if ( banner ) { - banner.className = ""; - } - - if ( result ) { - result.parentNode.removeChild( result ); - } - - if ( tests ) { - result = document.createElement( "p" ); - result.id = "qunit-testresult"; - result.className = "result"; - tests.parentNode.insertBefore( result, tests ); - result.innerHTML = 'Running...
       '; - } - }, - - /** - * Resets the test setup. Useful for tests that modify the DOM. - * - * If jQuery is available, uses jQuery's html(), otherwise just innerHTML. - */ - reset: function() { - if ( window.jQuery ) { - jQuery( "#qunit-fixture" ).html( config.fixture ); - } else { - var main = id( 'qunit-fixture' ); - if ( main ) { - main.innerHTML = config.fixture; - } - } - }, - - /** - * Trigger an event on an element. - * - * @example triggerEvent( document.body, "click" ); - * - * @param DOMElement elem - * @param String type - */ - triggerEvent: function( elem, type, event ) { - if ( document.createEvent ) { - event = document.createEvent("MouseEvents"); - event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, - 0, 0, 0, 0, 0, false, false, false, false, 0, null); - elem.dispatchEvent( event ); - - } else if ( elem.fireEvent ) { - elem.fireEvent("on"+type); - } - }, - - // Safe object type checking - is: function( type, obj ) { - return QUnit.objectType( obj ) == type; - }, - - objectType: function( obj ) { - if (typeof obj === "undefined") { - return "undefined"; - - // consider: typeof null === object - } - if (obj === null) { - return "null"; - } - - var type = Object.prototype.toString.call( obj ) - .match(/^\[object\s(.*)\]$/)[1] || ''; - - switch (type) { - case 'Number': - if (isNaN(obj)) { - return "nan"; - } else { - return "number"; - } - case 'String': - case 'Boolean': - case 'Array': - case 'Date': - case 'RegExp': - case 'Function': - return type.toLowerCase(); - } - if (typeof obj === "object") { - return "object"; - } - return undefined; - }, - - push: function(result, actual, expected, message) { - var details = { - result: result, - message: message, - actual: actual, - expected: expected - }; - - message = escapeHtml(message) || (result ? "okay" : "failed"); - message = '' + message + ""; - expected = escapeHtml(QUnit.jsDump.parse(expected)); - actual = escapeHtml(QUnit.jsDump.parse(actual)); - var output = message + ''; - if (actual != expected) { - output += ''; - output += ''; - } - if (!result) { - var source = sourceFromStacktrace(); - if (source) { - details.source = source; - output += ''; - } - } - output += "
      Expected:
      ' + expected + '
      Result:
      ' + actual + '
      Diff:
      ' + QUnit.diff(expected, actual) +'
      Source:
      ' + source +'
      "; - - QUnit.log(details); - - config.current.assertions.push({ - result: !!result, - message: output - }); - }, - - url: function( params ) { - params = extend( extend( {}, QUnit.urlParams ), params ); - var querystring = "?", - key; - for ( key in params ) { - querystring += encodeURIComponent( key ) + "=" + - encodeURIComponent( params[ key ] ) + "&"; - } - return window.location.pathname + querystring.slice( 0, -1 ); - }, - - // Logging callbacks; all receive a single argument with the listed properties - // run test/logs.html for any related changes - begin: function() {}, - // done: { failed, passed, total, runtime } - done: function() {}, - // log: { result, actual, expected, message } - log: function() {}, - // testStart: { name } - testStart: function() {}, - // testDone: { name, failed, passed, total } - testDone: function() {}, - // moduleStart: { name } - moduleStart: function() {}, - // moduleDone: { name, failed, passed, total } - moduleDone: function() {} -}); - -if ( typeof document === "undefined" || document.readyState === "complete" ) { - config.autorun = true; -} - -addEvent(window, "load", function() { - QUnit.begin({}); - - // Initialize the config, saving the execution queue - var oldconfig = extend({}, config); - QUnit.init(); - extend(config, oldconfig); - - config.blocking = false; - - var userAgent = id("qunit-userAgent"); - if ( userAgent ) { - userAgent.innerHTML = navigator.userAgent; - } - var banner = id("qunit-header"); - if ( banner ) { - banner.innerHTML = ' ' + banner.innerHTML + ' ' + - '' + - ''; - addEvent( banner, "change", function( event ) { - var params = {}; - params[ event.target.name ] = event.target.checked ? true : undefined; - window.location = QUnit.url( params ); - }); - } - - var toolbar = id("qunit-testrunner-toolbar"); - if ( toolbar ) { - var filter = document.createElement("input"); - filter.type = "checkbox"; - filter.id = "qunit-filter-pass"; - addEvent( filter, "click", function() { - var ol = document.getElementById("qunit-tests"); - if ( filter.checked ) { - ol.className = ol.className + " hidepass"; - } else { - var tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " "; - ol.className = tmp.replace(/ hidepass /, " "); - } - if ( defined.sessionStorage ) { - if (filter.checked) { - sessionStorage.setItem("qunit-filter-passed-tests", "true"); - } else { - sessionStorage.removeItem("qunit-filter-passed-tests"); - } - } - }); - if ( defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) { - filter.checked = true; - var ol = document.getElementById("qunit-tests"); - ol.className = ol.className + " hidepass"; - } - toolbar.appendChild( filter ); - - var label = document.createElement("label"); - label.setAttribute("for", "qunit-filter-pass"); - label.innerHTML = "Hide passed tests"; - toolbar.appendChild( label ); - } - - var main = id('qunit-fixture'); - if ( main ) { - config.fixture = main.innerHTML; - } - - if (config.autostart) { - QUnit.start(); - } -}); - -function done() { - config.autorun = true; - - // Log the last module results - if ( config.currentModule ) { - QUnit.moduleDone( { - name: config.currentModule, - failed: config.moduleStats.bad, - passed: config.moduleStats.all - config.moduleStats.bad, - total: config.moduleStats.all - } ); - } - - var banner = id("qunit-banner"), - tests = id("qunit-tests"), - runtime = +new Date - config.started, - passed = config.stats.all - config.stats.bad, - html = [ - 'Tests completed in ', - runtime, - ' milliseconds.
      ', - '', - passed, - ' tests of ', - config.stats.all, - ' passed, ', - config.stats.bad, - ' failed.' - ].join(''); - - if ( banner ) { - banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass"); - } - - if ( tests ) { - id( "qunit-testresult" ).innerHTML = html; - } - - QUnit.done( { - failed: config.stats.bad, - passed: passed, - total: config.stats.all, - runtime: runtime - } ); -} - -function validTest( name ) { - var filter = config.filter, - run = false; - - if ( !filter ) { - return true; - } - - not = filter.charAt( 0 ) === "!"; - if ( not ) { - filter = filter.slice( 1 ); - } - - if ( name.indexOf( filter ) !== -1 ) { - return !not; - } - - if ( not ) { - run = true; - } - - return run; -} - -// so far supports only Firefox, Chrome and Opera (buggy) -// could be extended in the future to use something like https://github.com/csnover/TraceKit -function sourceFromStacktrace() { - try { - throw new Error(); - } catch ( e ) { - if (e.stacktrace) { - // Opera - return e.stacktrace.split("\n")[6]; - } else if (e.stack) { - // Firefox, Chrome - return e.stack.split("\n")[4]; - } - } -} - -function escapeHtml(s) { - if (!s) { - return ""; - } - s = s + ""; - return s.replace(/[\&"<>\\]/g, function(s) { - switch(s) { - case "&": return "&"; - case "\\": return "\\\\"; - case '"': return '\"'; - case "<": return "<"; - case ">": return ">"; - default: return s; - } - }); -} - -function synchronize( callback ) { - config.queue.push( callback ); - - if ( config.autorun && !config.blocking ) { - process(); - } -} - -function process() { - var start = (new Date()).getTime(); - - while ( config.queue.length && !config.blocking ) { - if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) { - config.queue.shift()(); - } else { - window.setTimeout( process, 13 ); - break; - } - } - if (!config.blocking && !config.queue.length) { - done(); - } -} - -function saveGlobal() { - config.pollution = []; - - if ( config.noglobals ) { - for ( var key in window ) { - config.pollution.push( key ); - } - } -} - -function checkPollution( name ) { - var old = config.pollution; - saveGlobal(); - - var newGlobals = diff( config.pollution, old ); - if ( newGlobals.length > 0 ) { - ok( false, "Introduced global variable(s): " + newGlobals.join(", ") ); - } - - var deletedGlobals = diff( old, config.pollution ); - if ( deletedGlobals.length > 0 ) { - ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") ); - } -} - -// returns a new Array with the elements that are in a but not in b -function diff( a, b ) { - var result = a.slice(); - for ( var i = 0; i < result.length; i++ ) { - for ( var j = 0; j < b.length; j++ ) { - if ( result[i] === b[j] ) { - result.splice(i, 1); - i--; - break; - } - } - } - return result; -} - -function fail(message, exception, callback) { - if ( typeof console !== "undefined" && console.error && console.warn ) { - console.error(message); - console.error(exception); - console.warn(callback.toString()); - - } else if ( window.opera && opera.postError ) { - opera.postError(message, exception, callback.toString); - } -} - -function extend(a, b) { - for ( var prop in b ) { - if ( b[prop] === undefined ) { - delete a[prop]; - } else { - a[prop] = b[prop]; - } - } - - return a; -} - -function addEvent(elem, type, fn) { - if ( elem.addEventListener ) { - elem.addEventListener( type, fn, false ); - } else if ( elem.attachEvent ) { - elem.attachEvent( "on" + type, fn ); - } else { - fn(); - } -} - -function id(name) { - return !!(typeof document !== "undefined" && document && document.getElementById) && - document.getElementById( name ); -} - -// Test for equality any JavaScript type. -// Discussions and reference: http://philrathe.com/articles/equiv -// Test suites: http://philrathe.com/tests/equiv -// Author: Philippe Rathé -QUnit.equiv = function () { - - var innerEquiv; // the real equiv function - var callers = []; // stack to decide between skip/abort functions - var parents = []; // stack to avoiding loops from circular referencing - - // Call the o related callback with the given arguments. - function bindCallbacks(o, callbacks, args) { - var prop = QUnit.objectType(o); - if (prop) { - if (QUnit.objectType(callbacks[prop]) === "function") { - return callbacks[prop].apply(callbacks, args); - } else { - return callbacks[prop]; // or undefined - } - } - } - - var callbacks = function () { - - // for string, boolean, number and null - function useStrictEquality(b, a) { - if (b instanceof a.constructor || a instanceof b.constructor) { - // to catch short annotaion VS 'new' annotation of a declaration - // e.g. var i = 1; - // var j = new Number(1); - return a == b; - } else { - return a === b; - } - } - - return { - "string": useStrictEquality, - "boolean": useStrictEquality, - "number": useStrictEquality, - "null": useStrictEquality, - "undefined": useStrictEquality, - - "nan": function (b) { - return isNaN(b); - }, - - "date": function (b, a) { - return QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf(); - }, - - "regexp": function (b, a) { - return QUnit.objectType(b) === "regexp" && - a.source === b.source && // the regex itself - a.global === b.global && // and its modifers (gmi) ... - a.ignoreCase === b.ignoreCase && - a.multiline === b.multiline; - }, - - // - skip when the property is a method of an instance (OOP) - // - abort otherwise, - // initial === would have catch identical references anyway - "function": function () { - var caller = callers[callers.length - 1]; - return caller !== Object && - typeof caller !== "undefined"; - }, - - "array": function (b, a) { - var i, j, loop; - var len; - - // b could be an object literal here - if ( ! (QUnit.objectType(b) === "array")) { - return false; - } - - len = a.length; - if (len !== b.length) { // safe and faster - return false; - } - - //track reference to avoid circular references - parents.push(a); - for (i = 0; i < len; i++) { - loop = false; - for(j=0;j< 2) { - return true; // end transition - } - - return (function (a, b) { - if (a === b) { - return true; // catch the most you can - } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || QUnit.objectType(a) !== QUnit.objectType(b)) { - return false; // don't lose time with error prone cases - } else { - return bindCallbacks(a, callbacks, [b, a]); - } - - // apply transition with (1..n) arguments - })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1)); - }; - - return innerEquiv; - -}(); - -/** - * jsDump - * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com - * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php) - * Date: 5/15/2008 - * @projectDescription Advanced and extensible data dumping for Javascript. - * @version 1.0.0 - * @author Ariel Flesler - * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html} - */ -QUnit.jsDump = (function() { - function quote( str ) { - return '"' + str.toString().replace(/"/g, '\\"') + '"'; - }; - function literal( o ) { - return o + ''; - }; - function join( pre, arr, post ) { - var s = jsDump.separator(), - base = jsDump.indent(), - inner = jsDump.indent(1); - if ( arr.join ) - arr = arr.join( ',' + s + inner ); - if ( !arr ) - return pre + post; - return [ pre, inner + arr, base + post ].join(s); - }; - function array( arr ) { - var i = arr.length, ret = Array(i); - this.up(); - while ( i-- ) - ret[i] = this.parse( arr[i] ); - this.down(); - return join( '[', ret, ']' ); - }; - - var reName = /^function (\w+)/; - - var jsDump = { - parse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance - var parser = this.parsers[ type || this.typeOf(obj) ]; - type = typeof parser; - - return type == 'function' ? parser.call( this, obj ) : - type == 'string' ? parser : - this.parsers.error; - }, - typeOf:function( obj ) { - var type; - if ( obj === null ) { - type = "null"; - } else if (typeof obj === "undefined") { - type = "undefined"; - } else if (QUnit.is("RegExp", obj)) { - type = "regexp"; - } else if (QUnit.is("Date", obj)) { - type = "date"; - } else if (QUnit.is("Function", obj)) { - type = "function"; - } else if (typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined") { - type = "window"; - } else if (obj.nodeType === 9) { - type = "document"; - } else if (obj.nodeType) { - type = "node"; - } else if (typeof obj === "object" && typeof obj.length === "number" && obj.length >= 0) { - type = "array"; - } else { - type = typeof obj; - } - return type; - }, - separator:function() { - return this.multiline ? this.HTML ? '
      ' : '\n' : this.HTML ? ' ' : ' '; - }, - indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing - if ( !this.multiline ) - return ''; - var chr = this.indentChar; - if ( this.HTML ) - chr = chr.replace(/\t/g,' ').replace(/ /g,' '); - return Array( this._depth_ + (extra||0) ).join(chr); - }, - up:function( a ) { - this._depth_ += a || 1; - }, - down:function( a ) { - this._depth_ -= a || 1; - }, - setParser:function( name, parser ) { - this.parsers[name] = parser; - }, - // The next 3 are exposed so you can use them - quote:quote, - literal:literal, - join:join, - // - _depth_: 1, - // This is the list of parsers, to modify them, use jsDump.setParser - parsers:{ - window: '[Window]', - document: '[Document]', - error:'[ERROR]', //when no parser is found, shouldn't happen - unknown: '[Unknown]', - 'null':'null', - 'undefined':'undefined', - 'function':function( fn ) { - var ret = 'function', - name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE - if ( name ) - ret += ' ' + name; - ret += '('; - - ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join(''); - return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' ); - }, - array: array, - nodelist: array, - arguments: array, - object:function( map ) { - var ret = [ ]; - QUnit.jsDump.up(); - for ( var key in map ) - ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(map[key]) ); - QUnit.jsDump.down(); - return join( '{', ret, '}' ); - }, - node:function( node ) { - var open = QUnit.jsDump.HTML ? '<' : '<', - close = QUnit.jsDump.HTML ? '>' : '>'; - - var tag = node.nodeName.toLowerCase(), - ret = open + tag; - - for ( var a in QUnit.jsDump.DOMAttrs ) { - var val = node[QUnit.jsDump.DOMAttrs[a]]; - if ( val ) - ret += ' ' + a + '=' + QUnit.jsDump.parse( val, 'attribute' ); - } - return ret + close + open + '/' + tag + close; - }, - functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function - var l = fn.length; - if ( !l ) return ''; - - var args = Array(l); - while ( l-- ) - args[l] = String.fromCharCode(97+l);//97 is 'a' - return ' ' + args.join(', ') + ' '; - }, - key:quote, //object calls it internally, the key part of an item in a map - functionCode:'[code]', //function calls it internally, it's the content of the function - attribute:quote, //node calls it internally, it's an html attribute value - string:quote, - date:quote, - regexp:literal, //regex - number:literal, - 'boolean':literal - }, - DOMAttrs:{//attributes to dump from nodes, name=>realName - id:'id', - name:'name', - 'class':'className' - }, - HTML:false,//if true, entities are escaped ( <, >, \t, space and \n ) - indentChar:' ',//indentation unit - multiline:true //if true, items in a collection, are separated by a \n, else just a space. - }; - - return jsDump; -})(); - -// from Sizzle.js -function getText( elems ) { - var ret = "", elem; - - for ( var i = 0; elems[i]; i++ ) { - elem = elems[i]; - - // Get the text from text nodes and CDATA nodes - if ( elem.nodeType === 3 || elem.nodeType === 4 ) { - ret += elem.nodeValue; - - // Traverse everything else, except comment nodes - } else if ( elem.nodeType !== 8 ) { - ret += getText( elem.childNodes ); - } - } - - return ret; -}; - -/* - * Javascript Diff Algorithm - * By John Resig (http://ejohn.org/) - * Modified by Chu Alan "sprite" - * - * Released under the MIT license. - * - * More Info: - * http://ejohn.org/projects/javascript-diff-algorithm/ - * - * Usage: QUnit.diff(expected, actual) - * - * QUnit.diff("the quick brown fox jumped over", "the quick fox jumps over") == "the quick brown fox jumped jumps over" - */ -QUnit.diff = (function() { - function diff(o, n){ - var ns = new Object(); - var os = new Object(); - - for (var i = 0; i < n.length; i++) { - if (ns[n[i]] == null) - ns[n[i]] = { - rows: new Array(), - o: null - }; - ns[n[i]].rows.push(i); - } - - for (var i = 0; i < o.length; i++) { - if (os[o[i]] == null) - os[o[i]] = { - rows: new Array(), - n: null - }; - os[o[i]].rows.push(i); - } - - for (var i in ns) { - if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) { - n[ns[i].rows[0]] = { - text: n[ns[i].rows[0]], - row: os[i].rows[0] - }; - o[os[i].rows[0]] = { - text: o[os[i].rows[0]], - row: ns[i].rows[0] - }; - } - } - - for (var i = 0; i < n.length - 1; i++) { - if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null && - n[i + 1] == o[n[i].row + 1]) { - n[i + 1] = { - text: n[i + 1], - row: n[i].row + 1 - }; - o[n[i].row + 1] = { - text: o[n[i].row + 1], - row: i + 1 - }; - } - } - - for (var i = n.length - 1; i > 0; i--) { - if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null && - n[i - 1] == o[n[i].row - 1]) { - n[i - 1] = { - text: n[i - 1], - row: n[i].row - 1 - }; - o[n[i].row - 1] = { - text: o[n[i].row - 1], - row: i - 1 - }; - } - } - - return { - o: o, - n: n - }; - } - - return function(o, n){ - o = o.replace(/\s+$/, ''); - n = n.replace(/\s+$/, ''); - var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/)); - - var str = ""; - - var oSpace = o.match(/\s+/g); - if (oSpace == null) { - oSpace = [" "]; - } - else { - oSpace.push(" "); - } - var nSpace = n.match(/\s+/g); - if (nSpace == null) { - nSpace = [" "]; - } - else { - nSpace.push(" "); - } - - if (out.n.length == 0) { - for (var i = 0; i < out.o.length; i++) { - str += '' + out.o[i] + oSpace[i] + ""; - } - } - else { - if (out.n[0].text == null) { - for (n = 0; n < out.o.length && out.o[n].text == null; n++) { - str += '' + out.o[n] + oSpace[n] + ""; - } - } - - for (var i = 0; i < out.n.length; i++) { - if (out.n[i].text == null) { - str += '' + out.n[i] + nSpace[i] + ""; - } - else { - var pre = ""; - - for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) { - pre += '' + out.o[n] + oSpace[n] + ""; - } - str += " " + out.n[i].text + nSpace[i] + pre; - } - } - } - - return str; - }; -})(); - -})(this); - +/** + * QUnit - A JavaScript Unit Testing Framework + * + * http://docs.jquery.com/QUnit + * + * Copyright (c) 2011 John Resig, Jörn Zaefferer + * Dual licensed under the MIT (MIT-LICENSE.txt) + * or GPL (GPL-LICENSE.txt) licenses. + */ + +(function (window) +{ + var defined = { + setTimeout:typeof window.setTimeout !== "undefined", + sessionStorage:(function () + { + try + { + return !!sessionStorage.getItem; + } catch (e) + { + return false; + } + })() + }; + var testId = 0; + var Test = function (name, testName, expected, testEnvironmentArg, async, callback) + { + this.name = name; + this.testName = testName; + this.expected = expected; + this.testEnvironmentArg = testEnvironmentArg; + this.async = async; + this.callback = callback; + this.assertions = []; + }; + Test.prototype = { + init:function () + { + var tests = id("qunit-tests"); + if (tests) + { + var b = document.createElement("strong"); + b.innerHTML = "Running " + this.name; + var li = document.createElement("li"); + li.appendChild(b); + li.className = "running"; + li.id = this.id = "test-output" + testId++; + tests.appendChild(li); + } + }, + setup:function () + { + if (this.module != config.previousModule) + { + if (config.previousModule) + { + QUnit.moduleDone({ + name:config.previousModule, + failed:config.moduleStats.bad, + passed:config.moduleStats.all - config.moduleStats.bad, + total:config.moduleStats.all + }); + } + config.previousModule = this.module; + config.moduleStats = { all:0, bad:0 }; + QUnit.moduleStart({ + name:this.module + }); + } + config.current = this; + this.testEnvironment = extend({ + setup:function () + { + }, + teardown:function () + { + } + }, this.moduleTestEnvironment); + if (this.testEnvironmentArg) + { + extend(this.testEnvironment, this.testEnvironmentArg); + } + QUnit.testStart({ + name:this.testName + }); + // allow utility functions to access the current test environment + // TODO why?? + QUnit.current_testEnvironment = this.testEnvironment; + try + { + if (!config.pollution) + { + saveGlobal(); + } + this.testEnvironment.setup.call(this.testEnvironment); + } catch (e) + { + QUnit.ok(false, "Setup failed on " + this.testName + ": " + e.message); + } + }, + run:function () + { + if (this.async) + { + QUnit.stop(); + } + if (config.notrycatch) + { + this.callback.call(this.testEnvironment); + return; + } + try + { + this.callback.call(this.testEnvironment); + } catch (e) + { + fail("Test " + this.testName + " died, exception and test follows", e, this.callback); + QUnit.ok(false, "Died on test #" + (this.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e)); + // else next test will carry the responsibility + saveGlobal(); + // Restart the tests if they're blocking + if (config.blocking) + { + start(); + } + } + }, + teardown:function () + { + try + { + checkPollution(); + this.testEnvironment.teardown.call(this.testEnvironment); + } catch (e) + { + QUnit.ok(false, "Teardown failed on " + this.testName + ": " + e.message); + } + }, + finish:function () + { + if (this.expected && this.expected != this.assertions.length) + { + QUnit.ok(false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run"); + } + var good = 0, bad = 0, + tests = id("qunit-tests"); + config.stats.all += this.assertions.length; + config.moduleStats.all += this.assertions.length; + if (tests) + { + var ol = document.createElement("ol"); + for (var i = 0; i < this.assertions.length; i++) + { + var assertion = this.assertions[i]; + var li = document.createElement("li"); + li.className = assertion.result ? "pass" : "fail"; + li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed"); + ol.appendChild(li); + if (assertion.result) + { + good++; + } + else + { + bad++; + config.stats.bad++; + config.moduleStats.bad++; + } + } + // store result when possible + if (QUnit.config.reorder && defined.sessionStorage) + { + if (bad) + { + sessionStorage.setItem("qunit-" + this.module + "-" + this.testName, bad); + } + else + { + sessionStorage.removeItem("qunit-" + this.module + "-" + this.testName); + } + } + if (bad == 0) + { + ol.style.display = "none"; + } + var b = document.createElement("strong"); + b.innerHTML = this.name + " (" + bad + ", " + good + ", " + this.assertions.length + ")"; + var a = document.createElement("a"); + a.innerHTML = "Rerun"; + a.href = QUnit.url({ filter:getText([b]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") }); + addEvent(b, "click", function () + { + var next = b.nextSibling.nextSibling, + display = next.style.display; + next.style.display = display === "none" ? "block" : "none"; + }); + addEvent(b, "dblclick", function (e) + { + var target = e && e.target ? e.target : window.event.srcElement; + if (target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b") + { + target = target.parentNode; + } + if (window.location && target.nodeName.toLowerCase() === "strong") + { + window.location = QUnit.url({ filter:getText([target]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") }); + } + }); + var li = id(this.id); + li.className = bad ? "fail" : "pass"; + li.removeChild(li.firstChild); + li.appendChild(b); + li.appendChild(a); + li.appendChild(ol); + } + else + { + for (var i = 0; i < this.assertions.length; i++) + { + if (!this.assertions[i].result) + { + bad++; + config.stats.bad++; + config.moduleStats.bad++; + } + } + } + try + { + QUnit.reset(); + } catch (e) + { + fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset); + } + QUnit.testDone({ + name:this.testName, + failed:bad, + passed:this.assertions.length - bad, + total:this.assertions.length + }); + }, + queue:function () + { + var test = this; + synchronize(function () + { + test.init(); + }); + function run() + { + // each of these can by async + synchronize(function () + { + test.setup(); + }); + synchronize(function () + { + test.run(); + }); + synchronize(function () + { + test.teardown(); + }); + synchronize(function () + { + test.finish(); + }); + } + + // defer when previous test run passed, if storage is available + var bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.module + "-" + this.testName); + if (bad) + { + run(); + } + else + { + synchronize(run); + } + ; + } + + }; + var QUnit = { + + // call on start of module test to prepend name to all tests + module:function (name, testEnvironment) + { + config.currentModule = name; + config.currentModuleTestEnviroment = testEnvironment; + }, + asyncTest:function (testName, expected, callback) + { + if (arguments.length === 2) + { + callback = expected; + expected = 0; + } + QUnit.test(testName, expected, callback, true); + }, + test:function (testName, expected, callback, async) + { + var name = '' + testName + '', testEnvironmentArg; + if (arguments.length === 2) + { + callback = expected; + expected = null; + } + // is 2nd argument a testEnvironment? + if (expected && typeof expected === 'object') + { + testEnvironmentArg = expected; + expected = null; + } + if (config.currentModule) + { + name = '' + config.currentModule + ": " + name; + } + if (!validTest(config.currentModule + ": " + testName)) + { + return; + } + var test = new Test(name, testName, expected, testEnvironmentArg, async, callback); + test.module = config.currentModule; + test.moduleTestEnvironment = config.currentModuleTestEnviroment; + test.queue(); + }, + /** + * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. + */ + expect:function (asserts) + { + config.current.expected = asserts; + }, + /** + * Asserts true. + * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); + */ + ok:function (a, msg) + { + a = !!a; + var details = { + result:a, + message:msg + }; + msg = escapeHtml(msg); + QUnit.log(details); + config.current.assertions.push({ + result:a, + message:msg + }); + }, + /** + * Checks that the first two arguments are equal, with an optional message. + * Prints out both actual and expected values. + * + * Prefered to ok( actual == expected, message ) + * + * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." ); + * + * @param Object actual + * @param Object expected + * @param String message (optional) + */ + equal:function (actual, expected, message) + { + QUnit.push(expected == actual, actual, expected, message); + }, + notEqual:function (actual, expected, message) + { + QUnit.push(expected != actual, actual, expected, message); + }, + deepEqual:function (actual, expected, message) + { + QUnit.push(QUnit.equiv(actual, expected), actual, expected, message); + }, + notDeepEqual:function (actual, expected, message) + { + QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message); + }, + strictEqual:function (actual, expected, message) + { + QUnit.push(expected === actual, actual, expected, message); + }, + notStrictEqual:function (actual, expected, message) + { + QUnit.push(expected !== actual, actual, expected, message); + }, + raises:function (block, expected, message) + { + var actual, ok = false; + if (typeof expected === 'string') + { + message = expected; + expected = null; + } + try + { + block(); + } catch (e) + { + actual = e; + } + if (actual) + { + // we don't want to validate thrown error + if (!expected) + { + ok = true; + // expected is a regexp + } + else if (QUnit.objectType(expected) === "regexp") + { + ok = expected.test(actual); + // expected is a constructor + } + else if (actual instanceof expected) + { + ok = true; + // expected is a validation function which returns true is validation passed + } + else if (expected.call({}, actual) === true) + { + ok = true; + } + } + QUnit.ok(ok, message); + }, + start:function () + { + config.semaphore--; + if (config.semaphore > 0) + { + // don't start until equal number of stop-calls + return; + } + if (config.semaphore < 0) + { + // ignore if start is called more often then stop + config.semaphore = 0; + } + // A slight delay, to avoid any current callbacks + if (defined.setTimeout) + { + window.setTimeout(function () + { + if (config.timeout) + { + clearTimeout(config.timeout); + } + config.blocking = false; + process(); + }, 13); + } + else + { + config.blocking = false; + process(); + } + }, + stop:function (timeout) + { + config.semaphore++; + config.blocking = true; + if (timeout && defined.setTimeout) + { + clearTimeout(config.timeout); + config.timeout = window.setTimeout(function () + { + QUnit.ok(false, "Test timed out"); + QUnit.start(); + }, timeout); + } + } + }; +// Backwards compatibility, deprecated + QUnit.equals = QUnit.equal; + QUnit.same = QUnit.deepEqual; +// Maintain internal state + var config = { + // The queue of tests to run + queue:[], + // block until document ready + blocking:true, + // by default, run previously failed tests first + // very useful in combination with "Hide passed tests" checked + reorder:true, + noglobals:false, + notrycatch:false + }; +// Load paramaters + (function () + { + var location = window.location || { search:"", protocol:"file:" }, + params = location.search.slice(1).split("&"), + length = params.length, + urlParams = {}, + current; + if (params[ 0 ]) + { + for (var i = 0; i < length; i++) + { + current = params[ i ].split("="); + current[ 0 ] = decodeURIComponent(current[ 0 ]); + // allow just a key to turn on a flag, e.g., test.html?noglobals + current[ 1 ] = current[ 1 ] ? decodeURIComponent(current[ 1 ]) : true; + urlParams[ current[ 0 ] ] = current[ 1 ]; + if (current[ 0 ] in config) + { + config[ current[ 0 ] ] = current[ 1 ]; + } + } + } + QUnit.urlParams = urlParams; + config.filter = urlParams.filter; + // Figure out if we're running the tests from a server or not + QUnit.isLocal = !!(location.protocol === 'file:'); + })(); +// Expose the API as global variables, unless an 'exports' +// object exists, in that case we assume we're in CommonJS + if (typeof exports === "undefined" || typeof require === "undefined") + { + extend(window, QUnit); + window.QUnit = QUnit; + } + else + { + extend(exports, QUnit); + exports.QUnit = QUnit; + } +// define these after exposing globals to keep them in these QUnit namespace only + extend(QUnit, { + config:config, + // Initialize the configuration options + init:function () + { + extend(config, { + stats:{ all:0, bad:0 }, + moduleStats:{ all:0, bad:0 }, + started:+new Date, + updateRate:1000, + blocking:false, + autostart:true, + autorun:false, + filter:"", + queue:[], + semaphore:0 + }); + var tests = id("qunit-tests"), + banner = id("qunit-banner"), + result = id("qunit-testresult"); + if (tests) + { + tests.innerHTML = ""; + } + if (banner) + { + banner.className = ""; + } + if (result) + { + result.parentNode.removeChild(result); + } + if (tests) + { + result = document.createElement("p"); + result.id = "qunit-testresult"; + result.className = "result"; + tests.parentNode.insertBefore(result, tests); + result.innerHTML = 'Running...
       '; + } + }, + /** + * Resets the test setup. Useful for tests that modify the DOM. + * + * If jQuery is available, uses jQuery's html(), otherwise just innerHTML. + */ + reset:function () + { + if (window.jQuery) + { + jQuery("#qunit-fixture").html(config.fixture); + } + else + { + var main = id('qunit-fixture'); + if (main) + { + main.innerHTML = config.fixture; + } + } + }, + /** + * Trigger an event on an element. + * + * @example triggerEvent( document.body, "click" ); + * + * @param DOMElement elem + * @param String type + */ + triggerEvent:function (elem, type, event) + { + if (document.createEvent) + { + event = document.createEvent("MouseEvents"); + event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, + 0, 0, 0, 0, 0, false, false, false, false, 0, null); + elem.dispatchEvent(event); + } + else if (elem.fireEvent) + { + elem.fireEvent("on" + type); + } + }, + // Safe object type checking + is:function (type, obj) + { + return QUnit.objectType(obj) == type; + }, + objectType:function (obj) + { + if (typeof obj === "undefined") + { + return "undefined"; + // consider: typeof null === object + } + if (obj === null) + { + return "null"; + } + var type = Object.prototype.toString.call(obj) + .match(/^\[object\s(.*)\]$/)[1] || ''; + switch (type) + { + case 'Number': + if (isNaN(obj)) + { + return "nan"; + } + else + { + return "number"; + } + case 'String': + case 'Boolean': + case 'Array': + case 'Date': + case 'RegExp': + case 'Function': + return type.toLowerCase(); + } + if (typeof obj === "object") + { + return "object"; + } + return undefined; + }, + push:function (result, actual, expected, message) + { + var details = { + result:result, + message:message, + actual:actual, + expected:expected + }; + message = escapeHtml(message) || (result ? "okay" : "failed"); + message = '' + message + ""; + expected = escapeHtml(QUnit.jsDump.parse(expected)); + actual = escapeHtml(QUnit.jsDump.parse(actual)); + var output = message + ''; + if (actual != expected) + { + output += ''; + output += ''; + } + if (!result) + { + var source = sourceFromStacktrace(); + if (source) + { + details.source = source; + output += ''; + } + } + output += "
      Expected:
      ' + expected + '
      Result:
      ' + actual + '
      Diff:
      ' + QUnit.diff(expected, actual) + '
      Source:
      ' + source + '
      "; + QUnit.log(details); + config.current.assertions.push({ + result:!!result, + message:output + }); + }, + url:function (params) + { + params = extend(extend({}, QUnit.urlParams), params); + var querystring = "?", + key; + for (key in params) + { + querystring += encodeURIComponent(key) + "=" + + encodeURIComponent(params[ key ]) + "&"; + } + return window.location.pathname + querystring.slice(0, -1); + }, + // Logging callbacks; all receive a single argument with the listed properties + // run test/logs.html for any related changes + begin:function () + { + }, + // done: { failed, passed, total, runtime } + done:function () + { + }, + // log: { result, actual, expected, message } + log:function () + { + }, + // testStart: { name } + testStart:function () + { + }, + // testDone: { name, failed, passed, total } + testDone:function () + { + }, + // moduleStart: { name } + moduleStart:function () + { + }, + // moduleDone: { name, failed, passed, total } + moduleDone:function () + { + } + }); + if (typeof document === "undefined" || document.readyState === "complete") + { + config.autorun = true; + } + addEvent(window, "load", function () + { + QUnit.begin({}); + // Initialize the config, saving the execution queue + var oldconfig = extend({}, config); + QUnit.init(); + extend(config, oldconfig); + config.blocking = false; + var userAgent = id("qunit-userAgent"); + if (userAgent) + { + userAgent.innerHTML = navigator.userAgent; + } + var banner = id("qunit-header"); + if (banner) + { + banner.innerHTML = ' ' + banner.innerHTML + ' ' + + '' + + ''; + addEvent(banner, "change", function (event) + { + var params = {}; + params[ event.target.name ] = event.target.checked ? true : undefined; + window.location = QUnit.url(params); + }); + } + var toolbar = id("qunit-testrunner-toolbar"); + if (toolbar) + { + var filter = document.createElement("input"); + filter.type = "checkbox"; + filter.id = "qunit-filter-pass"; + addEvent(filter, "click", function () + { + var ol = document.getElementById("qunit-tests"); + if (filter.checked) + { + ol.className = ol.className + " hidepass"; + } + else + { + var tmp = " " + ol.className.replace(/[\n\t\r]/g, " ") + " "; + ol.className = tmp.replace(/ hidepass /, " "); + } + if (defined.sessionStorage) + { + if (filter.checked) + { + sessionStorage.setItem("qunit-filter-passed-tests", "true"); + } + else + { + sessionStorage.removeItem("qunit-filter-passed-tests"); + } + } + }); + if (defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests")) + { + filter.checked = true; + var ol = document.getElementById("qunit-tests"); + ol.className = ol.className + " hidepass"; + } + toolbar.appendChild(filter); + var label = document.createElement("label"); + label.setAttribute("for", "qunit-filter-pass"); + label.innerHTML = "Hide passed tests"; + toolbar.appendChild(label); + } + var main = id('qunit-fixture'); + if (main) + { + config.fixture = main.innerHTML; + } + if (config.autostart) + { + QUnit.start(); + } + }); + function done() + { + config.autorun = true; + // Log the last module results + if (config.currentModule) + { + QUnit.moduleDone({ + name:config.currentModule, + failed:config.moduleStats.bad, + passed:config.moduleStats.all - config.moduleStats.bad, + total:config.moduleStats.all + }); + } + var banner = id("qunit-banner"), + tests = id("qunit-tests"), + runtime = +new Date - config.started, + passed = config.stats.all - config.stats.bad, + html = [ + 'Tests completed in ', + runtime, + ' milliseconds.
      ', + '', + passed, + ' tests of ', + config.stats.all, + ' passed, ', + config.stats.bad, + ' failed.' + ].join(''); + if (banner) + { + banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass"); + } + if (tests) + { + id("qunit-testresult").innerHTML = html; + } + QUnit.done({ + failed:config.stats.bad, + passed:passed, + total:config.stats.all, + runtime:runtime + }); + } + + function validTest(name) + { + var filter = config.filter, + run = false; + if (!filter) + { + return true; + } + not = filter.charAt(0) === "!"; + if (not) + { + filter = filter.slice(1); + } + if (name.indexOf(filter) !== -1) + { + return !not; + } + if (not) + { + run = true; + } + return run; + } + +// so far supports only Firefox, Chrome and Opera (buggy) +// could be extended in the future to use something like https://github.com/csnover/TraceKit + function sourceFromStacktrace() + { + try + { + throw new Error(); + } catch (e) + { + if (e.stacktrace) + { + // Opera + return e.stacktrace.split("\n")[6]; + } + else if (e.stack) + { + // Firefox, Chrome + return e.stack.split("\n")[4]; + } + } + } + + function escapeHtml(s) + { + if (!s) + { + return ""; + } + s = s + ""; + return s.replace(/[\&"<>\\]/g, function (s) + { + switch (s) + { + case "&": + return "&"; + case "\\": + return "\\\\"; + case '"': + return '\"'; + case "<": + return "<"; + case ">": + return ">"; + default: + return s; + } + }); + } + + function synchronize(callback) + { + config.queue.push(callback); + if (config.autorun && !config.blocking) + { + process(); + } + } + + function process() + { + var start = (new Date()).getTime(); + while (config.queue.length && !config.blocking) + { + if (config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate)) + { + config.queue.shift()(); + } + else + { + window.setTimeout(process, 13); + break; + } + } + if (!config.blocking && !config.queue.length) + { + done(); + } + } + + function saveGlobal() + { + config.pollution = []; + if (config.noglobals) + { + for (var key in window) + { + config.pollution.push(key); + } + } + } + + function checkPollution(name) + { + var old = config.pollution; + saveGlobal(); + var newGlobals = diff(config.pollution, old); + if (newGlobals.length > 0) + { + ok(false, "Introduced global variable(s): " + newGlobals.join(", ")); + } + var deletedGlobals = diff(old, config.pollution); + if (deletedGlobals.length > 0) + { + ok(false, "Deleted global variable(s): " + deletedGlobals.join(", ")); + } + } + +// returns a new Array with the elements that are in a but not in b + function diff(a, b) + { + var result = a.slice(); + for (var i = 0; i < result.length; i++) + { + for (var j = 0; j < b.length; j++) + { + if (result[i] === b[j]) + { + result.splice(i, 1); + i--; + break; + } + } + } + return result; + } + + function fail(message, exception, callback) + { + if (typeof console !== "undefined" && console.error && console.warn) + { + console.error(message); + console.error(exception); + console.warn(callback.toString()); + } + else if (window.opera && opera.postError) + { + opera.postError(message, exception, callback.toString); + } + } + + function extend(a, b) + { + for (var prop in b) + { + if (b[prop] === undefined) + { + delete a[prop]; + } + else + { + a[prop] = b[prop]; + } + } + return a; + } + + function addEvent(elem, type, fn) + { + if (elem.addEventListener) + { + elem.addEventListener(type, fn, false); + } + else if (elem.attachEvent) + { + elem.attachEvent("on" + type, fn); + } + else + { + fn(); + } + } + + function id(name) + { + return !!(typeof document !== "undefined" && document && document.getElementById) && + document.getElementById(name); + } + +// Test for equality any JavaScript type. +// Discussions and reference: http://philrathe.com/articles/equiv +// Test suites: http://philrathe.com/tests/equiv +// Author: Philippe Rathé + QUnit.equiv = function () + { + var innerEquiv; // the real equiv function + var callers = []; // stack to decide between skip/abort functions + var parents = []; // stack to avoiding loops from circular referencing + // Call the o related callback with the given arguments. + function bindCallbacks(o, callbacks, args) + { + var prop = QUnit.objectType(o); + if (prop) + { + if (QUnit.objectType(callbacks[prop]) === "function") + { + return callbacks[prop].apply(callbacks, args); + } + else + { + return callbacks[prop]; // or undefined + } + } + } + + var callbacks = function () + { + + // for string, boolean, number and null + function useStrictEquality(b, a) + { + if (b instanceof a.constructor || a instanceof b.constructor) + { + // to catch short annotaion VS 'new' annotation of a declaration + // e.g. var i = 1; + // var j = new Number(1); + return a == b; + } + else + { + return a === b; + } + } + + return { + "string":useStrictEquality, + "boolean":useStrictEquality, + "number":useStrictEquality, + "null":useStrictEquality, + "undefined":useStrictEquality, + "nan":function (b) + { + return isNaN(b); + }, + "date":function (b, a) + { + return QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf(); + }, + "regexp":function (b, a) + { + return QUnit.objectType(b) === "regexp" && + a.source === b.source && // the regex itself + a.global === b.global && // and its modifers (gmi) ... + a.ignoreCase === b.ignoreCase && + a.multiline === b.multiline; + }, + // - skip when the property is a method of an instance (OOP) + // - abort otherwise, + // initial === would have catch identical references anyway + "function":function () + { + var caller = callers[callers.length - 1]; + return caller !== Object && + typeof caller !== "undefined"; + }, + "array":function (b, a) + { + var i, j, loop; + var len; + // b could be an object literal here + if (!(QUnit.objectType(b) === "array")) + { + return false; + } + len = a.length; + if (len !== b.length) + { // safe and faster + return false; + } + //track reference to avoid circular references + parents.push(a); + for (i = 0; i < len; i++) + { + loop = false; + for (j = 0; j < parents.length; j++) + { + if (parents[j] === a[i]) + { + loop = true;//dont rewalk array + } + } + if (!loop && !innerEquiv(a[i], b[i])) + { + parents.pop(); + return false; + } + } + parents.pop(); + return true; + }, + "object":function (b, a) + { + var i, j, loop; + var eq = true; // unless we can proove it + var aProperties = [], bProperties = []; // collection of strings + // comparing constructors is more strict than using instanceof + if (a.constructor !== b.constructor) + { + return false; + } + // stack constructor before traversing properties + callers.push(a.constructor); + //track reference to avoid circular references + parents.push(a); + for (i in a) + { // be strict: don't ensures hasOwnProperty and go deep + loop = false; + for (j = 0; j < parents.length; j++) + { + if (parents[j] === a[i]) + loop = true; //don't go down the same path twice + } + aProperties.push(i); // collect a's properties + if (!loop && !innerEquiv(a[i], b[i])) + { + eq = false; + break; + } + } + callers.pop(); // unstack, we are done + parents.pop(); + for (i in b) + { + bProperties.push(i); // collect b's properties + } + // Ensures identical properties name + return eq && innerEquiv(aProperties.sort(), bProperties.sort()); + } + }; + }(); + innerEquiv = function () + { // can take multiple arguments + var args = Array.prototype.slice.apply(arguments); + if (args.length < 2) + { + return true; // end transition + } + return (function (a, b) + { + if (a === b) + { + return true; // catch the most you can + } + else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || QUnit.objectType(a) !== QUnit.objectType(b)) + { + return false; // don't lose time with error prone cases + } + else + { + return bindCallbacks(a, callbacks, [b, a]); + } + // apply transition with (1..n) arguments + })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length - 1)); + }; + return innerEquiv; + }(); + /** + * jsDump + * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com + * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php) + * Date: 5/15/2008 + * @projectDescription Advanced and extensible data dumping for Javascript. + * @version 1.0.0 + * @author Ariel Flesler + * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html} + */ + QUnit.jsDump = (function () + { + function quote(str) + { + return '"' + str.toString().replace(/"/g, '\\"') + '"'; + }; + function literal(o) + { + return o + ''; + }; + function join(pre, arr, post) + { + var s = jsDump.separator(), + base = jsDump.indent(), + inner = jsDump.indent(1); + if (arr.join) + arr = arr.join(',' + s + inner); + if (!arr) + return pre + post; + return [ pre, inner + arr, base + post ].join(s); + }; + function array(arr) + { + var i = arr.length, ret = Array(i); + this.up(); + while (i--) + ret[i] = this.parse(arr[i]); + this.down(); + return join('[', ret, ']'); + }; + var reName = /^function (\w+)/; + var jsDump = { + parse:function (obj, type) + { //type is used mostly internally, you can fix a (custom)type in advance + var parser = this.parsers[ type || this.typeOf(obj) ]; + type = typeof parser; + return type == 'function' ? parser.call(this, obj) : + type == 'string' ? parser : + this.parsers.error; + }, + typeOf:function (obj) + { + var type; + if (obj === null) + { + type = "null"; + } + else if (typeof obj === "undefined") + { + type = "undefined"; + } + else if (QUnit.is("RegExp", obj)) + { + type = "regexp"; + } + else if (QUnit.is("Date", obj)) + { + type = "date"; + } + else if (QUnit.is("Function", obj)) + { + type = "function"; + } + else if (typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined") + { + type = "window"; + } + else if (obj.nodeType === 9) + { + type = "document"; + } + else if (obj.nodeType) + { + type = "node"; + } + else if (typeof obj === "object" && typeof obj.length === "number" && obj.length >= 0) + { + type = "array"; + } + else + { + type = typeof obj; + } + return type; + }, + separator:function () + { + return this.multiline ? this.HTML ? '
      ' : '\n' : this.HTML ? ' ' : ' '; + }, + indent:function (extra) + {// extra can be a number, shortcut for increasing-calling-decreasing + if (!this.multiline) + return ''; + var chr = this.indentChar; + if (this.HTML) + chr = chr.replace(/\t/g, ' ').replace(/ /g, ' '); + return Array(this._depth_ + (extra || 0)).join(chr); + }, + up:function (a) + { + this._depth_ += a || 1; + }, + down:function (a) + { + this._depth_ -= a || 1; + }, + setParser:function (name, parser) + { + this.parsers[name] = parser; + }, + // The next 3 are exposed so you can use them + quote:quote, + literal:literal, + join:join, + // + _depth_:1, + // This is the list of parsers, to modify them, use jsDump.setParser + parsers:{ + window:'[Window]', + document:'[Document]', + error:'[ERROR]', //when no parser is found, shouldn't happen + unknown:'[Unknown]', + 'null':'null', + 'undefined':'undefined', + 'function':function (fn) + { + var ret = 'function', + name = 'name' in fn ? fn.name : (reName.exec(fn) || [])[1];//functions never have name in IE + if (name) + ret += ' ' + name; + ret += '('; + ret = [ ret, QUnit.jsDump.parse(fn, 'functionArgs'), '){'].join(''); + return join(ret, QUnit.jsDump.parse(fn, 'functionCode'), '}'); + }, + array:array, + nodelist:array, + arguments:array, + object:function (map) + { + var ret = [ ]; + QUnit.jsDump.up(); + for (var key in map) + ret.push(QUnit.jsDump.parse(key, 'key') + ': ' + QUnit.jsDump.parse(map[key])); + QUnit.jsDump.down(); + return join('{', ret, '}'); + }, + node:function (node) + { + var open = QUnit.jsDump.HTML ? '<' : '<', + close = QUnit.jsDump.HTML ? '>' : '>'; + var tag = node.nodeName.toLowerCase(), + ret = open + tag; + for (var a in QUnit.jsDump.DOMAttrs) + { + var val = node[QUnit.jsDump.DOMAttrs[a]]; + if (val) + ret += ' ' + a + '=' + QUnit.jsDump.parse(val, 'attribute'); + } + return ret + close + open + '/' + tag + close; + }, + functionArgs:function (fn) + {//function calls it internally, it's the arguments part of the function + var l = fn.length; + if (!l) return ''; + var args = Array(l); + while (l--) + args[l] = String.fromCharCode(97 + l);//97 is 'a' + return ' ' + args.join(', ') + ' '; + }, + key:quote, //object calls it internally, the key part of an item in a map + functionCode:'[code]', //function calls it internally, it's the content of the function + attribute:quote, //node calls it internally, it's an html attribute value + string:quote, + date:quote, + regexp:literal, //regex + number:literal, + 'boolean':literal + }, + DOMAttrs:{//attributes to dump from nodes, name=>realName + id:'id', + name:'name', + 'class':'className' + }, + HTML:false, //if true, entities are escaped ( <, >, \t, space and \n ) + indentChar:' ', //indentation unit + multiline:true //if true, items in a collection, are separated by a \n, else just a space. + }; + return jsDump; + })(); +// from Sizzle.js + function getText(elems) + { + var ret = "", elem; + for (var i = 0; elems[i]; i++) + { + elem = elems[i]; + // Get the text from text nodes and CDATA nodes + if (elem.nodeType === 3 || elem.nodeType === 4) + { + ret += elem.nodeValue; + // Traverse everything else, except comment nodes + } + else if (elem.nodeType !== 8) + { + ret += getText(elem.childNodes); + } + } + return ret; + }; + /* + * Javascript Diff Algorithm + * By John Resig (http://ejohn.org/) + * Modified by Chu Alan "sprite" + * + * Released under the MIT license. + * + * More Info: + * http://ejohn.org/projects/javascript-diff-algorithm/ + * + * Usage: QUnit.diff(expected, actual) + * + * QUnit.diff("the quick brown fox jumped over", "the quick fox jumps over") == "the quick brown fox jumped jumps over" + */ + QUnit.diff = (function () + { + function diff(o, n) + { + var ns = new Object(); + var os = new Object(); + for (var i = 0; i < n.length; i++) + { + if (ns[n[i]] == null) + ns[n[i]] = { + rows:new Array(), + o:null + }; + ns[n[i]].rows.push(i); + } + for (var i = 0; i < o.length; i++) + { + if (os[o[i]] == null) + os[o[i]] = { + rows:new Array(), + n:null + }; + os[o[i]].rows.push(i); + } + for (var i in ns) + { + if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) + { + n[ns[i].rows[0]] = { + text:n[ns[i].rows[0]], + row:os[i].rows[0] + }; + o[os[i].rows[0]] = { + text:o[os[i].rows[0]], + row:ns[i].rows[0] + }; + } + } + for (var i = 0; i < n.length - 1; i++) + { + if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null && + n[i + 1] == o[n[i].row + 1]) + { + n[i + 1] = { + text:n[i + 1], + row:n[i].row + 1 + }; + o[n[i].row + 1] = { + text:o[n[i].row + 1], + row:i + 1 + }; + } + } + for (var i = n.length - 1; i > 0; i--) + { + if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null && + n[i - 1] == o[n[i].row - 1]) + { + n[i - 1] = { + text:n[i - 1], + row:n[i].row - 1 + }; + o[n[i].row - 1] = { + text:o[n[i].row - 1], + row:i - 1 + }; + } + } + return { + o:o, + n:n + }; + } + + return function (o, n) + { + o = o.replace(/\s+$/, ''); + n = n.replace(/\s+$/, ''); + var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/)); + var str = ""; + var oSpace = o.match(/\s+/g); + if (oSpace == null) + { + oSpace = [" "]; + } + else + { + oSpace.push(" "); + } + var nSpace = n.match(/\s+/g); + if (nSpace == null) + { + nSpace = [" "]; + } + else + { + nSpace.push(" "); + } + if (out.n.length == 0) + { + for (var i = 0; i < out.o.length; i++) + { + str += '' + out.o[i] + oSpace[i] + ""; + } + } + else + { + if (out.n[0].text == null) + { + for (n = 0; n < out.o.length && out.o[n].text == null; n++) + { + str += '' + out.o[n] + oSpace[n] + ""; + } + } + for (var i = 0; i < out.n.length; i++) + { + if (out.n[i].text == null) + { + str += '' + out.n[i] + nSpace[i] + ""; + } + else + { + var pre = ""; + for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) + { + pre += '' + out.o[n] + oSpace[n] + ""; + } + str += " " + out.n[i].text + nSpace[i] + pre; + } + } + } + return str; + }; + })(); +})(this); + From 009176c8dd346cb03810f8e3f4e0ee6728fe6006 Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 1 Feb 2013 14:45:04 +0100 Subject: [PATCH 3/4] updated .gitignore --- .gitignore | 167 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 164 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index aa73f51..4c33ea7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,164 @@ -test/localize_test.js -test/qunit_setup.js - +################# +## Eclipse +################# + +*.pydevproject +.project +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath +*.idea + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.vspscc +.builds +*.dotCover + +## TODO: If you have NuGet Package Restore enabled, uncomment this +#packages/ + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp + +# ReSharper is a .NET coding add-in +_ReSharper* + +# Installshield output folder +[Ee]xpress + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish + +# Others +[Bb]in +[Oo]bj +sql +TestResults +*.Cache +ClientBin +stylecop.* +~$* +*.dbmdl +Generated_Code #added for RIA/Silverlight projects + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML + + + +############ +## Windows +############ + +# Windows image file caches +Thumbs.db + +# Folder config file +Desktop.ini + + +############# +## Python +############# + +*.py[co] + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +#Translations +*.mo + +#Mr Developer +.mr.developer.cfg + +# Mac crap +.DS_Store From 82b71aebc9d5bddd195db39b1d6bad0f53d3e19d Mon Sep 17 00:00:00 2001 From: alexandros Date: Wed, 12 Feb 2014 15:25:02 +0100 Subject: [PATCH 4/4] pushed changes from parent --- src/jquery.localize.js | 198 +++++++++++++++-------------------------- 1 file changed, 71 insertions(+), 127 deletions(-) diff --git a/src/jquery.localize.js b/src/jquery.localize.js index a21daf9..8e41a3c 100644 --- a/src/jquery.localize.js +++ b/src/jquery.localize.js @@ -1,121 +1,89 @@ -/** - * @url https://github.com/alexwebgr/jquery-localize - * @desc simple plugin for localization using data-localize attributes and json files - */ -(function () -{ +// Generated by CoffeeScript 1.3.3 +(function() { var $, normaliseLang; + $ = jQuery; - normaliseLang = function (lang) - { + + normaliseLang = function(lang) { lang = lang.replace(/_/, '-').toLowerCase(); - if (lang.length > 3) + if(lang.length > 3) { lang = lang.substring(0, 3) + lang.substring(3).toUpperCase(); + } return lang; }; + $.defaultLanguage = normaliseLang(navigator.language || navigator.userLanguage); - $.localize = function (pkg, options) - { - var defaultCallback, - fileExtension, - intermediateLangData, - jsonCall, - lang, - loadLanguage, - localizeElement, - localizeForSpecialKeys, - localizeImageElement, - localizeInputElement, - localizeOptgroupElement, - notifyDelegateLanguageLoaded, - regexify, - setAttrFromValueForKey, - setTextFromValueForKey, - valueForKey, - wrappedSet - ; - if (options == null) - { + + $.localize = function(pkg, options) { + var defaultCallback, fileExtension, intermediateLangData, jsonCall, lang, loadLanguage, localizeElement, localizeForSpecialKeys, localizeImageElement, localizeInputElement, localizeOptgroupElement, notifyDelegateLanguageLoaded, regexify, setAttrFromValueForKey, setTextFromValueForKey, valueForKey, wrappedSet; + if(options == null) { options = {}; } wrappedSet = this; intermediateLangData = {}; fileExtension = options.fileExtension || "json"; - loadLanguage = function (pkg, lang, level) - { + loadLanguage = function(pkg, lang, level) { var file; - if (level == null) + if(level == null) { level = 1; - switch (level) - { + } + switch(level) { case 1: intermediateLangData = {}; - if (options.loadBase) - { + if(options.loadBase) { file = pkg + ("." + fileExtension); return jsonCall(file, pkg, lang, level); - } - else - { + } else { return loadLanguage(pkg, lang, 2); } break; case 2: - if (lang.length >= 2) - { + if(lang.length >= 2) { file = "" + pkg + "-" + (lang.substring(0, 2)) + "." + fileExtension; return jsonCall(file, pkg, lang, level); } break; case 3: - if (lang.length >= 5) - { + if(lang.length >= 5) { file = "" + pkg + "-" + (lang.substring(0, 5)) + "." + fileExtension; return jsonCall(file, pkg, lang, level); } - break; } }; - jsonCall = function (file, pkg, lang, level) - { + jsonCall = function(file, pkg, lang, level) { var ajaxOptions, successFunc; - if (options.pathPrefix != null) + if(options.pathPrefix != null) { file = "" + options.pathPrefix + "/" + file; - successFunc = function (d) - { + } + successFunc = function(d) { $.extend(intermediateLangData, d); notifyDelegateLanguageLoaded(intermediateLangData); return loadLanguage(pkg, lang, level + 1); }; - ajaxOptions = - { - url:file, - dataType:"json", - async:false, - timeout:options.timeout != null ? options.timeout : 500, - success:successFunc + ajaxOptions = { + url : file, + dataType : "json", + async : false, + timeout : options.timeout != null ? options.timeout : 500, + success : successFunc }; - if (window.location.protocol === "file:") - { - ajaxOptions.error = function (xhr) - { + if(window.location.protocol === "file:") { + ajaxOptions.error = function(xhr) { return successFunc($.parseJSON(xhr.responseText)); }; } return $.ajax(ajaxOptions); }; - notifyDelegateLanguageLoaded = function (data) - { - if (options.callback != null) + notifyDelegateLanguageLoaded = function(data) { + if(options.callback != null) { return options.callback(data, defaultCallback); - else + } else { return defaultCallback(data); + } }; - defaultCallback = function (data) - { + defaultCallback = function(data) { $.localize.data[pkg] = data; - return wrappedSet.each(function () - { + return wrappedSet.each(function() { var elem, key, value; elem = $(this); key = elem.data("localize"); @@ -124,113 +92,89 @@ return localizeElement(elem, key, value); }); }; - localizeElement = function (elem, key, value) - { - if (elem.is('input')) - { + localizeElement = function(elem, key, value) { + if(elem.is('input')) { localizeInputElement(elem, key, value); - } - else if (elem.is('img')) - { + } else if(elem.is('img')) { localizeImageElement(elem, key, value); - } - else if (elem.is('optgroup')) - { + } else if(elem.is('optgroup')) { localizeOptgroupElement(elem, key, value); - } - else if (!$.isPlainObject(value)) - { + } else if(!$.isPlainObject(value)) { elem.html(value); } - if ($.isPlainObject(value)) - { + if($.isPlainObject(value)) { return localizeForSpecialKeys(elem, value); } }; - localizeInputElement = function (elem, key, value) - { - if (elem.is("[placeholder]")) - { - return elem.attr("placeholder", value); - } - else - { - return elem.val(value); + localizeInputElement = function(elem, key, value) { + var val; + val = $.isPlainObject(value) ? value.value : value; + if(elem.is("[placeholder]")) { + return elem.attr("placeholder", val); + } else { + return elem.val(val); } }; - localizeForSpecialKeys = function (elem, value) - { + localizeForSpecialKeys = function(elem, value) { setAttrFromValueForKey(elem, "title", value); return setTextFromValueForKey(elem, "text", value); }; - localizeOptgroupElement = function (elem, key, value) - { + localizeOptgroupElement = function(elem, key, value) { return elem.attr("label", value); }; - localizeImageElement = function (elem, key, value) - { + localizeImageElement = function(elem, key, value) { setAttrFromValueForKey(elem, "alt", value); return setAttrFromValueForKey(elem, "src", value); }; - valueForKey = function (key, data) - { + valueForKey = function(key, data) { var keys, value, _i, _len; keys = key.split(/\./); value = data; - for (_i = 0, _len = keys.length; _i < _len; _i++) - { + for(_i = 0, _len = keys.length ; _i < _len ; _i++) { key = keys[_i]; value = value != null ? value[key] : null; } return value; }; - setAttrFromValueForKey = function (elem, key, value) - { + setAttrFromValueForKey = function(elem, key, value) { value = valueForKey(key, value); - if (value != null) - { + if(value != null) { return elem.attr(key, value); } }; - setTextFromValueForKey = function (elem, key, value) - { + setTextFromValueForKey = function(elem, key, value) { value = valueForKey(key, value); - if (value != null) - { + if(value != null) { return elem.text(value); } }; - regexify = function (string_or_regex_or_array) - { + regexify = function(string_or_regex_or_array) { var thing; - if (typeof string_or_regex_or_array === "string") - { + if(typeof string_or_regex_or_array === "string") { return "^" + string_or_regex_or_array + "$"; - } - else if (string_or_regex_or_array.length != null) - { - return ((function () - { + } else if(string_or_regex_or_array.length != null) { + return ((function() { var _i, _len, _results; _results = []; - for (_i = 0, _len = string_or_regex_or_array.length; _i < _len; _i++) - { + for(_i = 0, _len = string_or_regex_or_array.length ; _i < _len ; _i++) { thing = string_or_regex_or_array[_i]; _results.push(regexify(thing)); } return _results; })()).join("|"); - } - else - { + } else { return string_or_regex_or_array; } }; lang = normaliseLang(options.language ? options.language : $.defaultLanguage); - if (!(options.skipLanguage && lang.match(regexify(options.skipLanguage)))) + if(!(options.skipLanguage && lang.match(regexify(options.skipLanguage)))) { loadLanguage(pkg, lang, 1); + } return wrappedSet; }; + $.fn.localize = $.localize; + $.localize.data = {}; + }).call(this);