Commit 6373a817 authored by Sven Franck's avatar Sven Franck

updated 3rd party plugins to latest version

parent 3da91715
// HACKED badly (XXX) to support promise callbacks
/** /**
* @hello.js * @hello.js
* *
...@@ -396,7 +397,15 @@ hello.utils.extend( hello, { ...@@ -396,7 +397,15 @@ hello.utils.extend( hello, {
// Trigger callback // Trigger callback
var popup = window.open( var popup = window.open(
url, //
// OAuth redirect, fixes URI fragments from being lost in Safari
// (URI Fragments within 302 Location URI are lost over HTTPS)
// Loading the redirect.html before triggering the OAuth Flow seems to fix it.
//
// FIREFOX, decodes URL fragments when calling location.hash.
// - This is bad if the value contains break points which are escaped
// - Hence the url must be encoded twice as it contains breakpoints.
p.qs.redirect_uri + "#oauth_redirect=" + encodeURIComponent(encodeURIComponent(url)),
'Authentication', 'Authentication',
"resizeable=true,height=" + windowHeight + ",width=" + windowWidth + ",left="+((window.innerWidth-windowWidth)/2)+",top="+((window.innerHeight-windowHeight)/2) "resizeable=true,height=" + windowHeight + ",width=" + windowWidth + ",left="+((window.innerWidth-windowWidth)/2)+",top="+((window.innerHeight-windowHeight)/2)
); );
...@@ -490,6 +499,7 @@ hello.utils.extend( hello, { ...@@ -490,6 +499,7 @@ hello.utils.extend( hello, {
// @param string optional, name of the service to get information about. // @param string optional, name of the service to get information about.
// //
getAuthResponse : function(service){ getAuthResponse : function(service){
// If the service doesn't exist // If the service doesn't exist
service = service || this.settings.default_service; service = service || this.settings.default_service;
...@@ -500,6 +510,7 @@ hello.utils.extend( hello, { ...@@ -500,6 +510,7 @@ hello.utils.extend( hello, {
}}); }});
return null; return null;
} }
return this.utils.store(service) || null; return this.utils.store(service) || null;
}, },
...@@ -555,8 +566,8 @@ hello.utils.extend( hello.utils, { ...@@ -555,8 +566,8 @@ hello.utils.extend( hello.utils, {
m = s.replace(/^[\#\?]/,'').match(/([^=\/\&]+)=([^\&]+)/g); m = s.replace(/^[\#\?]/,'').match(/([^=\/\&]+)=([^\&]+)/g);
if(m){ if(m){
for(var i=0;i<m.length;i++){ for(var i=0;i<m.length;i++){
b = m[i].split('='); b = m[i].match(/([^=]+)=(.*)/);
a[b[0]] = decodeURIComponent( b[1] ); a[b[1]] = decodeURIComponent( b[2] );
} }
} }
return a; return a;
...@@ -706,6 +717,7 @@ hello.utils.extend( hello.utils, { ...@@ -706,6 +717,7 @@ hello.utils.extend( hello.utils, {
x = null; x = null;
// define x // define x
// x is the first key in the list of object parameters
for(x in o){if(o.hasOwnProperty(x)){ for(x in o){if(o.hasOwnProperty(x)){
break; break;
}} }}
...@@ -713,8 +725,17 @@ hello.utils.extend( hello.utils, { ...@@ -713,8 +725,17 @@ hello.utils.extend( hello.utils, {
// Passing in hash object of arguments? // Passing in hash object of arguments?
// Where the first argument can't be an object // Where the first argument can't be an object
if((args.length===1)&&(typeof(args[0])==='object')&&o[x]!='o!'){ if((args.length===1)&&(typeof(args[0])==='object')&&o[x]!='o!'){
// return same hash.
return args[0]; // Could this object still belong to a property?
// Check the object keys if they match any of the property keys
for(x in args[0]){if(o.hasOwnProperty(x)){
// Does this key exist in the property list?
if( x in o ){
// Yes this key does exist so its most likely this function has been invoked with an object parameter
// return first argument as the hash of all arguments
return args[0];
}
}}
} }
// else loop through and account for the missing ones. // else loop through and account for the missing ones.
...@@ -979,6 +1000,7 @@ hello.utils.extend( hello.utils, { ...@@ -979,6 +1000,7 @@ hello.utils.extend( hello.utils, {
return this; return this;
}; };
// XXX HACK to badly support then()
this.success = this.done = this.then = function(callback){ this.success = this.done = this.then = function(callback){
return this.on("success",callback); return this.on("success",callback);
}; };
...@@ -1186,6 +1208,13 @@ hello.unsubscribe = hello.off; ...@@ -1186,6 +1208,13 @@ hello.unsubscribe = hello.off;
var utils = hello.utils, var utils = hello.utils,
location = window.location; location = window.location;
var debug = function(msg,e){
utils.append("p", {text:msg}, document.documentElement);
if(e){
console.log(e);
}
};
// //
// AuthCallback // AuthCallback
// Trigger a callback to authenticate // Trigger a callback to authenticate
...@@ -1212,15 +1241,38 @@ hello.unsubscribe = hello.off; ...@@ -1212,15 +1241,38 @@ hello.unsubscribe = hello.off;
delete obj.callback; delete obj.callback;
}catch(e){} }catch(e){}
// Call the globalEvent function on the parent
win[cb](obj);
// Update store // Update store
utils.store(obj.network,obj); utils.store(obj.network,obj);
// Call the globalEvent function on the parent
if(cb in win){
try{
win[cb](obj);
}
catch(e){
debug("Error thrown whilst executing parent callback", e);
return;
}
}
else{
debug("Error: Callback missing from parent window, snap!");
return;
}
} }
window.close(); // Close this current window
hello.emit("notice",'Trying to close window'); try{
window.close();
}
catch(e){}
// IOS bug wont let us clos it if still loading
window.addEventListener('load', function(){
window.close();
});
debug("Trying to close window");
// Dont execute any more // Dont execute any more
return; return;
...@@ -1233,7 +1285,8 @@ hello.unsubscribe = hello.off; ...@@ -1233,7 +1285,8 @@ hello.unsubscribe = hello.off;
// //
// FACEBOOK is returning auth errors within as a query_string... thats a stickler for consistency. // FACEBOOK is returning auth errors within as a query_string... thats a stickler for consistency.
// SoundCloud is the state in the querystring and the token in the hashtag, so we'll mix the two together // SoundCloud is the state in the querystring and the token in the hashtag, so we'll mix the two together
var p = utils.merge(hello.utils.param(location.search||''), utils.param(location.hash||''));
var p = utils.merge(utils.param(location.search||''), utils.param(location.hash||''));
// if p.state // if p.state
...@@ -1245,7 +1298,7 @@ hello.unsubscribe = hello.off; ...@@ -1245,7 +1298,7 @@ hello.unsubscribe = hello.off;
var a = JSON.parse(p.state); var a = JSON.parse(p.state);
p = utils.merge(p, a); p = utils.merge(p, a);
}catch(e){ }catch(e){
hello.emit("error", "Could not decode state parameter"); debug("Could not decode state parameter");
} }
// access_token? // access_token?
...@@ -1289,6 +1342,14 @@ hello.unsubscribe = hello.off; ...@@ -1289,6 +1342,14 @@ hello.unsubscribe = hello.off;
} }
} }
} }
//
// OAuth redirect, fixes URI fragments from being lost in Safari
// (URI Fragments within 302 Location URI are lost over HTTPS)
// Loading the redirect.html before triggering the OAuth Flow seems to fix it.
else if("oauth_redirect" in p){
window.location = decodeURIComponent(p.oauth_redirect);
return;
}
// redefine // redefine
p = utils.param(location.search); p = utils.param(location.search);
...@@ -1337,6 +1398,7 @@ hello.api = function(){ ...@@ -1337,6 +1398,7 @@ hello.api = function(){
var self = this.use(), var self = this.use(),
utils = self.utils; utils = self.utils;
// Reference arguments // Reference arguments
self.args = p; self.args = p;
...@@ -1450,6 +1512,11 @@ hello.api = function(){ ...@@ -1450,6 +1512,11 @@ hello.api = function(){
}}); }});
} }
//
// Get the current session
var session = self.getAuthResponse(p.network);
// //
// Given the path trigger the fix // Given the path trigger the fix
processPath(p.path); processPath(p.path);
...@@ -1572,7 +1639,12 @@ hello.api = function(){ ...@@ -1572,7 +1639,12 @@ hello.api = function(){
o.jsonp(p,qs); o.jsonp(p,qs);
} }
// Is self still a post? // Does this provider have a custom method?
if("api" in o && o.api( url, p, {access_token:session.access_token}, callback ) ){
return;
}
// Is method still a post?
if( p.method === 'post' ){ if( p.method === 'post' ){
// Add some additional query parameters to the URL // Add some additional query parameters to the URL
...@@ -1615,8 +1687,7 @@ hello.api = function(){ ...@@ -1615,8 +1687,7 @@ hello.api = function(){
function _sign(network, path, method, data, modifyQueryString, callback){ function _sign(network, path, method, data, modifyQueryString, callback){
// OAUTH SIGNING PROXY // OAUTH SIGNING PROXY
var session = self.getAuthResponse(network), var service = self.services[network],
service = self.services[network],
token = (session ? session.access_token : null); token = (session ? session.access_token : null);
// Is self an OAuth1 endpoint // Is self an OAuth1 endpoint
...@@ -1685,7 +1756,7 @@ hello.utils.extend( hello.utils, { ...@@ -1685,7 +1756,7 @@ hello.utils.extend( hello.utils, {
// Create a clone of an object // Create a clone of an object
clone : function(obj){ clone : function(obj){
if("nodeName" in obj){ if("nodeName" in obj){
return obj[x]; return obj;
} }
var clone = {}, x; var clone = {}, x;
for(x in obj){ for(x in obj){
...@@ -1763,7 +1834,7 @@ hello.utils.extend( hello.utils, { ...@@ -1763,7 +1834,7 @@ hello.utils.extend( hello.utils, {
} }
data = null; data = null;
} }
else if( data && typeof(data) !== 'string' && !(data instanceof FormData)){ else if( data && typeof(data) !== 'string' && !(data instanceof FormData) && !(data instanceof File) && !(data instanceof Blob)){
// Loop through and add formData // Loop through and add formData
var f = new FormData(); var f = new FormData();
for( x in data )if(data.hasOwnProperty(x)){ for( x in data )if(data.hasOwnProperty(x)){
...@@ -1772,6 +1843,9 @@ hello.utils.extend( hello.utils, { ...@@ -1772,6 +1843,9 @@ hello.utils.extend( hello.utils, {
f.append(x, data[x].files[0]); f.append(x, data[x].files[0]);
} }
} }
else if(data[x] instanceof Blob){
f.append(x, data[x], data.name);
}
else{ else{
f.append(x, data[x]); f.append(x, data[x]);
} }
...@@ -2182,8 +2256,6 @@ hello.utils.extend( hello.utils, { ...@@ -2182,8 +2256,6 @@ hello.utils.extend( hello.utils, {
} }
return false; return false;
} }
}); });
...@@ -2360,7 +2432,7 @@ function format_file(o){ ...@@ -2360,7 +2432,7 @@ function format_file(o){
o.file = 'https://api-content.dropbox.com/1/files/'+ path; o.file = 'https://api-content.dropbox.com/1/files/'+ path;
} }
if(!o.id){ if(!o.id){
o.id = o.name; o.id = o.path.replace(/^\//,'');
} }
// o.media = "https://api-content.dropbox.com/1/files/" + path; // o.media = "https://api-content.dropbox.com/1/files/" + path;
} }
...@@ -2373,6 +2445,18 @@ function req(str){ ...@@ -2373,6 +2445,18 @@ function req(str){
}; };
} }
function dataURItoBlob(dataURI) {
var reg = /^data\:([^;,]+(\;charset=[^;,]+)?)(\;base64)?,/i;
var m = dataURI.match(reg);
var binary = atob(dataURI.replace(reg,''));
var array = [];
for(var i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], {type: m[1]});
}
hello.init({ hello.init({
'dropbox' : { 'dropbox' : {
...@@ -2419,7 +2503,7 @@ hello.init({ ...@@ -2419,7 +2503,7 @@ hello.init({
"me" : 'account/info', "me" : 'account/info',
// https://www.dropbox.com/developers/core/docs#metadata // https://www.dropbox.com/developers/core/docs#metadata
"me/files" : req("metadata/@{root|sandbox}/"), "me/files" : req("metadata/@{root|sandbox}/@{parent}"),
"me/folder" : req("metadata/@{root|sandbox}/@{id}"), "me/folder" : req("metadata/@{root|sandbox}/@{id}"),
"me/folders" : req('metadata/@{root|sandbox}/'), "me/folders" : req('metadata/@{root|sandbox}/'),
...@@ -2434,13 +2518,18 @@ hello.init({ ...@@ -2434,13 +2518,18 @@ hello.init({
post : { post : {
"me/files" : function(p,callback){ "me/files" : function(p,callback){
var path = p.data.id, var path = p.data.parent,
file_name = p.data.name; file_name = p.data.name;
p.data = { p.data = {
file : p.data.file file : p.data.file
}; };
// Does this have a data-uri to upload as a file?
if( typeof( p.data.file ) === 'string' ){
p.data.file = dataURItoBlob(p.data.file);
}
callback('https://api-content.dropbox.com/1/files_put/@{root|sandbox}/'+path+"/"+file_name); callback('https://api-content.dropbox.com/1/files_put/@{root|sandbox}/'+path+"/"+file_name);
}, },
"me/folders" : function(p, callback){ "me/folders" : function(p, callback){
...@@ -2453,6 +2542,14 @@ hello.init({ ...@@ -2453,6 +2542,14 @@ hello.init({
})); }));
} }
}, },
// Map DELETE requests
del : {
"me/files" : "fileops/delete?root=@{root|sandbox}&path=@{id}",
"me/folder" : "fileops/delete?root=@{root|sandbox}&path=@{id}"
},
wrap : { wrap : {
me : function(o){ me : function(o){
formatError(o); formatError(o);
...@@ -2481,18 +2578,34 @@ hello.init({ ...@@ -2481,18 +2578,34 @@ hello.init({
format_file(o); format_file(o);
if(o.is_deleted){
o.success = true;
}
return o; return o;
} }
}, },
// doesn't return the CORS headers // doesn't return the CORS headers
xhr : function(p){ xhr : function(p){
// forgetting content DropBox supports the allow-cross-origin-resource
if(p.path.match("https://api-content.dropbox.com/")){ // the proxy supports allow-cross-origin-resource
//p.data = p.data.file.files[0]; // alas that's the only thing we're using.
return false; if( p.data && p.data.file ){
var file = p.data.file;
if( file ){
if(file.files){
p.data = file.files[0];
}
else{
p.data = file;
}
}
} }
else if(p.path.match("me/files")&&p.method==='post'){ if(p.method==='delete'){
return true; // Post delete operations
p.method = 'post';
} }
return true; return true;
} }
...@@ -2553,7 +2666,7 @@ hello.init({ ...@@ -2553,7 +2666,7 @@ hello.init({
// REF: http://developers.facebook.com/docs/reference/dialogs/oauth/ // REF: http://developers.facebook.com/docs/reference/dialogs/oauth/
oauth : { oauth : {
version : 2, version : 2,
auth : 'http://www.facebook.com/dialog/oauth/' auth : 'https://www.facebook.com/dialog/oauth/'
}, },
// Authorization scopes // Authorization scopes
...@@ -2603,7 +2716,12 @@ hello.init({ ...@@ -2603,7 +2716,12 @@ hello.init({
// Map DELETE requests // Map DELETE requests
del : { del : {
//'me/album' : '@{id}' /*
// Can't delete an album
// http://stackoverflow.com/questions/8747181/how-to-delete-an-album
'me/album' : '@{id}'
*/
'me/photo' : '@{id}'
}, },
wrap : { wrap : {
...@@ -2621,20 +2739,20 @@ hello.init({ ...@@ -2621,20 +2739,20 @@ hello.init({
if(p.method==='get'||p.method==='post'){ if(p.method==='get'||p.method==='post'){
qs.suppress_response_codes = true; qs.suppress_response_codes = true;
} }
else if(p.method === "delete"){
qs.method = 'delete';
p.method = "post";
}
return true; return true;
}, },
// Special requirements for handling JSONP fallback // Special requirements for handling JSONP fallback
jsonp : function(p){ jsonp : function(p,qs){
var m = p.method.toLowerCase(); var m = p.method.toLowerCase();
if( m !== 'get' && !hello.utils.hasBinary(p.data) ){ if( m !== 'get' && !hello.utils.hasBinary(p.data) ){
p.data.method = m; p.data.method = m;
p.method = 'get'; p.method = 'get';
} }
else if(p.method === "delete"){
qs.method = 'delete';
p.method = "post";
}
}, },
// Special requirements for iframe form hack // Special requirements for iframe form hack
...@@ -3041,7 +3159,7 @@ hello.init({ ...@@ -3041,7 +3159,7 @@ hello.init({
// //
// GOOGLE API // GOOGLE API
// //
(function(hello){ (function(hello, window){
"use strict"; "use strict";
...@@ -3210,6 +3328,295 @@ hello.init({ ...@@ -3210,6 +3328,295 @@ hello.init({
} }
} }
//
// Misc
var utils = hello.utils;
// Multipart
// Construct a multipart message
function Multipart(){
// Internal body
var body = [],
boundary = (Math.random()*1e10).toString(32),
counter = 0,
line_break = "\r\n",
delim = line_break + "--" + boundary,
ready = function(){},
data_uri = /^data\:([^;,]+(\;charset=[^;,]+)?)(\;base64)?,/i;
// Add File
function addFile(item){
var fr = new FileReader();
fr.onload = function(e){
//addContent( e.target.result, item.type );
addContent( btoa(e.target.result), item.type + line_break + "Content-Transfer-Encoding: base64");
};
fr.readAsBinaryString(item);
}
// Add content
function addContent(content, type){
body.push(line_break + 'Content-Type: ' + type + line_break + line_break + content);
counter--;
ready();
}
// Add new things to the object
this.append = function(content, type){
// Does the content have an array
if(typeof(content) === "string" || !('length' in Object(content)) ){
// converti to multiples
content = [content];
}
for(var i=0;i<content.length;i++){
counter++;
var item = content[i];
// Is this a file?
// Files can be either Blobs or File types
if(item instanceof window.File || item instanceof window.Blob){
// Read the file in
addFile(item);
}
// Data-URI?
// data:[<mime type>][;charset=<charset>][;base64],<encoded data>
// /^data\:([^;,]+(\;charset=[^;,]+)?)(\;base64)?,/i
else if( typeof( item ) === 'string' && item.match(data_uri) ){
var m = item.match(data_uri);
addContent(item.replace(data_uri,''), m[1] + line_break + "Content-Transfer-Encoding: base64");
}
// Regular string
else{
addContent(item, type);
}
}
};
this.onready = function(fn){
ready = function(){
if( counter===0 ){
// trigger ready
body.unshift('');
body.push('--');
fn( body.join(delim), boundary);
body = [];
}
};
ready();
};
}
//
// Events
//
var addEvent, removeEvent;
if(document.removeEventListener){
addEvent = function(elm, event_name, callback){
elm.addEventListener(event_name, callback);
};
removeEvent = function(elm, event_name, callback){
elm.removeEventListener(event_name, callback);
};
}
else if(document.detachEvent){
removeEvent = function (elm, event_name, callback){
elm.detachEvent("on"+event_name, callback);
};
addEvent = function (elm, event_name, callback){
elm.attachEvent("on"+event_name, callback);
};
}
//
// postMessage
// This is used whereby the browser does not support CORS
//
var xd_iframe, xd_ready, xd_id, xd_counter, xd_queue=[];
function xd(method, url, headers, body, callback){
// This is the origin of the Domain we're opening
var origin = 'https://content.googleapis.com';
// Is this the first time?
if(!xd_iframe){
// ID
xd_id = String(parseInt(Math.random()*1e8,10));
// Create the proxy window
xd_iframe = utils.append('iframe', { src : origin + "/static/proxy.html?jsh=m%3B%2F_%2Fscs%2Fapps-static%2F_%2Fjs%2Fk%3Doz.gapi.en.mMZgig4ibk0.O%2Fm%3D__features__%2Fam%3DEQ%2Frt%3Dj%2Fd%3D1%2Frs%3DAItRSTNZBJcXGialq7mfSUkqsE3kvYwkpQ#parent="+window.location.origin+"&rpctoken="+xd_id,
style : {position:'absolute',left:"-1000px",bottom:0,height:'1px',width:'1px'} }, 'body');
// Listen for on ready events
// Set the window listener to handle responses from this
addEvent( window, "message", function CB(e){
// Try a callback
if(e.origin !== origin){
return;
}
var json;
try{
json = JSON.parse(e.data);
}
catch(ee){
// This wasn't meant to be
return;
}
// Is this the right implementation?
if(json && json.s && json.s === "ready:"+xd_id){
// Yes, it is.
// Lets trigger the pending operations
xd_ready = true;
xd_counter = 0;
for(var i=0;i<xd_queue.length;i++){
xd_queue[i]();
}
}
});
}
//
// Action
// This is the function to call if/once the proxy has successfully loaded
// If makes a call to the IFRAME
var action = function(){
var nav = window.navigator,
position = ++xd_counter,
qs = utils.param(url.match(/\?.+/)[0]);
var token = qs.access_token;
delete qs.access_token;
// The endpoint is ready send the response
var message = JSON.stringify({
"s":"makeHttpRequests",
"f":"..",
"c":position,
"a":[[{
"key":"gapiRequest",
"params":{
"url":url.replace(/(^https?\:\/\/[^\/]+|\?.+$)/,''), // just the pathname
"httpMethod":method.toUpperCase(),
"body": body,
"headers":{
"Authorization":":Bearer "+token,
"Content-Type":headers['content-type'],
"X-Origin":window.location.origin,
"X-ClientDetails":"appVersion="+nav.appVersion+"&platform="+nav.platform+"&userAgent="+nav.userAgent
},
"urlParams": qs,
"clientName":"google-api-javascript-client",
"clientVersion":"1.1.0-beta"
}
}]],
"t":xd_id,
"l":false,
"g":true,
"r":".."
});
addEvent( window, "message", function CB2(e){
if(e.origin !== origin ){
// not the incoming message we're after
return;
}
// Decode the string
try{
var json = JSON.parse(e.data);
if( json.t === xd_id && json.a[0] === position ){
removeEvent( window, "message", CB2);
callback(JSON.parse(JSON.parse(json.a[1]).gapiRequest.data.body));
}
}
catch(ee){
callback({
error: {
code : "request_error",
message : "Failed to post to Google"
}
});
}
});
// Post a message to iframe once it has loaded
xd_iframe.contentWindow.postMessage(message, '*');
};
//
// Check to see if the proy has loaded,
// If it has then action()!
// Otherwise, xd_queue until the proxy has loaded
if(xd_ready){
action();
}
else{
xd_queue.push(action);
}
}
/**/
//
// Upload to Drive
// If this is PUT then only augment the file uploaded
// PUT https://developers.google.com/drive/v2/reference/files/update
// POST https://developers.google.com/drive/manage-uploads
function uploadDrive(p, callback){
var data = {};
if( p.data && p.data instanceof window.HTMLInputElement ){
p.data = { file : p.data };
}
if( !p.data.name && Object(Object(p.data.file).files).length && p.method === 'post' ){
p.data.name = p.data.file.files[0].name;
}
if(p.method==='post'){
p.data = {
"title": p.data.name,
"parents": [{"id":p.data.parent||'root'}],
"file" : p.data.file
};
}
else{
// Make a reference
data = p.data;
p.data = {};
// Add the parts to change as required
if( data.parent ){
p.data["parents"] = [{"id":p.data.parent||'root'}];
}
if( data.file ){
p.data.file = data.file;
}
if( data.name ){
p.data.title = data.name;
}
}
callback('upload/drive/v2/files'+( data.id ? '/' + data.id : '' )+'?uploadType=multipart');
}
// //
// URLS // URLS
...@@ -3278,15 +3685,49 @@ hello.init({ ...@@ -3278,15 +3685,49 @@ hello.init({
'me/photos' : 'https://picasaweb.google.com/data/feed/api/user/default?alt=json&kind=photo&max-results=@{limit|100}&start-index=@{start|1}', 'me/photos' : 'https://picasaweb.google.com/data/feed/api/user/default?alt=json&kind=photo&max-results=@{limit|100}&start-index=@{start|1}',
// https://developers.google.com/drive/v2/reference/files/list // https://developers.google.com/drive/v2/reference/files/list
'me/files' : 'drive/v2/files?q=%22root%22+in+parents&maxResults=@{limit|100}' 'me/files' : 'drive/v2/files?q=%22@{parent|root}%22+in+parents+and+trashed=false&maxResults=@{limit|100}',
// https://developers.google.com/drive/v2/reference/files/list
'me/folders' : 'drive/v2/files?q=%22@{id|root}%22+in+parents+and+mimeType+=+%22application/vnd.google-apps.folder%22+and+trashed=false&maxResults=@{limit|100}',
// https://developers.google.com/drive/v2/reference/files/list
'me/folder' : 'drive/v2/files?q=%22@{id|root}%22+in+parents+and+trashed=false&maxResults=@{limit|100}'
}, },
// Map post requests
post : { post : {
// 'me/albums' : 'https://picasaweb.google.com/data/feed/api/user/default?alt=json' /*
// PICASA
'me/albums' : function(p, callback){
p.data = {
"title": p.data.name,
"summary": p.data.description,
"category": 'http://schemas.google.com/photos/2007#album'
};
callback('https://picasaweb.google.com/data/feed/api/user/default?alt=json');
},
*/
// DRIVE
'me/files' : uploadDrive,
'me/folders' : function(p, callback){
p.data = {
"title": p.data.name,
"parents": [{"id":p.data.parent||'root'}],
"mimeType": "application/vnd.google-apps.folder"
};
callback('drive/v2/files');
}
},
// Map post requests
put : {
'me/files' : uploadDrive
}, },
// Map DELETE requests // Map DELETE requests
del : { del : {
'me/files' : 'drive/v2/files/@{id}',
'me/folder' : 'drive/v2/files/@{id}'
}, },
wrap : { wrap : {
...@@ -3321,14 +3762,112 @@ hello.init({ ...@@ -3321,14 +3762,112 @@ hello.init({
'default' : gEntry 'default' : gEntry
}, },
xhr : function(p){ xhr : function(p){
if(p.method==='post'){
return false; // Post
if(p.method==='post'||p.method==='put'){
// Does this contain binary data?
if( p.data && utils.hasBinary(p.data) || p.data.file ){
// There is support for CORS via Access Control headers
// ... unless otherwise stated by post/put handlers
p.cors_support = p.cors_support || true;
// There is noway, as it appears, to Upload a file along with its meta data
// So lets cancel the typical approach and use the override '{ api : function() }' below
return false;
}
// Convert the POST into a javascript object
p.data = JSON.stringify(p.data);
p.headers = {
'content-type' : 'application/json'
};
}
return true;
},
//
// Custom API handler, overwrites the default fallbacks
// Performs a postMessage Request
//
api : function(url,p,qs,callback){
// Dont use this function for GET requests
if(p.method==='get'){
return;
}
// Contain inaccessible binary data?
// If there is no "files" property on an INPUT then we can't get the data
if( "file" in p.data && utils.domInstance('input', p.data.file ) && !( "files" in p.data.file ) ){
callback({
error : {
code : 'request_invalid',
message : "Sorry, can't upload your files to Google Drive in this browser"
}
});
}
// Extract the file, if it exists from the data object
// If the File is an INPUT element lets just concern ourselves with the NodeList
var file;
if( "file" in p.data ){
file = p.data.file;
delete p.data.file;
if( typeof(file)==='object' && "files" in file){
// Assign the NodeList
file = file.files;
}
if(!file || !file.length){
callback({
error : {
code : 'request_invalid',
message : 'There were no files attached with this request to upload'
}
});
return;
}
} }
// p.data.mimeType = Object(file[0]).type || 'application/octet-stream';
// Construct a multipart message
var parts = new Multipart();
parts.append( JSON.stringify(p.data), 'application/json');
// Read the file into a base64 string... yep a hassle, i know
// FormData doesn't let us assign our own Multipart headers and HTTP Content-Type
// Alas GoogleApi need these in a particular format
if(file){
parts.append( file );
}
parts.onready(function(body, boundary){
// Does this userAgent and endpoint support CORS?
if( p.cors_support ){
// Deliver via
utils.xhr( p.method, utils.qs(url,qs), {
'content-type' : 'multipart/related; boundary="'+boundary+'"'
}, body, callback );
}
else{
// Otherwise lets POST the data the good old fashioned way postMessage
xd( p.method, utils.qs(url,qs), {
'content-type' : 'multipart/related; boundary="'+boundary+'"'
}, body, callback );
}
});
return true; return true;
} }
} }
}); });
})(hello); })(hello, window);
// //
// Instagram // Instagram
// //
...@@ -3834,6 +4373,17 @@ function formatFriends(o){ ...@@ -3834,6 +4373,17 @@ function formatFriends(o){
return o; return o;
} }
function dataURItoBlob(dataURI) {
var reg = /^data\:([^;,]+(\;charset=[^;,]+)?)(\;base64)?,/i;
var m = dataURI.match(reg);
var binary = atob(dataURI.replace(reg,''));
var array = [];
for(var i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], {type: m[1]});
}
hello.init({ hello.init({
windows : { windows : {
name : 'Windows live', name : 'Windows live',
...@@ -3852,7 +4402,7 @@ hello.init({ ...@@ -3852,7 +4402,7 @@ hello.init({
events : 'wl.calendars', events : 'wl.calendars',
photos : 'wl.photos', photos : 'wl.photos',
videos : 'wl.photos', videos : 'wl.photos',
friends : '', friends : 'wl.contacts_emails',
files : 'wl.skydrive', files : 'wl.skydrive',
publish : 'wl.share', publish : 'wl.share',
...@@ -3870,7 +4420,7 @@ hello.init({ ...@@ -3870,7 +4420,7 @@ hello.init({
// Friends // Friends
"me" : "me", "me" : "me",
"me/friends" : "me/friends", "me/friends" : "me/friends",
"me/following" : "me/friends", "me/following" : "me/contacts",
"me/followers" : "me/friends", "me/followers" : "me/friends",
"me/albums" : 'me/albums', "me/albums" : 'me/albums',
...@@ -3880,7 +4430,7 @@ hello.init({ ...@@ -3880,7 +4430,7 @@ hello.init({
"me/photo" : '@{id}', "me/photo" : '@{id}',
// FILES // FILES
"me/files" : '@{id|me/skydrive}/files', "me/files" : '@{parent|me/skydrive}/files',
"me/folders" : '@{id|me/skydrive}/files', "me/folders" : '@{id|me/skydrive}/files',
"me/folder" : '@{id|me/skydrive}/files' "me/folder" : '@{id|me/skydrive}/files'
...@@ -3888,19 +4438,19 @@ hello.init({ ...@@ -3888,19 +4438,19 @@ hello.init({
// Map POST requests // Map POST requests
post : { post : {
"me/feed" : "me/share",
"me/share" : "me/share",
"me/albums" : "me/albums", "me/albums" : "me/albums",
"me/album" : "@{id}/files", "me/album" : "@{id}/files",
"me/folders" : '@{id|me/skydrive/}', "me/folders" : '@{id|me/skydrive/}',
"me/files" : "@{id|me/skydrive/}/files" "me/files" : "@{parent|me/skydrive/}/files"
}, },
// Map DELETE requests // Map DELETE requests
del : { del : {
// Include the data[id] in the path // Include the data[id] in the path
"me/album" : '@{id}', "me/album" : '@{id}',
"me/photo" : '@{id}',
"me/folder" : '@{id}',
"me/files" : '@{id}' "me/files" : '@{id}'
}, },
...@@ -3933,7 +4483,21 @@ hello.init({ ...@@ -3933,7 +4483,21 @@ hello.init({
return o; return o;
} }
}, },
xhr : false, xhr : function(p){
if( p.method !== 'get' && p.method !== 'delete' && !hello.utils.hasBinary(p.data) ){
// Does this have a data-uri to upload as a file?
if( typeof( p.data.file ) === 'string' ){
p.data.file = dataURItoBlob(p.data.file);
}else{
p.data = JSON.stringify(p.data);
p.headers = {
'Content-Type' : 'application/json'
};
}
}
return true;
},
jsonp : function(p){ jsonp : function(p){
if( p.method.toLowerCase() !== 'get' && !hello.utils.hasBinary(p.data) ){ if( p.method.toLowerCase() !== 'get' && !hello.utils.hasBinary(p.data) ){
//p.data = {data: JSON.stringify(p.data), method: p.method.toLowerCase()}; //p.data = {data: JSON.stringify(p.data), method: p.method.toLowerCase()};
...@@ -4004,7 +4568,7 @@ function paging(res){ ...@@ -4004,7 +4568,7 @@ function paging(res){
} }
var yql = function(q){ var yql = function(q){
return 'http://query.yahooapis.com/v1/yql?q=' + (q + ' limit @{limit|100} offset @{start|0}').replace(" ", '%20') + "&format=json"; return 'https://query.yahooapis.com/v1/yql?q=' + (q + ' limit @{limit|100} offset @{start|0}').replace(/\s/g, '%20') + "&format=json";
}; };
hello.init({ hello.init({
...@@ -4041,9 +4605,9 @@ hello.init({ ...@@ -4041,9 +4605,9 @@ hello.init({
base : "https://social.yahooapis.com/v1/", base : "https://social.yahooapis.com/v1/",
get : { get : {
"me" : yql('select * from social.profile where guid=me'), "me" : yql('select * from social.profile(0) where guid=me'),
"me/friends" : yql('select * from social.contacts where guid=me'), "me/friends" : yql('select * from social.contacts(0) where guid=me'),
"me/following" : yql('select * from social.contacts where guid=me') "me/following" : yql('select * from social.contacts(0) where guid=me')
}, },
wrap : { wrap : {
me : function(o){ me : function(o){
......
// i18next, v1.7.1 // i18next, v1.7.2
// Copyright (c)2013 Jan Mühlemann (jamuhl). // Copyright (c)2014 Jan Mühlemann (jamuhl).
// Distributed under MIT license // Distributed under MIT license
// http://i18next.com // http://i18next.com
(function() { (function() {
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
return -1; return -1;
} }
} }
// add lastIndexOf to non ECMA-262 standard compliant browsers // add lastIndexOf to non ECMA-262 standard compliant browsers
if (!Array.prototype.lastIndexOf) { if (!Array.prototype.lastIndexOf) {
Array.prototype.lastIndexOf = function(searchElement /*, fromIndex*/) { Array.prototype.lastIndexOf = function(searchElement /*, fromIndex*/) {
...@@ -79,7 +79,7 @@ ...@@ -79,7 +79,7 @@
, initialized = false; , initialized = false;
// Export the i18next object for **CommonJS**. // Export the i18next object for **CommonJS**.
// If we're not in CommonJS, add `i18n` to the // If we're not in CommonJS, add `i18n` to the
// global object or to jquery. // global object or to jquery.
if (typeof module !== 'undefined' && module.exports) { if (typeof module !== 'undefined' && module.exports) {
...@@ -88,7 +88,7 @@ ...@@ -88,7 +88,7 @@
if ($) { if ($) {
$.i18n = $.i18n || i18n; $.i18n = $.i18n || i18n;
} }
root.i18n = root.i18n || i18n; root.i18n = root.i18n || i18n;
} }
// defaults // defaults
...@@ -103,27 +103,28 @@ ...@@ -103,27 +103,28 @@
detectLngQS: 'setLng', detectLngQS: 'setLng',
ns: 'translation', ns: 'translation',
fallbackOnNull: true, fallbackOnNull: true,
fallbackOnEmpty: false,
fallbackToDefaultNS: false, fallbackToDefaultNS: false,
nsseparator: ':', nsseparator: ':',
keyseparator: '.', keyseparator: '.',
selectorAttr: 'data-i18n', selectorAttr: 'data-i18n',
debug: false, debug: false,
resGetPath: 'locales/__lng__/__ns__.json', resGetPath: 'locales/__lng__/__ns__.json',
resPostPath: 'locales/add/__lng__/__ns__', resPostPath: 'locales/add/__lng__/__ns__',
getAsync: true, getAsync: true,
postAsync: true, postAsync: true,
resStore: undefined, resStore: undefined,
useLocalStorage: false, useLocalStorage: false,
localStorageExpirationTime: 7*24*60*60*1000, localStorageExpirationTime: 7*24*60*60*1000,
dynamicLoad: false, dynamicLoad: false,
sendMissing: false, sendMissing: false,
sendMissingTo: 'fallback', // current | all sendMissingTo: 'fallback', // current | all
sendType: 'POST', sendType: 'POST',
interpolationPrefix: '__', interpolationPrefix: '__',
interpolationSuffix: '__', interpolationSuffix: '__',
reusePrefix: '$t(', reusePrefix: '$t(',
...@@ -132,7 +133,7 @@ ...@@ -132,7 +133,7 @@
pluralNotFound: ['plural_not_found', Math.random()].join(''), pluralNotFound: ['plural_not_found', Math.random()].join(''),
contextNotFound: ['context_not_found', Math.random()].join(''), contextNotFound: ['context_not_found', Math.random()].join(''),
escapeInterpolation: false, escapeInterpolation: false,
setJqueryExt: true, setJqueryExt: true,
defaultValueFromContent: true, defaultValueFromContent: true,
useDataAttrOptions: false, useDataAttrOptions: false,
...@@ -140,26 +141,27 @@ ...@@ -140,26 +141,27 @@
useCookie: true, useCookie: true,
cookieName: 'i18next', cookieName: 'i18next',
cookieDomain: undefined, cookieDomain: undefined,
objectTreeKeyHandler: undefined,
postProcess: undefined, postProcess: undefined,
parseMissingKey: undefined, parseMissingKey: undefined,
shortcutFunction: 'sprintf' // or: defaultValue shortcutFunction: 'sprintf' // or: defaultValue
}; };
function _extend(target, source) { function _extend(target, source) {
if (!source || typeof source === 'function') { if (!source || typeof source === 'function') {
return target; return target;
} }
for (var attr in source) { target[attr] = source[attr]; } for (var attr in source) { target[attr] = source[attr]; }
return target; return target;
} }
function _each(object, callback, args) { function _each(object, callback, args) {
var name, i = 0, var name, i = 0,
length = object.length, length = object.length,
isObj = length === undefined || typeof object === "function"; isObj = length === undefined || typeof object === "function";
if (args) { if (args) {
if (isObj) { if (isObj) {
for (name in object) { for (name in object) {
...@@ -174,7 +176,7 @@ ...@@ -174,7 +176,7 @@
} }
} }
} }
// A special, fast, case for the most common use of each // A special, fast, case for the most common use of each
} else { } else {
if (isObj) { if (isObj) {
...@@ -191,10 +193,10 @@ ...@@ -191,10 +193,10 @@
} }
} }
} }
return object; return object;
} }
var _entityMap = { var _entityMap = {
"&": "&amp;", "&": "&amp;",
"<": "&lt;", "<": "&lt;",
...@@ -203,19 +205,19 @@ ...@@ -203,19 +205,19 @@
"'": '&#39;', "'": '&#39;',
"/": '&#x2F;' "/": '&#x2F;'
}; };
function _escape(data) { function _escape(data) {
if (typeof data === 'string') { if (typeof data === 'string') {
return data.replace(/[&<>"'\/]/g, function (s) { return data.replace(/[&<>"'\/]/g, function (s) {
return _entityMap[s]; return _entityMap[s];
}); });
}else{ }else{
return data; return data;
} }
} }
function _ajax(options) { function _ajax(options) {
// v0.5.0 of https://github.com/goloroden/http.js // v0.5.0 of https://github.com/goloroden/http.js
var getXhr = function (callback) { var getXhr = function (callback) {
// Use the native XHR object if the browser supports it. // Use the native XHR object if the browser supports it.
...@@ -229,33 +231,33 @@ ...@@ -229,33 +231,33 @@
return callback(null, new ActiveXObject("Microsoft.XMLHTTP")); return callback(null, new ActiveXObject("Microsoft.XMLHTTP"));
} }
} }
// If no XHR support was found, throw an error. // If no XHR support was found, throw an error.
return callback(new Error()); return callback(new Error());
}; };
var encodeUsingUrlEncoding = function (data) { var encodeUsingUrlEncoding = function (data) {
if(typeof data === 'string') { if(typeof data === 'string') {
return data; return data;
} }
var result = []; var result = [];
for(var dataItem in data) { for(var dataItem in data) {
if(data.hasOwnProperty(dataItem)) { if(data.hasOwnProperty(dataItem)) {
result.push(encodeURIComponent(dataItem) + '=' + encodeURIComponent(data[dataItem])); result.push(encodeURIComponent(dataItem) + '=' + encodeURIComponent(data[dataItem]));
} }
} }
return result.join('&'); return result.join('&');
}; };
var utf8 = function (text) { var utf8 = function (text) {
text = text.replace(/\r\n/g, '\n'); text = text.replace(/\r\n/g, '\n');
var result = ''; var result = '';
for(var i = 0; i < text.length; i++) { for(var i = 0; i < text.length; i++) {
var c = text.charCodeAt(i); var c = text.charCodeAt(i);
if(c < 128) { if(c < 128) {
result += String.fromCharCode(c); result += String.fromCharCode(c);
} else if((c > 127) && (c < 2048)) { } else if((c > 127) && (c < 2048)) {
...@@ -267,35 +269,35 @@ ...@@ -267,35 +269,35 @@
result += String.fromCharCode((c & 63) | 128); result += String.fromCharCode((c & 63) | 128);
} }
} }
return result; return result;
}; };
var base64 = function (text) { var base64 = function (text) {
var keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; var keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
text = utf8(text); text = utf8(text);
var result = '', var result = '',
chr1, chr2, chr3, chr1, chr2, chr3,
enc1, enc2, enc3, enc4, enc1, enc2, enc3, enc4,
i = 0; i = 0;
do { do {
chr1 = text.charCodeAt(i++); chr1 = text.charCodeAt(i++);
chr2 = text.charCodeAt(i++); chr2 = text.charCodeAt(i++);
chr3 = text.charCodeAt(i++); chr3 = text.charCodeAt(i++);
enc1 = chr1 >> 2; enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63; enc4 = chr3 & 63;
if(isNaN(chr2)) { if(isNaN(chr2)) {
enc3 = enc4 = 64; enc3 = enc4 = 64;
} else if(isNaN(chr3)) { } else if(isNaN(chr3)) {
enc4 = 64; enc4 = 64;
} }
result += result +=
keyStr.charAt(enc1) + keyStr.charAt(enc1) +
keyStr.charAt(enc2) + keyStr.charAt(enc2) +
...@@ -304,14 +306,14 @@ ...@@ -304,14 +306,14 @@
chr1 = chr2 = chr3 = ''; chr1 = chr2 = chr3 = '';
enc1 = enc2 = enc3 = enc4 = ''; enc1 = enc2 = enc3 = enc4 = '';
} while(i < text.length); } while(i < text.length);
return result; return result;
}; };
var mergeHeaders = function () { var mergeHeaders = function () {
// Use the first header object as base. // Use the first header object as base.
var result = arguments[0]; var result = arguments[0];
// Iterate through the remaining header objects and add them. // Iterate through the remaining header objects and add them.
for(var i = 1; i < arguments.length; i++) { for(var i = 1; i < arguments.length; i++) {
var currentHeaders = arguments[i]; var currentHeaders = arguments[i];
...@@ -321,39 +323,39 @@ ...@@ -321,39 +323,39 @@
} }
} }
} }
// Return the merged headers. // Return the merged headers.
return result; return result;
}; };
var ajax = function (method, url, options, callback) { var ajax = function (method, url, options, callback) {
// Adjust parameters. // Adjust parameters.
if(typeof options === 'function') { if(typeof options === 'function') {
callback = options; callback = options;
options = {}; options = {};
} }
// Set default parameter values. // Set default parameter values.
options.cache = options.cache || false; options.cache = options.cache || false;
options.data = options.data || {}; options.data = options.data || {};
options.headers = options.headers || {}; options.headers = options.headers || {};
options.jsonp = options.jsonp || false; options.jsonp = options.jsonp || false;
options.async = options.async === undefined ? true : options.async; options.async = options.async === undefined ? true : options.async;
// Merge the various header objects. // Merge the various header objects.
var headers = mergeHeaders({ var headers = mergeHeaders({
'accept': '*/*', 'accept': '*/*',
'content-type': 'application/x-www-form-urlencoded;charset=UTF-8' 'content-type': 'application/x-www-form-urlencoded;charset=UTF-8'
}, ajax.headers, options.headers); }, ajax.headers, options.headers);
// Encode the data according to the content-type. // Encode the data according to the content-type.
var payload; var payload;
if (headers['content-type'] === 'application/json') { if (headers['content-type'] === 'application/json') {
payload = JSON.stringify(options.data); payload = JSON.stringify(options.data);
} else { } else {
payload = encodeUsingUrlEncoding(options.data); payload = encodeUsingUrlEncoding(options.data);
} }
// Specially prepare GET requests: Setup the query string, handle caching and make a JSONP call // Specially prepare GET requests: Setup the query string, handle caching and make a JSONP call
// if neccessary. // if neccessary.
if(method === 'GET') { if(method === 'GET') {
...@@ -363,18 +365,18 @@ ...@@ -363,18 +365,18 @@
queryString.push(payload); queryString.push(payload);
payload = null; payload = null;
} }
// Handle caching. // Handle caching.
if(!options.cache) { if(!options.cache) {
queryString.push('_=' + (new Date()).getTime()); queryString.push('_=' + (new Date()).getTime());
} }
// If neccessary prepare the query string for a JSONP call. // If neccessary prepare the query string for a JSONP call.
if(options.jsonp) { if(options.jsonp) {
queryString.push('callback=' + options.jsonp); queryString.push('callback=' + options.jsonp);
queryString.push('jsonp=' + options.jsonp); queryString.push('jsonp=' + options.jsonp);
} }
// Merge the query string and attach it to the url. // Merge the query string and attach it to the url.
queryString = queryString.join('&'); queryString = queryString.join('&');
if (queryString.length > 1) { if (queryString.length > 1) {
...@@ -384,116 +386,116 @@ ...@@ -384,116 +386,116 @@
url += '?' + queryString; url += '?' + queryString;
} }
} }
// Make a JSONP call if neccessary. // Make a JSONP call if neccessary.
if(options.jsonp) { if(options.jsonp) {
var head = document.getElementsByTagName('head')[0]; var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script'); var script = document.createElement('script');
script.type = 'text/javascript'; script.type = 'text/javascript';
script.src = url; script.src = url;
head.appendChild(script); head.appendChild(script);
return; return;
} }
} }
// Since we got here, it is no JSONP request, so make a normal XHR request. // Since we got here, it is no JSONP request, so make a normal XHR request.
getXhr(function (err, xhr) { getXhr(function (err, xhr) {
if(err) return callback(err); if(err) return callback(err);
// Open the request. // Open the request.
xhr.open(method, url, options.async); xhr.open(method, url, options.async);
// Set the request headers. // Set the request headers.
for(var header in headers) { for(var header in headers) {
if(headers.hasOwnProperty(header)) { if(headers.hasOwnProperty(header)) {
xhr.setRequestHeader(header, headers[header]); xhr.setRequestHeader(header, headers[header]);
} }
} }
// Handle the request events. // Handle the request events.
xhr.onreadystatechange = function () { xhr.onreadystatechange = function () {
if(xhr.readyState === 4) { if(xhr.readyState === 4) {
var data = xhr.responseText || ''; var data = xhr.responseText || '';
// If no callback is given, return. // If no callback is given, return.
if(!callback) { if(!callback) {
return; return;
} }
// Return an object that provides access to the data as text and JSON. // Return an object that provides access to the data as text and JSON.
callback(xhr.status, { callback(xhr.status, {
text: function () { text: function () {
return data; return data;
}, },
json: function () { json: function () {
return JSON.parse(data); return JSON.parse(data);
} }
}); });
} }
}; };
// Actually send the XHR request. // Actually send the XHR request.
xhr.send(payload); xhr.send(payload);
}); });
}; };
// Define the external interface. // Define the external interface.
var http = { var http = {
authBasic: function (username, password) { authBasic: function (username, password) {
ajax.headers['Authorization'] = 'Basic ' + base64(username + ':' + password); ajax.headers['Authorization'] = 'Basic ' + base64(username + ':' + password);
}, },
connect: function (url, options, callback) { connect: function (url, options, callback) {
return ajax('CONNECT', url, options, callback); return ajax('CONNECT', url, options, callback);
}, },
del: function (url, options, callback) { del: function (url, options, callback) {
return ajax('DELETE', url, options, callback); return ajax('DELETE', url, options, callback);
}, },
get: function (url, options, callback) { get: function (url, options, callback) {
return ajax('GET', url, options, callback); return ajax('GET', url, options, callback);
}, },
head: function (url, options, callback) { head: function (url, options, callback) {
return ajax('HEAD', url, options, callback); return ajax('HEAD', url, options, callback);
}, },
headers: function (headers) { headers: function (headers) {
ajax.headers = headers || {}; ajax.headers = headers || {};
}, },
isAllowed: function (url, verb, callback) { isAllowed: function (url, verb, callback) {
this.options(url, function (status, data) { this.options(url, function (status, data) {
callback(data.text().indexOf(verb) !== -1); callback(data.text().indexOf(verb) !== -1);
}); });
}, },
options: function (url, options, callback) { options: function (url, options, callback) {
return ajax('OPTIONS', url, options, callback); return ajax('OPTIONS', url, options, callback);
}, },
patch: function (url, options, callback) { patch: function (url, options, callback) {
return ajax('PATCH', url, options, callback); return ajax('PATCH', url, options, callback);
}, },
post: function (url, options, callback) { post: function (url, options, callback) {
return ajax('POST', url, options, callback); return ajax('POST', url, options, callback);
}, },
put: function (url, options, callback) { put: function (url, options, callback) {
return ajax('PUT', url, options, callback); return ajax('PUT', url, options, callback);
}, },
trace: function (url, options, callback) { trace: function (url, options, callback) {
return ajax('TRACE', url, options, callback); return ajax('TRACE', url, options, callback);
} }
}; };
var methode = options.type ? options.type.toLowerCase() : 'get'; var methode = options.type ? options.type.toLowerCase() : 'get';
http[methode](options.url, options, function (status, data) { http[methode](options.url, options, function (status, data) {
if (status === 200) { if (status === 200) {
options.success(data.json(), status, null); options.success(data.json(), status, null);
...@@ -502,7 +504,7 @@ ...@@ -502,7 +504,7 @@
} }
}); });
} }
var _cookie = { var _cookie = {
create: function(name,value,minutes,domain) { create: function(name,value,minutes,domain) {
var expires; var expires;
...@@ -515,7 +517,7 @@ ...@@ -515,7 +517,7 @@
domain = (domain)? "domain="+domain+";" : ""; domain = (domain)? "domain="+domain+";" : "";
document.cookie = name+"="+value+expires+";"+domain+"path=/"; document.cookie = name+"="+value+expires+";"+domain+"path=/";
}, },
read: function(name) { read: function(name) {
var nameEQ = name + "="; var nameEQ = name + "=";
var ca = document.cookie.split(';'); var ca = document.cookie.split(';');
...@@ -526,26 +528,26 @@ ...@@ -526,26 +528,26 @@
} }
return null; return null;
}, },
remove: function(name) { remove: function(name) {
this.create(name,"",-1); this.create(name,"",-1);
} }
}; };
var cookie_noop = { var cookie_noop = {
create: function(name,value,minutes,domain) {}, create: function(name,value,minutes,domain) {},
read: function(name) { return null; }, read: function(name) { return null; },
remove: function(name) {} remove: function(name) {}
}; };
// move dependent functions to a container so that // move dependent functions to a container so that
// they can be overriden easier in no jquery environment (node.js) // they can be overriden easier in no jquery environment (node.js)
var f = { var f = {
extend: $ ? $.extend : _extend, extend: $ ? $.extend : _extend,
each: $ ? $.each : _each, each: $ ? $.each : _each,
ajax: $ ? $.ajax : _ajax, ajax: $ ? $.ajax : (typeof document !== 'undefined' ? _ajax : function() {}),
cookie: typeof document !== 'undefined' ? _cookie : cookie_noop, cookie: typeof document !== 'undefined' ? _cookie : cookie_noop,
detectLanguage: detectLanguage, detectLanguage: detectLanguage,
escape: _escape, escape: _escape,
...@@ -556,19 +558,19 @@ ...@@ -556,19 +558,19 @@
var languages = []; var languages = [];
if (typeof lng === 'string' && lng.indexOf('-') > -1) { if (typeof lng === 'string' && lng.indexOf('-') > -1) {
var parts = lng.split('-'); var parts = lng.split('-');
lng = o.lowerCaseLng ? lng = o.lowerCaseLng ?
parts[0].toLowerCase() + '-' + parts[1].toLowerCase() : parts[0].toLowerCase() + '-' + parts[1].toLowerCase() :
parts[0].toLowerCase() + '-' + parts[1].toUpperCase(); parts[0].toLowerCase() + '-' + parts[1].toUpperCase();
if (o.load !== 'unspecific') languages.push(lng); if (o.load !== 'unspecific') languages.push(lng);
if (o.load !== 'current') languages.push(parts[0]); if (o.load !== 'current') languages.push(parts[0]);
} else { } else {
languages.push(lng); languages.push(lng);
} }
if (languages.indexOf(o.fallbackLng) === -1 && o.fallbackLng) languages.push(o.fallbackLng); if (languages.indexOf(o.fallbackLng) === -1 && o.fallbackLng) languages.push(o.fallbackLng);
return languages; return languages;
}, },
regexEscape: function(str) { regexEscape: function(str) {
...@@ -576,32 +578,32 @@ ...@@ -576,32 +578,32 @@
} }
}; };
function init(options, cb) { function init(options, cb) {
if (typeof options === 'function') { if (typeof options === 'function') {
cb = options; cb = options;
options = {}; options = {};
} }
options = options || {}; options = options || {};
// override defaults with passed in options // override defaults with passed in options
f.extend(o, options); f.extend(o, options);
delete o.fixLng; /* passed in each time */ delete o.fixLng; /* passed in each time */
// create namespace object if namespace is passed in as string // create namespace object if namespace is passed in as string
if (typeof o.ns == 'string') { if (typeof o.ns == 'string') {
o.ns = { namespaces: [o.ns], defaultNs: o.ns}; o.ns = { namespaces: [o.ns], defaultNs: o.ns};
} }
// fallback namespaces // fallback namespaces
if (typeof o.fallbackNS == 'string') { if (typeof o.fallbackNS == 'string') {
o.fallbackNS = [o.fallbackNS]; o.fallbackNS = [o.fallbackNS];
} }
// escape prefix/suffix // escape prefix/suffix
o.interpolationPrefixEscaped = f.regexEscape(o.interpolationPrefix); o.interpolationPrefixEscaped = f.regexEscape(o.interpolationPrefix);
o.interpolationSuffixEscaped = f.regexEscape(o.interpolationSuffix); o.interpolationSuffixEscaped = f.regexEscape(o.interpolationSuffix);
if (!o.lng) o.lng = f.detectLanguage(); if (!o.lng) o.lng = f.detectLanguage();
if (o.lng) { if (o.lng) {
// set cookie with lng set (as detectLanguage will set cookie on need) // set cookie with lng set (as detectLanguage will set cookie on need)
if (o.useCookie) f.cookie.create(o.cookieName, o.lng, o.cookieExpirationTime, o.cookieDomain); if (o.useCookie) f.cookie.create(o.cookieName, o.lng, o.cookieExpirationTime, o.cookieDomain);
...@@ -609,11 +611,11 @@ ...@@ -609,11 +611,11 @@
o.lng = o.fallbackLng; o.lng = o.fallbackLng;
if (o.useCookie) f.cookie.remove(o.cookieName); if (o.useCookie) f.cookie.remove(o.cookieName);
} }
languages = f.toLanguages(o.lng); languages = f.toLanguages(o.lng);
currentLng = languages[0]; currentLng = languages[0];
f.log('currentLng set to: ' + currentLng); f.log('currentLng set to: ' + currentLng);
var lngTranslate = translate; var lngTranslate = translate;
if (options.fixLng) { if (options.fixLng) {
lngTranslate = function(key, options) { lngTranslate = function(key, options) {
...@@ -623,18 +625,18 @@ ...@@ -623,18 +625,18 @@
}; };
lngTranslate.lng = currentLng; lngTranslate.lng = currentLng;
} }
pluralExtensions.setCurrentLng(currentLng); pluralExtensions.setCurrentLng(currentLng);
// add JQuery extensions // add JQuery extensions
if ($ && o.setJqueryExt) addJqueryFunct(); if ($ && o.setJqueryExt) addJqueryFunct();
// jQuery deferred // jQuery deferred
var deferred; var deferred;
if ($ && $.Deferred) { if ($ && $.Deferred) {
deferred = $.Deferred(); deferred = $.Deferred();
} }
// return immidiatly if res are passed in // return immidiatly if res are passed in
if (o.resStore) { if (o.resStore) {
resStore = o.resStore; resStore = o.resStore;
...@@ -644,7 +646,7 @@ ...@@ -644,7 +646,7 @@
if (deferred) return deferred.promise(); if (deferred) return deferred.promise();
return; return;
} }
// languages to load // languages to load
var lngsToLoad = f.toLanguages(o.lng); var lngsToLoad = f.toLanguages(o.lng);
if (typeof o.preload === 'string') o.preload = [o.preload]; if (typeof o.preload === 'string') o.preload = [o.preload];
...@@ -656,16 +658,16 @@ ...@@ -656,16 +658,16 @@
} }
} }
} }
// else load them // else load them
i18n.sync.load(lngsToLoad, o, function(err, store) { i18n.sync.load(lngsToLoad, o, function(err, store) {
resStore = store; resStore = store;
initialized = true; initialized = true;
if (cb) cb(lngTranslate); if (cb) cb(lngTranslate);
if (deferred) deferred.resolve(lngTranslate); if (deferred) deferred.resolve(lngTranslate);
}); });
if (deferred) return deferred.promise(); if (deferred) return deferred.promise();
} }
function preload(lngs, cb) { function preload(lngs, cb) {
...@@ -677,7 +679,7 @@ ...@@ -677,7 +679,7 @@
} }
return init(cb); return init(cb);
} }
function addResourceBundle(lng, ns, resources) { function addResourceBundle(lng, ns, resources) {
if (typeof ns !== 'string') { if (typeof ns !== 'string') {
resources = ns; resources = ns;
...@@ -685,21 +687,30 @@ ...@@ -685,21 +687,30 @@
} else if (o.ns.namespaces.indexOf(ns) < 0) { } else if (o.ns.namespaces.indexOf(ns) < 0) {
o.ns.namespaces.push(ns); o.ns.namespaces.push(ns);
} }
resStore[lng] = resStore[lng] || {}; resStore[lng] = resStore[lng] || {};
resStore[lng][ns] = resStore[lng][ns] || {}; resStore[lng][ns] = resStore[lng][ns] || {};
f.extend(resStore[lng][ns], resources); f.extend(resStore[lng][ns], resources);
} }
function removeResourceBundle(lng, ns) {
if (typeof ns !== 'string') {
ns = o.ns.defaultNs;
}
resStore[lng] = resStore[lng] || {};
resStore[lng][ns] = {};
}
function setDefaultNamespace(ns) { function setDefaultNamespace(ns) {
o.ns.defaultNs = ns; o.ns.defaultNs = ns;
} }
function loadNamespace(namespace, cb) { function loadNamespace(namespace, cb) {
loadNamespaces([namespace], cb); loadNamespaces([namespace], cb);
} }
function loadNamespaces(namespaces, cb) { function loadNamespaces(namespaces, cb) {
var opts = { var opts = {
dynamicLoad: o.dynamicLoad, dynamicLoad: o.dynamicLoad,
...@@ -708,7 +719,7 @@ ...@@ -708,7 +719,7 @@
customLoad: o.customLoad, customLoad: o.customLoad,
ns: { namespaces: namespaces, defaultNs: ''} /* new namespaces to load */ ns: { namespaces: namespaces, defaultNs: ''} /* new namespaces to load */
}; };
// languages to load // languages to load
var lngsToLoad = f.toLanguages(o.lng); var lngsToLoad = f.toLanguages(o.lng);
if (typeof o.preload === 'string') o.preload = [o.preload]; if (typeof o.preload === 'string') o.preload = [o.preload];
...@@ -720,7 +731,7 @@ ...@@ -720,7 +731,7 @@
} }
} }
} }
// check if we have to load // check if we have to load
var lngNeedLoad = []; var lngNeedLoad = [];
for (var a = 0, lenA = lngsToLoad.length; a < lenA; a++) { for (var a = 0, lenA = lngsToLoad.length; a < lenA; a++) {
...@@ -733,26 +744,26 @@ ...@@ -733,26 +744,26 @@
} else { } else {
needLoad = true; needLoad = true;
} }
if (needLoad) lngNeedLoad.push(lngsToLoad[a]); if (needLoad) lngNeedLoad.push(lngsToLoad[a]);
} }
if (lngNeedLoad.length) { if (lngNeedLoad.length) {
i18n.sync._fetch(lngNeedLoad, opts, function(err, store) { i18n.sync._fetch(lngNeedLoad, opts, function(err, store) {
var todo = namespaces.length * lngNeedLoad.length; var todo = namespaces.length * lngNeedLoad.length;
// load each file individual // load each file individual
f.each(namespaces, function(nsIndex, nsValue) { f.each(namespaces, function(nsIndex, nsValue) {
// append namespace to namespace array // append namespace to namespace array
if (o.ns.namespaces.indexOf(nsValue) < 0) { if (o.ns.namespaces.indexOf(nsValue) < 0) {
o.ns.namespaces.push(nsValue); o.ns.namespaces.push(nsValue);
} }
f.each(lngNeedLoad, function(lngIndex, lngValue) { f.each(lngNeedLoad, function(lngIndex, lngValue) {
resStore[lngValue] = resStore[lngValue] || {}; resStore[lngValue] = resStore[lngValue] || {};
resStore[lngValue][nsValue] = store[lngValue][nsValue]; resStore[lngValue][nsValue] = store[lngValue][nsValue];
todo--; // wait for all done befor callback todo--; // wait for all done befor callback
if (todo === 0 && cb) { if (todo === 0 && cb) {
if (o.useLocalStorage) i18n.sync._storeLocal(resStore); if (o.useLocalStorage) i18n.sync._storeLocal(resStore);
...@@ -765,91 +776,99 @@ ...@@ -765,91 +776,99 @@
if (cb) cb(); if (cb) cb();
} }
} }
function setLng(lng, options, cb) { function setLng(lng, options, cb) {
if (typeof options === 'function') { if (typeof options === 'function') {
cb = options; cb = options;
options = {}; options = {};
} else if (!options) {
options = {};
} }
options.lng = lng; options.lng = lng;
return init(options, cb); return init(options, cb);
} }
function lng() { function lng() {
return currentLng; return currentLng;
} }
function addJqueryFunct() { function addJqueryFunct() {
// $.t shortcut // $.t shortcut
$.t = $.t || translate; $.t = $.t || translate;
function parse(ele, key, options) { function parse(ele, key, options) {
if (key.length === 0) return; if (key.length === 0) return;
var attr = 'text'; var attr = 'text';
if (key.indexOf('[') === 0) { if (key.indexOf('[') === 0) {
var parts = key.split(']'); var parts = key.split(']');
key = parts[1]; key = parts[1];
attr = parts[0].substr(1, parts[0].length-1); attr = parts[0].substr(1, parts[0].length-1);
} }
if (key.indexOf(';') === key.length-1) { if (key.indexOf(';') === key.length-1) {
key = key.substr(0, key.length-2); key = key.substr(0, key.length-2);
} }
var optionsToUse; var optionsToUse;
if (attr === 'html') { if (attr === 'html') {
optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.html() }, options) : options; optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.html() }, options) : options;
ele.html($.t(key, optionsToUse)); ele.html($.t(key, optionsToUse));
} } else if (attr === 'text') {
else if (attr === 'text') {
optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.text() }, options) : options; optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.text() }, options) : options;
ele.text($.t(key, optionsToUse)); ele.text($.t(key, optionsToUse));
} else if (attr === 'prepend') {
optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.html() }, options) : options;
ele.prepend($.t(key, optionsToUse));
} else if (attr === 'append') {
optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.html() }, options) : options;
ele.append($.t(key, optionsToUse));
} else { } else {
optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.attr(attr) }, options) : options; optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.attr(attr) }, options) : options;
ele.attr(attr, $.t(key, optionsToUse)); ele.attr(attr, $.t(key, optionsToUse));
} }
} }
function localize(ele, options) { function localize(ele, options) {
var key = ele.attr(o.selectorAttr); var key = ele.attr(o.selectorAttr);
if (!key && typeof key !== 'undefined' && key !== false) key = ele.text() || ele.val(); if (!key && typeof key !== 'undefined' && key !== false) key = ele.text() || ele.val();
if (!key) return; if (!key) return;
var target = ele var target = ele
, targetSelector = ele.data("i18n-target"); , targetSelector = ele.data("i18n-target");
if (targetSelector) { if (targetSelector) {
target = ele.find(targetSelector) || ele; target = ele.find(targetSelector) || ele;
} }
if (!options && o.useDataAttrOptions === true) { if (!options && o.useDataAttrOptions === true) {
options = ele.data("i18n-options"); options = ele.data("i18n-options");
} }
options = options || {}; options = options || {};
if (key.indexOf(';') >= 0) { if (key.indexOf(';') >= 0) {
var keys = key.split(';'); var keys = key.split(';');
$.each(keys, function(m, k) { $.each(keys, function(m, k) {
if (k !== '') parse(target, k, options); if (k !== '') parse(target, k, options);
}); });
} else { } else {
parse(target, key, options); parse(target, key, options);
} }
if (o.useDataAttrOptions === true) ele.data("i18n-options", options); if (o.useDataAttrOptions === true) ele.data("i18n-options", options);
} }
// fn // fn
$.fn.i18n = function (options) { $.fn.i18n = function (options) {
return this.each(function() { return this.each(function() {
// localize element itself // localize element itself
localize($(this), options); localize($(this), options);
// localize childs // localize childs
var elements = $(this).find('[' + o.selectorAttr + ']'); var elements = $(this).find('[' + o.selectorAttr + ']');
elements.each(function() { elements.each(function() {
localize($(this), options); localize($(this), options);
}); });
}); });
...@@ -857,14 +876,14 @@ ...@@ -857,14 +876,14 @@
} }
function applyReplacement(str, replacementHash, nestedKey, options) { function applyReplacement(str, replacementHash, nestedKey, options) {
if (!str) return str; if (!str) return str;
options = options || replacementHash; // first call uses replacement hash combined with options options = options || replacementHash; // first call uses replacement hash combined with options
if (str.indexOf(options.interpolationPrefix || o.interpolationPrefix) < 0) return str; if (str.indexOf(options.interpolationPrefix || o.interpolationPrefix) < 0) return str;
var prefix = options.interpolationPrefix ? f.regexEscape(options.interpolationPrefix) : o.interpolationPrefixEscaped var prefix = options.interpolationPrefix ? f.regexEscape(options.interpolationPrefix) : o.interpolationPrefixEscaped
, suffix = options.interpolationSuffix ? f.regexEscape(options.interpolationSuffix) : o.interpolationSuffixEscaped , suffix = options.interpolationSuffix ? f.regexEscape(options.interpolationSuffix) : o.interpolationSuffixEscaped
, unEscapingSuffix = 'HTML'+suffix; , unEscapingSuffix = 'HTML'+suffix;
f.each(replacementHash, function(key, value) { f.each(replacementHash, function(key, value) {
var nextKey = nestedKey ? nestedKey + o.keyseparator + key : key; var nextKey = nestedKey ? nestedKey + o.keyseparator + key : key;
if (typeof value === 'object' && value !== null) { if (typeof value === 'object' && value !== null) {
...@@ -881,18 +900,18 @@ ...@@ -881,18 +900,18 @@
}); });
return str; return str;
} }
// append it to functions // append it to functions
f.applyReplacement = applyReplacement; f.applyReplacement = applyReplacement;
function applyReuse(translated, options) { function applyReuse(translated, options) {
var comma = ','; var comma = ',';
var options_open = '{'; var options_open = '{';
var options_close = '}'; var options_close = '}';
var opts = f.extend({}, options); var opts = f.extend({}, options);
delete opts.postProcess; delete opts.postProcess;
while (translated.indexOf(o.reusePrefix) != -1) { while (translated.indexOf(o.reusePrefix) != -1) {
replacementCounter++; replacementCounter++;
if (replacementCounter > o.maxRecursion) { break; } // safety net for too much recursion if (replacementCounter > o.maxRecursion) { break; } // safety net for too much recursion
...@@ -900,8 +919,8 @@ ...@@ -900,8 +919,8 @@
var index_of_end_of_closing = translated.indexOf(o.reuseSuffix, index_of_opening) + o.reuseSuffix.length; var index_of_end_of_closing = translated.indexOf(o.reuseSuffix, index_of_opening) + o.reuseSuffix.length;
var token = translated.substring(index_of_opening, index_of_end_of_closing); var token = translated.substring(index_of_opening, index_of_end_of_closing);
var token_without_symbols = token.replace(o.reusePrefix, '').replace(o.reuseSuffix, ''); var token_without_symbols = token.replace(o.reusePrefix, '').replace(o.reuseSuffix, '');
if (token_without_symbols.indexOf(comma) != -1) { if (token_without_symbols.indexOf(comma) != -1) {
var index_of_token_end_of_closing = token_without_symbols.indexOf(comma); var index_of_token_end_of_closing = token_without_symbols.indexOf(comma);
if (token_without_symbols.indexOf(options_open, index_of_token_end_of_closing) != -1 && token_without_symbols.indexOf(options_close, index_of_token_end_of_closing) != -1) { if (token_without_symbols.indexOf(options_open, index_of_token_end_of_closing) != -1 && token_without_symbols.indexOf(options_close, index_of_token_end_of_closing) != -1) {
...@@ -914,31 +933,33 @@ ...@@ -914,31 +933,33 @@
} }
} }
} }
var translated_token = _translate(token_without_symbols, opts); var translated_token = _translate(token_without_symbols, opts);
translated = translated.replace(token, translated_token); translated = translated.replace(token, translated_token);
} }
return translated; return translated;
} }
function hasContext(options) { function hasContext(options) {
return (options.context && typeof options.context == 'string'); return (options.context && (typeof options.context == 'string' || typeof options.context == 'number'));
} }
function needsPlural(options) { function needsPlural(options) {
return (options.count !== undefined && typeof options.count != 'string' && options.count !== 1); return (options.count !== undefined && typeof options.count != 'string' && options.count !== 1);
} }
function exists(key, options) { function exists(key, options) {
options = options || {}; options = options || {};
var notFound = _getDefaultValue(key, options) var notFound = _getDefaultValue(key, options)
, found = _find(key, options); , found = _find(key, options);
return found !== undefined || found === notFound; return found !== undefined || found === notFound;
} }
function translate(key, options) { function translate(key, options) {
options = options || {};
if (!initialized) { if (!initialized) {
f.log('i18next not finished initialization. you might have called t function before loading resources finished.') f.log('i18next not finished initialization. you might have called t function before loading resources finished.')
return options.defaultValue || ''; return options.defaultValue || '';
...@@ -946,26 +967,26 @@ ...@@ -946,26 +967,26 @@
replacementCounter = 0; replacementCounter = 0;
return _translate.apply(null, arguments); return _translate.apply(null, arguments);
} }
function _getDefaultValue(key, options) { function _getDefaultValue(key, options) {
return (options.defaultValue !== undefined) ? options.defaultValue : key; return (options.defaultValue !== undefined) ? options.defaultValue : key;
} }
function _injectSprintfProcessor() { function _injectSprintfProcessor() {
var values = []; var values = [];
// mh: build array from second argument onwards // mh: build array from second argument onwards
for (var i = 1; i < arguments.length; i++) { for (var i = 1; i < arguments.length; i++) {
values.push(arguments[i]); values.push(arguments[i]);
} }
return { return {
postProcess: 'sprintf', postProcess: 'sprintf',
sprintf: values sprintf: values
}; };
} }
function _translate(potentialKeys, options) { function _translate(potentialKeys, options) {
if (typeof options == 'string') { if (typeof options == 'string') {
if (o.shortcutFunction === 'sprintf') { if (o.shortcutFunction === 'sprintf') {
...@@ -979,33 +1000,37 @@ ...@@ -979,33 +1000,37 @@
} else { } else {
options = options || {}; options = options || {};
} }
if (potentialKeys === undefined || potentialKeys === null) return '';
if (typeof potentialKeys == 'string') { if (typeof potentialKeys == 'string') {
potentialKeys = [potentialKeys]; potentialKeys = [potentialKeys];
} }
var key = null; var key = potentialKeys[0];
for (var i = 0; i < potentialKeys.length; i++) { if (potentialKeys.length > 1) {
key = potentialKeys[i]; for (var i = 0; i < potentialKeys.length; i++) {
if (exists(key)) { key = potentialKeys[i];
break; if (exists(key)) {
break;
}
} }
} }
var notFound = _getDefaultValue(key, options) var notFound = _getDefaultValue(key, options)
, found = _find(key, options) , found = _find(key, options)
, lngs = options.lng ? f.toLanguages(options.lng) : languages , lngs = options.lng ? f.toLanguages(options.lng) : languages
, ns = options.ns || o.ns.defaultNs , ns = options.ns || o.ns.defaultNs
, parts; , parts;
// split ns and key // split ns and key
if (key.indexOf(o.nsseparator) > -1) { if (key.indexOf(o.nsseparator) > -1) {
parts = key.split(o.nsseparator); parts = key.split(o.nsseparator);
ns = parts[0]; ns = parts[0];
key = parts[1]; key = parts[1];
} }
if (found === undefined && o.sendMissing) { if (found === undefined && o.sendMissing) {
if (options.lng) { if (options.lng) {
sync.postMissing(lngs[0], ns, key, notFound, lngs); sync.postMissing(lngs[0], ns, key, notFound, lngs);
...@@ -1013,14 +1038,14 @@ ...@@ -1013,14 +1038,14 @@
sync.postMissing(o.lng, ns, key, notFound, lngs); sync.postMissing(o.lng, ns, key, notFound, lngs);
} }
} }
var postProcessor = options.postProcess || o.postProcess; var postProcessor = options.postProcess || o.postProcess;
if (found !== undefined && postProcessor) { if (found !== undefined && postProcessor) {
if (postProcessors[postProcessor]) { if (postProcessors[postProcessor]) {
found = postProcessors[postProcessor](found, key, options); found = postProcessors[postProcessor](found, key, options);
} }
} }
// process notFound if function exists // process notFound if function exists
var splitNotFound = notFound; var splitNotFound = notFound;
if (notFound.indexOf(o.nsseparator) > -1) { if (notFound.indexOf(o.nsseparator) > -1) {
...@@ -1030,68 +1055,68 @@ ...@@ -1030,68 +1055,68 @@
if (splitNotFound === key && o.parseMissingKey) { if (splitNotFound === key && o.parseMissingKey) {
notFound = o.parseMissingKey(notFound); notFound = o.parseMissingKey(notFound);
} }
if (found === undefined) { if (found === undefined) {
notFound = applyReplacement(notFound, options); notFound = applyReplacement(notFound, options);
notFound = applyReuse(notFound, options); notFound = applyReuse(notFound, options);
if (postProcessor && postProcessors[postProcessor]) { if (postProcessor && postProcessors[postProcessor]) {
var val = _getDefaultValue(key, options); var val = _getDefaultValue(key, options);
found = postProcessors[postProcessor](val, key, options); found = postProcessors[postProcessor](val, key, options);
} }
} }
return (found !== undefined) ? found : notFound; return (found !== undefined) ? found : notFound;
} }
function _find(key, options){ function _find(key, options) {
options = options || {}; options = options || {};
var optionWithoutCount, translated var optionWithoutCount, translated
, notFound = _getDefaultValue(key, options) , notFound = _getDefaultValue(key, options)
, lngs = languages; , lngs = languages;
if (!resStore) { return notFound; } // no resStore to translate from if (!resStore) { return notFound; } // no resStore to translate from
if (options.lng) { if (options.lng) {
lngs = f.toLanguages(options.lng); lngs = f.toLanguages(options.lng);
if (!resStore[lngs[0]]) { if (!resStore[lngs[0]]) {
var oldAsync = o.getAsync; var oldAsync = o.getAsync;
o.getAsync = false; o.getAsync = false;
i18n.sync.load(lngs, o, function(err, store) { i18n.sync.load(lngs, o, function(err, store) {
f.extend(resStore, store); f.extend(resStore, store);
o.getAsync = oldAsync; o.getAsync = oldAsync;
}); });
} }
} }
var ns = options.ns || o.ns.defaultNs; var ns = options.ns || o.ns.defaultNs;
if (key.indexOf(o.nsseparator) > -1) { if (key.indexOf(o.nsseparator) > -1) {
var parts = key.split(o.nsseparator); var parts = key.split(o.nsseparator);
ns = parts[0]; ns = parts[0];
key = parts[1]; key = parts[1];
} }
if (hasContext(options)) { if (hasContext(options)) {
optionWithoutCount = f.extend({}, options); optionWithoutCount = f.extend({}, options);
delete optionWithoutCount.context; delete optionWithoutCount.context;
optionWithoutCount.defaultValue = o.contextNotFound; optionWithoutCount.defaultValue = o.contextNotFound;
var contextKey = ns + o.nsseparator + key + '_' + options.context; var contextKey = ns + o.nsseparator + key + '_' + options.context;
translated = translate(contextKey, optionWithoutCount); translated = translate(contextKey, optionWithoutCount);
if (translated != o.contextNotFound) { if (translated != o.contextNotFound) {
return applyReplacement(translated, { context: options.context }); // apply replacement for context only return applyReplacement(translated, { context: options.context }); // apply replacement for context only
} // else continue translation with original/nonContext key } // else continue translation with original/nonContext key
} }
if (needsPlural(options)) { if (needsPlural(options)) {
optionWithoutCount = f.extend({}, options); optionWithoutCount = f.extend({}, options);
delete optionWithoutCount.count; delete optionWithoutCount.count;
optionWithoutCount.defaultValue = o.pluralNotFound; optionWithoutCount.defaultValue = o.pluralNotFound;
var pluralKey = ns + o.nsseparator + key + o.pluralSuffix; var pluralKey = ns + o.nsseparator + key + o.pluralSuffix;
var pluralExtension = pluralExtensions.get(lngs[0], options.count); var pluralExtension = pluralExtensions.get(lngs[0], options.count);
if (pluralExtension >= 0) { if (pluralExtension >= 0) {
...@@ -1099,7 +1124,7 @@ ...@@ -1099,7 +1124,7 @@
} else if (pluralExtension === 1) { } else if (pluralExtension === 1) {
pluralKey = ns + o.nsseparator + key; // singular pluralKey = ns + o.nsseparator + key; // singular
} }
translated = translate(pluralKey, optionWithoutCount); translated = translate(pluralKey, optionWithoutCount);
if (translated != o.pluralNotFound) { if (translated != o.pluralNotFound) {
return applyReplacement(translated, { return applyReplacement(translated, {
...@@ -1109,14 +1134,14 @@ ...@@ -1109,14 +1134,14 @@
}); // apply replacement for count only }); // apply replacement for count only
} // else continue translation with original/singular key } // else continue translation with original/singular key
} }
var found; var found;
var keys = key.split(o.keyseparator); var keys = key.split(o.keyseparator);
for (var i = 0, len = lngs.length; i < len; i++ ) { for (var i = 0, len = lngs.length; i < len; i++ ) {
if (found !== undefined) break; if (found !== undefined) break;
var l = lngs[i]; var l = lngs[i];
var x = 0; var x = 0;
var value = resStore[l] && resStore[l][ns]; var value = resStore[l] && resStore[l][ns];
while (keys[x]) { while (keys[x]) {
...@@ -1124,10 +1149,11 @@ ...@@ -1124,10 +1149,11 @@
x++; x++;
} }
if (value !== undefined) { if (value !== undefined) {
var valueType = Object.prototype.toString.apply(value);
if (typeof value === 'string') { if (typeof value === 'string') {
value = applyReplacement(value, options); value = applyReplacement(value, options);
value = applyReuse(value, options); value = applyReuse(value, options);
} else if (Object.prototype.toString.apply(value) === '[object Array]' && !o.returnObjectTrees && !options.returnObjectTrees) { } else if (valueType === '[object Array]' && !o.returnObjectTrees && !options.returnObjectTrees) {
value = value.join('\n'); value = value.join('\n');
value = applyReplacement(value, options); value = applyReplacement(value, options);
value = applyReuse(value, options); value = applyReuse(value, options);
...@@ -1135,35 +1161,43 @@ ...@@ -1135,35 +1161,43 @@
value = undefined; value = undefined;
} else if (value !== null) { } else if (value !== null) {
if (!o.returnObjectTrees && !options.returnObjectTrees) { if (!o.returnObjectTrees && !options.returnObjectTrees) {
value = 'key \'' + ns + ':' + key + ' (' + l + ')\' ' + if (o.objectTreeKeyHandler && typeof o.objectTreeKeyHandler == 'function') {
'returned a object instead of string.'; value = o.objectTreeKeyHandler(key, value, l, ns, options);
f.log(value); } else {
} else if (typeof value !== 'number') { value = 'key \'' + ns + ':' + key + ' (' + l + ')\' ' +
var copy = {}; // apply child translation on a copy 'returned an object instead of string.';
f.log(value);
}
} else if (valueType !== '[object Number]' && valueType !== '[object Function]' && valueType !== '[object RegExp]') {
var copy = (valueType === '[object Array]') ? [] : {}; // apply child translation on a copy
f.each(value, function(m) { f.each(value, function(m) {
copy[m] = _translate(ns + o.nsseparator + key + o.keyseparator + m, options); copy[m] = _translate(ns + o.nsseparator + key + o.keyseparator + m, options);
}); });
value = copy; value = copy;
} }
} }
if (typeof value === 'string' && value.trim() === '' && o.fallbackOnEmpty === true)
value = undefined;
found = value; found = value;
} }
} }
if (found === undefined && !options.isFallbackLookup && (o.fallbackToDefaultNS === true || (o.fallbackNS && o.fallbackNS.length > 0))) { if (found === undefined && !options.isFallbackLookup && (o.fallbackToDefaultNS === true || (o.fallbackNS && o.fallbackNS.length > 0))) {
// set flag for fallback lookup - avoid recursion // set flag for fallback lookup - avoid recursion
options.isFallbackLookup = true; options.isFallbackLookup = true;
if (o.fallbackNS.length) { if (o.fallbackNS.length) {
for (var y = 0, lenY = o.fallbackNS.length; y < lenY; y++) { for (var y = 0, lenY = o.fallbackNS.length; y < lenY; y++) {
found = _find(o.fallbackNS[y] + o.nsseparator + key, options); found = _find(o.fallbackNS[y] + o.nsseparator + key, options);
if (found) { if (found) {
/* compare value without namespace */ /* compare value without namespace */
var foundValue = found.indexOf(o.nsseparator) > -1 ? found.split(o.nsseparator)[1] : found var foundValue = found.indexOf(o.nsseparator) > -1 ? found.split(o.nsseparator)[1] : found
, notFoundValue = notFound.indexOf(o.nsseparator) > -1 ? notFound.split(o.nsseparator)[1] : notFound; , notFoundValue = notFound.indexOf(o.nsseparator) > -1 ? notFound.split(o.nsseparator)[1] : notFound;
if (foundValue !== notFoundValue) break; if (foundValue !== notFoundValue) break;
} }
} }
...@@ -1171,12 +1205,12 @@ ...@@ -1171,12 +1205,12 @@
found = _find(key, options); // fallback to default NS found = _find(key, options); // fallback to default NS
} }
} }
return found; return found;
} }
function detectLanguage() { function detectLanguage() {
var detectedLng; var detectedLng;
// get from qs // get from qs
var qsParm = []; var qsParm = [];
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
...@@ -1196,22 +1230,22 @@ ...@@ -1196,22 +1230,22 @@
detectedLng = qsParm[o.detectLngQS]; detectedLng = qsParm[o.detectLngQS];
} }
} }
// get from cookie // get from cookie
if (!detectedLng && typeof document !== 'undefined' && o.useCookie ) { if (!detectedLng && typeof document !== 'undefined' && o.useCookie ) {
var c = f.cookie.read(o.cookieName); var c = f.cookie.read(o.cookieName);
if (c) detectedLng = c; if (c) detectedLng = c;
} }
// get from navigator // get from navigator
if (!detectedLng && typeof navigator !== 'undefined') { if (!detectedLng && typeof navigator !== 'undefined') {
detectedLng = (navigator.language) ? navigator.language : navigator.userLanguage; detectedLng = (navigator.language) ? navigator.language : navigator.userLanguage;
} }
return detectedLng; return detectedLng;
} }
var sync = { var sync = {
load: function(lngs, options, cb) { load: function(lngs, options, cb) {
if (options.useLocalStorage) { if (options.useLocalStorage) {
sync._loadLocal(lngs, options, function(err, store) { sync._loadLocal(lngs, options, function(err, store) {
...@@ -1219,12 +1253,12 @@ ...@@ -1219,12 +1253,12 @@
for (var i = 0, len = lngs.length; i < len; i++) { for (var i = 0, len = lngs.length; i < len; i++) {
if (!store[lngs[i]]) missingLngs.push(lngs[i]); if (!store[lngs[i]]) missingLngs.push(lngs[i]);
} }
if (missingLngs.length > 0) { if (missingLngs.length > 0) {
sync._fetch(missingLngs, options, function(err, fetched) { sync._fetch(missingLngs, options, function(err, fetched) {
f.extend(store, fetched); f.extend(store, fetched);
sync._storeLocal(fetched); sync._storeLocal(fetched);
cb(null, store); cb(null, store);
}); });
} else { } else {
...@@ -1237,32 +1271,32 @@ ...@@ -1237,32 +1271,32 @@
}); });
} }
}, },
_loadLocal: function(lngs, options, cb) { _loadLocal: function(lngs, options, cb) {
var store = {} var store = {}
, nowMS = new Date().getTime(); , nowMS = new Date().getTime();
if(window.localStorage) { if(window.localStorage) {
var todo = lngs.length; var todo = lngs.length;
f.each(lngs, function(key, lng) { f.each(lngs, function(key, lng) {
var local = window.localStorage.getItem('res_' + lng); var local = window.localStorage.getItem('res_' + lng);
if (local) { if (local) {
local = JSON.parse(local); local = JSON.parse(local);
if (local.i18nStamp && local.i18nStamp + options.localStorageExpirationTime > nowMS) { if (local.i18nStamp && local.i18nStamp + options.localStorageExpirationTime > nowMS) {
store[lng] = local; store[lng] = local;
} }
} }
todo--; // wait for all done befor callback todo--; // wait for all done befor callback
if (todo === 0) cb(null, store); if (todo === 0) cb(null, store);
}); });
} }
}, },
_storeLocal: function(store) { _storeLocal: function(store) {
if(window.localStorage) { if(window.localStorage) {
for (var m in store) { for (var m in store) {
...@@ -1272,19 +1306,19 @@ ...@@ -1272,19 +1306,19 @@
} }
return; return;
}, },
_fetch: function(lngs, options, cb) { _fetch: function(lngs, options, cb) {
var ns = options.ns var ns = options.ns
, store = {}; , store = {};
if (!options.dynamicLoad) { if (!options.dynamicLoad) {
var todo = ns.namespaces.length * lngs.length var todo = ns.namespaces.length * lngs.length
, errors; , errors;
// load each file individual // load each file individual
f.each(ns.namespaces, function(nsIndex, nsValue) { f.each(ns.namespaces, function(nsIndex, nsValue) {
f.each(lngs, function(lngIndex, lngValue) { f.each(lngs, function(lngIndex, lngValue) {
// Call this once our translation has returned. // Call this once our translation has returned.
var loadComplete = function(err, data) { var loadComplete = function(err, data) {
if (err) { if (err) {
...@@ -1293,11 +1327,11 @@ ...@@ -1293,11 +1327,11 @@
} }
store[lngValue] = store[lngValue] || {}; store[lngValue] = store[lngValue] || {};
store[lngValue][nsValue] = data; store[lngValue][nsValue] = data;
todo--; // wait for all done befor callback todo--; // wait for all done befor callback
if (todo === 0) cb(errors, store); if (todo === 0) cb(errors, store);
}; };
if(typeof options.customLoad == 'function'){ if(typeof options.customLoad == 'function'){
// Use the specified custom callback. // Use the specified custom callback.
options.customLoad(lngValue, nsValue, options, loadComplete); options.customLoad(lngValue, nsValue, options, loadComplete);
...@@ -1312,7 +1346,7 @@ ...@@ -1312,7 +1346,7 @@
var loadComplete = function(err, data) { var loadComplete = function(err, data) {
cb(null, data); cb(null, data);
}; };
if(typeof options.customLoad == 'function'){ if(typeof options.customLoad == 'function'){
// Use the specified custom callback. // Use the specified custom callback.
options.customLoad(lngs, ns.namespaces, options, loadComplete); options.customLoad(lngs, ns.namespaces, options, loadComplete);
...@@ -1332,10 +1366,10 @@ ...@@ -1332,10 +1366,10 @@
dataType: "json", dataType: "json",
async : options.getAsync async : options.getAsync
}); });
} }
} }
}, },
_fetchOne: function(lng, ns, options, done) { _fetchOne: function(lng, ns, options, done) {
var url = applyReplacement(options.resGetPath, { lng: lng, ns: ns }); var url = applyReplacement(options.resGetPath, { lng: lng, ns: ns });
f.ajax({ f.ajax({
...@@ -1345,20 +1379,28 @@ ...@@ -1345,20 +1379,28 @@
done(null, data); done(null, data);
}, },
error : function(xhr, status, error) { error : function(xhr, status, error) {
f.log('failed loading: ' + url); if (error.status == 200) {
// file loaded but invalid json, stop waste time !
f.log('There is a typo in: ' + url);
} else if (error.status == 404) {
f.log('Does not exist: ' + url);
} else {
f.log(error.status + ' when loading ' + url);
}
done(error, {}); done(error, {});
}, },
dataType: "json", dataType: "json",
async : options.getAsync async : options.getAsync
}); });
}, },
postMissing: function(lng, ns, key, defaultValue, lngs) { postMissing: function(lng, ns, key, defaultValue, lngs) {
var payload = {}; var payload = {};
payload[key] = defaultValue; payload[key] = defaultValue;
var urls = []; var urls = [];
if (o.sendMissingTo === 'fallback' && o.fallbackLng !== false) { if (o.sendMissingTo === 'fallback' && o.fallbackLng !== false) {
urls.push({lng: o.fallbackLng, url: applyReplacement(o.resPostPath, { lng: o.fallbackLng, ns: ns })}); urls.push({lng: o.fallbackLng, url: applyReplacement(o.resPostPath, { lng: o.fallbackLng, ns: ns })});
} else if (o.sendMissingTo === 'current' || (o.sendMissingTo === 'fallback' && o.fallbackLng === false) ) { } else if (o.sendMissingTo === 'current' || (o.sendMissingTo === 'fallback' && o.fallbackLng === false) ) {
...@@ -1368,7 +1410,7 @@ ...@@ -1368,7 +1410,7 @@
urls.push({lng: lngs[i], url: applyReplacement(o.resPostPath, { lng: lngs[i], ns: ns })}); urls.push({lng: lngs[i], url: applyReplacement(o.resPostPath, { lng: lngs[i], ns: ns })});
} }
} }
for (var y = 0, len = urls.length; y < len; y++) { for (var y = 0, len = urls.length; y < len; y++) {
var item = urls[y]; var item = urls[y];
f.ajax({ f.ajax({
...@@ -1377,7 +1419,7 @@ ...@@ -1377,7 +1419,7 @@
data: payload, data: payload,
success: function(data, status, xhr) { success: function(data, status, xhr) {
f.log('posted missing key \'' + key + '\' to: ' + item.url); f.log('posted missing key \'' + key + '\' to: ' + item.url);
// add key to resStore // add key to resStore
var keys = key.split('.'); var keys = key.split('.');
var x = 0; var x = 0;
...@@ -1402,1081 +1444,1081 @@ ...@@ -1402,1081 +1444,1081 @@
}; };
// definition http://translate.sourceforge.net/wiki/l10n/pluralforms // definition http://translate.sourceforge.net/wiki/l10n/pluralforms
var pluralExtensions = { var pluralExtensions = {
rules: { rules: {
"ach": { "ach": {
"name": "Acholi", "name": "Acholi",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n > 1); } "plurals": function(n) { return Number(n > 1); }
}, },
"af": { "af": {
"name": "Afrikaans", "name": "Afrikaans",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"ak": { "ak": {
"name": "Akan", "name": "Akan",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n > 1); } "plurals": function(n) { return Number(n > 1); }
}, },
"am": { "am": {
"name": "Amharic", "name": "Amharic",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n > 1); } "plurals": function(n) { return Number(n > 1); }
}, },
"an": { "an": {
"name": "Aragonese", "name": "Aragonese",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"ar": { "ar": {
"name": "Arabic", "name": "Arabic",
"numbers": [ "numbers": [
0, 0,
1, 1,
2, 2,
3, 3,
11, 11,
100 100
], ],
"plurals": function(n) { return Number(n===0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5); } "plurals": function(n) { return Number(n===0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5); }
}, },
"arn": { "arn": {
"name": "Mapudungun", "name": "Mapudungun",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n > 1); } "plurals": function(n) { return Number(n > 1); }
}, },
"ast": { "ast": {
"name": "Asturian", "name": "Asturian",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"ay": { "ay": {
"name": "Aymar\u00e1", "name": "Aymar\u00e1",
"numbers": [ "numbers": [
1 1
], ],
"plurals": function(n) { return 0; } "plurals": function(n) { return 0; }
}, },
"az": { "az": {
"name": "Azerbaijani", "name": "Azerbaijani",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"be": { "be": {
"name": "Belarusian", "name": "Belarusian",
"numbers": [ "numbers": [
1, 1,
2, 2,
5 5
], ],
"plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); } "plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); }
}, },
"bg": { "bg": {
"name": "Bulgarian", "name": "Bulgarian",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"bn": { "bn": {
"name": "Bengali", "name": "Bengali",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"bo": { "bo": {
"name": "Tibetan", "name": "Tibetan",
"numbers": [ "numbers": [
1 1
], ],
"plurals": function(n) { return 0; } "plurals": function(n) { return 0; }
}, },
"br": { "br": {
"name": "Breton", "name": "Breton",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n > 1); } "plurals": function(n) { return Number(n > 1); }
}, },
"bs": { "bs": {
"name": "Bosnian", "name": "Bosnian",
"numbers": [ "numbers": [
1, 1,
2, 2,
5 5
], ],
"plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); } "plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); }
}, },
"ca": { "ca": {
"name": "Catalan", "name": "Catalan",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"cgg": { "cgg": {
"name": "Chiga", "name": "Chiga",
"numbers": [ "numbers": [
1 1
], ],
"plurals": function(n) { return 0; } "plurals": function(n) { return 0; }
}, },
"cs": { "cs": {
"name": "Czech", "name": "Czech",
"numbers": [ "numbers": [
1, 1,
2, 2,
5 5
], ],
"plurals": function(n) { return Number((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2); } "plurals": function(n) { return Number((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2); }
}, },
"csb": { "csb": {
"name": "Kashubian", "name": "Kashubian",
"numbers": [ "numbers": [
1, 1,
2, 2,
5 5
], ],
"plurals": function(n) { return Number(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); } "plurals": function(n) { return Number(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); }
}, },
"cy": { "cy": {
"name": "Welsh", "name": "Welsh",
"numbers": [ "numbers": [
1, 1,
2, 2,
3, 3,
8 8
], ],
"plurals": function(n) { return Number((n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != 11) ? 2 : 3); } "plurals": function(n) { return Number((n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != 11) ? 2 : 3); }
}, },
"da": { "da": {
"name": "Danish", "name": "Danish",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"de": { "de": {
"name": "German", "name": "German",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"dz": { "dz": {
"name": "Dzongkha", "name": "Dzongkha",
"numbers": [ "numbers": [
1 1
], ],
"plurals": function(n) { return 0; } "plurals": function(n) { return 0; }
}, },
"el": { "el": {
"name": "Greek", "name": "Greek",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"en": { "en": {
"name": "English", "name": "English",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"eo": { "eo": {
"name": "Esperanto", "name": "Esperanto",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"es": { "es": {
"name": "Spanish", "name": "Spanish",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"es_ar": { "es_ar": {
"name": "Argentinean Spanish", "name": "Argentinean Spanish",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"et": { "et": {
"name": "Estonian", "name": "Estonian",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"eu": { "eu": {
"name": "Basque", "name": "Basque",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"fa": { "fa": {
"name": "Persian", "name": "Persian",
"numbers": [ "numbers": [
1 1
], ],
"plurals": function(n) { return 0; } "plurals": function(n) { return 0; }
}, },
"fi": { "fi": {
"name": "Finnish", "name": "Finnish",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"fil": { "fil": {
"name": "Filipino", "name": "Filipino",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n > 1); } "plurals": function(n) { return Number(n > 1); }
}, },
"fo": { "fo": {
"name": "Faroese", "name": "Faroese",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"fr": { "fr": {
"name": "French", "name": "French",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n > 1); } "plurals": function(n) { return Number(n > 1); }
}, },
"fur": { "fur": {
"name": "Friulian", "name": "Friulian",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"fy": { "fy": {
"name": "Frisian", "name": "Frisian",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"ga": { "ga": {
"name": "Irish", "name": "Irish",
"numbers": [ "numbers": [
1, 1,
2, 2,
3, 3,
7, 7,
11 11
], ],
"plurals": function(n) { return Number(n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n<11 ? 3 : 4) ;} "plurals": function(n) { return Number(n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n<11 ? 3 : 4) ;}
}, },
"gd": { "gd": {
"name": "Scottish Gaelic", "name": "Scottish Gaelic",
"numbers": [ "numbers": [
1, 1,
2, 2,
3, 3,
20 20
], ],
"plurals": function(n) { return Number((n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : (n > 2 && n < 20) ? 2 : 3); } "plurals": function(n) { return Number((n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : (n > 2 && n < 20) ? 2 : 3); }
}, },
"gl": { "gl": {
"name": "Galician", "name": "Galician",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"gu": { "gu": {
"name": "Gujarati", "name": "Gujarati",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"gun": { "gun": {
"name": "Gun", "name": "Gun",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n > 1); } "plurals": function(n) { return Number(n > 1); }
}, },
"ha": { "ha": {
"name": "Hausa", "name": "Hausa",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"he": { "he": {
"name": "Hebrew", "name": "Hebrew",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"hi": { "hi": {
"name": "Hindi", "name": "Hindi",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"hr": { "hr": {
"name": "Croatian", "name": "Croatian",
"numbers": [ "numbers": [
1, 1,
2, 2,
5 5
], ],
"plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); } "plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); }
}, },
"hu": { "hu": {
"name": "Hungarian", "name": "Hungarian",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"hy": { "hy": {
"name": "Armenian", "name": "Armenian",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"ia": { "ia": {
"name": "Interlingua", "name": "Interlingua",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"id": { "id": {
"name": "Indonesian", "name": "Indonesian",
"numbers": [ "numbers": [
1 1
], ],
"plurals": function(n) { return 0; } "plurals": function(n) { return 0; }
}, },
"is": { "is": {
"name": "Icelandic", "name": "Icelandic",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n%10!=1 || n%100==11); } "plurals": function(n) { return Number(n%10!=1 || n%100==11); }
}, },
"it": { "it": {
"name": "Italian", "name": "Italian",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"ja": { "ja": {
"name": "Japanese", "name": "Japanese",
"numbers": [ "numbers": [
1 1
], ],
"plurals": function(n) { return 0; } "plurals": function(n) { return 0; }
}, },
"jbo": { "jbo": {
"name": "Lojban", "name": "Lojban",
"numbers": [ "numbers": [
1 1
], ],
"plurals": function(n) { return 0; } "plurals": function(n) { return 0; }
}, },
"jv": { "jv": {
"name": "Javanese", "name": "Javanese",
"numbers": [ "numbers": [
0, 0,
1 1
], ],
"plurals": function(n) { return Number(n !== 0); } "plurals": function(n) { return Number(n !== 0); }
}, },
"ka": { "ka": {
"name": "Georgian", "name": "Georgian",
"numbers": [ "numbers": [
1 1
], ],
"plurals": function(n) { return 0; } "plurals": function(n) { return 0; }
}, },
"kk": { "kk": {
"name": "Kazakh", "name": "Kazakh",
"numbers": [ "numbers": [
1 1
], ],
"plurals": function(n) { return 0; } "plurals": function(n) { return 0; }
}, },
"km": { "km": {
"name": "Khmer", "name": "Khmer",
"numbers": [ "numbers": [
1 1
], ],
"plurals": function(n) { return 0; } "plurals": function(n) { return 0; }
}, },
"kn": { "kn": {
"name": "Kannada", "name": "Kannada",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"ko": { "ko": {
"name": "Korean", "name": "Korean",
"numbers": [ "numbers": [
1 1
], ],
"plurals": function(n) { return 0; } "plurals": function(n) { return 0; }
}, },
"ku": { "ku": {
"name": "Kurdish", "name": "Kurdish",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"kw": { "kw": {
"name": "Cornish", "name": "Cornish",
"numbers": [ "numbers": [
1, 1,
2, 2,
3, 3,
4 4
], ],
"plurals": function(n) { return Number((n==1) ? 0 : (n==2) ? 1 : (n == 3) ? 2 : 3); } "plurals": function(n) { return Number((n==1) ? 0 : (n==2) ? 1 : (n == 3) ? 2 : 3); }
}, },
"ky": { "ky": {
"name": "Kyrgyz", "name": "Kyrgyz",
"numbers": [ "numbers": [
1 1
], ],
"plurals": function(n) { return 0; } "plurals": function(n) { return 0; }
}, },
"lb": { "lb": {
"name": "Letzeburgesch", "name": "Letzeburgesch",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"ln": { "ln": {
"name": "Lingala", "name": "Lingala",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n > 1); } "plurals": function(n) { return Number(n > 1); }
}, },
"lo": { "lo": {
"name": "Lao", "name": "Lao",
"numbers": [ "numbers": [
1 1
], ],
"plurals": function(n) { return 0; } "plurals": function(n) { return 0; }
}, },
"lt": { "lt": {
"name": "Lithuanian", "name": "Lithuanian",
"numbers": [ "numbers": [
1, 1,
2, 2,
10 10
], ],
"plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2); } "plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2); }
}, },
"lv": { "lv": {
"name": "Latvian", "name": "Latvian",
"numbers": [ "numbers": [
0, 0,
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n !== 0 ? 1 : 2); } "plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n !== 0 ? 1 : 2); }
}, },
"mai": { "mai": {
"name": "Maithili", "name": "Maithili",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"mfe": { "mfe": {
"name": "Mauritian Creole", "name": "Mauritian Creole",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n > 1); } "plurals": function(n) { return Number(n > 1); }
}, },
"mg": { "mg": {
"name": "Malagasy", "name": "Malagasy",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n > 1); } "plurals": function(n) { return Number(n > 1); }
}, },
"mi": { "mi": {
"name": "Maori", "name": "Maori",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n > 1); } "plurals": function(n) { return Number(n > 1); }
}, },
"mk": { "mk": {
"name": "Macedonian", "name": "Macedonian",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n==1 || n%10==1 ? 0 : 1); } "plurals": function(n) { return Number(n==1 || n%10==1 ? 0 : 1); }
}, },
"ml": { "ml": {
"name": "Malayalam", "name": "Malayalam",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"mn": { "mn": {
"name": "Mongolian", "name": "Mongolian",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"mnk": { "mnk": {
"name": "Mandinka", "name": "Mandinka",
"numbers": [ "numbers": [
0, 0,
1, 1,
2 2
], ],
"plurals": function(n) { return Number(0 ? 0 : n==1 ? 1 : 2); } "plurals": function(n) { return Number(0 ? 0 : n==1 ? 1 : 2); }
}, },
"mr": { "mr": {
"name": "Marathi", "name": "Marathi",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"ms": { "ms": {
"name": "Malay", "name": "Malay",
"numbers": [ "numbers": [
1 1
], ],
"plurals": function(n) { return 0; } "plurals": function(n) { return 0; }
}, },
"mt": { "mt": {
"name": "Maltese", "name": "Maltese",
"numbers": [ "numbers": [
1, 1,
2, 2,
11, 11,
20 20
], ],
"plurals": function(n) { return Number(n==1 ? 0 : n===0 || ( n%100>1 && n%100<11) ? 1 : (n%100>10 && n%100<20 ) ? 2 : 3); } "plurals": function(n) { return Number(n==1 ? 0 : n===0 || ( n%100>1 && n%100<11) ? 1 : (n%100>10 && n%100<20 ) ? 2 : 3); }
}, },
"nah": { "nah": {
"name": "Nahuatl", "name": "Nahuatl",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"nap": { "nap": {
"name": "Neapolitan", "name": "Neapolitan",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"nb": { "nb": {
"name": "Norwegian Bokmal", "name": "Norwegian Bokmal",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"ne": { "ne": {
"name": "Nepali", "name": "Nepali",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"nl": { "nl": {
"name": "Dutch", "name": "Dutch",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"nn": { "nn": {
"name": "Norwegian Nynorsk", "name": "Norwegian Nynorsk",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"no": { "no": {
"name": "Norwegian", "name": "Norwegian",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"nso": { "nso": {
"name": "Northern Sotho", "name": "Northern Sotho",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"oc": { "oc": {
"name": "Occitan", "name": "Occitan",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n > 1); } "plurals": function(n) { return Number(n > 1); }
}, },
"or": { "or": {
"name": "Oriya", "name": "Oriya",
"numbers": [ "numbers": [
2, 2,
1 1
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"pa": { "pa": {
"name": "Punjabi", "name": "Punjabi",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"pap": { "pap": {
"name": "Papiamento", "name": "Papiamento",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"pl": { "pl": {
"name": "Polish", "name": "Polish",
"numbers": [ "numbers": [
1, 1,
2, 2,
5 5
], ],
"plurals": function(n) { return Number(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); } "plurals": function(n) { return Number(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); }
}, },
"pms": { "pms": {
"name": "Piemontese", "name": "Piemontese",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"ps": { "ps": {
"name": "Pashto", "name": "Pashto",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"pt": { "pt": {
"name": "Portuguese", "name": "Portuguese",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"pt_br": { "pt_br": {
"name": "Brazilian Portuguese", "name": "Brazilian Portuguese",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"rm": { "rm": {
"name": "Romansh", "name": "Romansh",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"ro": { "ro": {
"name": "Romanian", "name": "Romanian",
"numbers": [ "numbers": [
1, 1,
2, 2,
20 20
], ],
"plurals": function(n) { return Number(n==1 ? 0 : (n===0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2); } "plurals": function(n) { return Number(n==1 ? 0 : (n===0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2); }
}, },
"ru": { "ru": {
"name": "Russian", "name": "Russian",
"numbers": [ "numbers": [
1, 1,
2, 2,
5 5
], ],
"plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); } "plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); }
}, },
"sah": { "sah": {
"name": "Yakut", "name": "Yakut",
"numbers": [ "numbers": [
1 1
], ],
"plurals": function(n) { return 0; } "plurals": function(n) { return 0; }
}, },
"sco": { "sco": {
"name": "Scots", "name": "Scots",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"se": { "se": {
"name": "Northern Sami", "name": "Northern Sami",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"si": { "si": {
"name": "Sinhala", "name": "Sinhala",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"sk": { "sk": {
"name": "Slovak", "name": "Slovak",
"numbers": [ "numbers": [
1, 1,
2, 2,
5 5
], ],
"plurals": function(n) { return Number((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2); } "plurals": function(n) { return Number((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2); }
}, },
"sl": { "sl": {
"name": "Slovenian", "name": "Slovenian",
"numbers": [ "numbers": [
5, 5,
1, 1,
2, 2,
3 3
], ],
"plurals": function(n) { return Number(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n%100==4 ? 3 : 0); } "plurals": function(n) { return Number(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n%100==4 ? 3 : 0); }
}, },
"so": { "so": {
"name": "Somali", "name": "Somali",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"son": { "son": {
"name": "Songhay", "name": "Songhay",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"sq": { "sq": {
"name": "Albanian", "name": "Albanian",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"sr": { "sr": {
"name": "Serbian", "name": "Serbian",
"numbers": [ "numbers": [
1, 1,
2, 2,
5 5
], ],
"plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); } "plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); }
}, },
"su": { "su": {
"name": "Sundanese", "name": "Sundanese",
"numbers": [ "numbers": [
1 1
], ],
"plurals": function(n) { return 0; } "plurals": function(n) { return 0; }
}, },
"sv": { "sv": {
"name": "Swedish", "name": "Swedish",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"sw": { "sw": {
"name": "Swahili", "name": "Swahili",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"ta": { "ta": {
"name": "Tamil", "name": "Tamil",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"te": { "te": {
"name": "Telugu", "name": "Telugu",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"tg": { "tg": {
"name": "Tajik", "name": "Tajik",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n > 1); } "plurals": function(n) { return Number(n > 1); }
}, },
"th": { "th": {
"name": "Thai", "name": "Thai",
"numbers": [ "numbers": [
1 1
], ],
"plurals": function(n) { return 0; } "plurals": function(n) { return 0; }
}, },
"ti": { "ti": {
"name": "Tigrinya", "name": "Tigrinya",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n > 1); } "plurals": function(n) { return Number(n > 1); }
}, },
"tk": { "tk": {
"name": "Turkmen", "name": "Turkmen",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"tr": { "tr": {
"name": "Turkish", "name": "Turkish",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n > 1); } "plurals": function(n) { return Number(n > 1); }
}, },
"tt": { "tt": {
"name": "Tatar", "name": "Tatar",
"numbers": [ "numbers": [
1 1
], ],
"plurals": function(n) { return 0; } "plurals": function(n) { return 0; }
}, },
"ug": { "ug": {
"name": "Uyghur", "name": "Uyghur",
"numbers": [ "numbers": [
1 1
], ],
"plurals": function(n) { return 0; } "plurals": function(n) { return 0; }
}, },
"uk": { "uk": {
"name": "Ukrainian", "name": "Ukrainian",
"numbers": [ "numbers": [
1, 1,
2, 2,
5 5
], ],
"plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); } "plurals": function(n) { return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); }
}, },
"ur": { "ur": {
"name": "Urdu", "name": "Urdu",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"uz": { "uz": {
"name": "Uzbek", "name": "Uzbek",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n > 1); } "plurals": function(n) { return Number(n > 1); }
}, },
"vi": { "vi": {
"name": "Vietnamese", "name": "Vietnamese",
"numbers": [ "numbers": [
1 1
], ],
"plurals": function(n) { return 0; } "plurals": function(n) { return 0; }
}, },
"wa": { "wa": {
"name": "Walloon", "name": "Walloon",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n > 1); } "plurals": function(n) { return Number(n > 1); }
}, },
"wo": { "wo": {
"name": "Wolof", "name": "Wolof",
"numbers": [ "numbers": [
1 1
], ],
"plurals": function(n) { return 0; } "plurals": function(n) { return 0; }
}, },
"yo": { "yo": {
"name": "Yoruba", "name": "Yoruba",
"numbers": [ "numbers": [
1, 1,
2 2
], ],
"plurals": function(n) { return Number(n != 1); } "plurals": function(n) { return Number(n != 1); }
}, },
"zh": { "zh": {
"name": "Chinese", "name": "Chinese",
"numbers": [ "numbers": [
1 1
], ],
"plurals": function(n) { return 0; } "plurals": function(n) { return 0; }
} }
}, },
// for demonstration only sl and ar is added but you can add your own pluralExtensions // for demonstration only sl and ar is added but you can add your own pluralExtensions
addRule: function(lng, obj) { addRule: function(lng, obj) {
pluralExtensions.rules[lng] = obj; pluralExtensions.rules[lng] = obj;
}, },
setCurrentLng: function(lng) { setCurrentLng: function(lng) {
if (!pluralExtensions.currentRule || pluralExtensions.currentRule.lng !== lng) { if (!pluralExtensions.currentRule || pluralExtensions.currentRule.lng !== lng) {
var parts = lng.split('-'); var parts = lng.split('-');
pluralExtensions.currentRule = { pluralExtensions.currentRule = {
lng: lng, lng: lng,
rule: pluralExtensions.rules[parts[0]] rule: pluralExtensions.rules[parts[0]]
}; };
} }
}, },
get: function(lng, count) { get: function(lng, count) {
var parts = lng.split('-'); var parts = lng.split('-');
function getResult(l, c) { function getResult(l, c) {
var ext; var ext;
if (pluralExtensions.currentRule && pluralExtensions.currentRule.lng === lng) { if (pluralExtensions.currentRule && pluralExtensions.currentRule.lng === lng) {
ext = pluralExtensions.currentRule.rule; ext = pluralExtensions.currentRule.rule;
} else { } else {
ext = pluralExtensions.rules[l]; ext = pluralExtensions.rules[l];
} }
...@@ -2484,7 +2526,7 @@ ...@@ -2484,7 +2526,7 @@
var i = ext.plurals(c); var i = ext.plurals(c);
var number = ext.numbers[i]; var number = ext.numbers[i];
if (ext.numbers.length === 2 && ext.numbers[0] === 1) { if (ext.numbers.length === 2 && ext.numbers[0] === 1) {
if (number === 2) { if (number === 2) {
number = -1; // regular plural number = -1; // regular plural
} else if (number === 1) { } else if (number === 1) {
number = 1; // singular number = 1; // singular
...@@ -2495,10 +2537,10 @@ ...@@ -2495,10 +2537,10 @@
return c === 1 ? '1' : '-1'; return c === 1 ? '1' : '-1';
} }
} }
return getResult(parts[0], count); return getResult(parts[0], count);
} }
}; };
var postProcessors = {}; var postProcessors = {};
var addPostProcessor = function(name, fc) { var addPostProcessor = function(name, fc) {
...@@ -2513,14 +2555,14 @@ ...@@ -2513,14 +2555,14 @@
for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */} for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
return output.join(''); return output.join('');
} }
var str_format = function() { var str_format = function() {
if (!str_format.cache.hasOwnProperty(arguments[0])) { if (!str_format.cache.hasOwnProperty(arguments[0])) {
str_format.cache[arguments[0]] = str_format.parse(arguments[0]); str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
} }
return str_format.format.call(null, str_format.cache[arguments[0]], arguments); return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
}; };
str_format.format = function(parse_tree, argv) { str_format.format = function(parse_tree, argv) {
var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length; var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
for (i = 0; i < tree_length; i++) { for (i = 0; i < tree_length; i++) {
...@@ -2545,7 +2587,7 @@ ...@@ -2545,7 +2587,7 @@
else { // positional argument (implicit) else { // positional argument (implicit)
arg = argv[cursor++]; arg = argv[cursor++];
} }
if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) { if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
throw(sprintf('[sprintf] expecting number but found %s', get_type(arg))); throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
} }
...@@ -2570,9 +2612,9 @@ ...@@ -2570,9 +2612,9 @@
} }
return output.join(''); return output.join('');
}; };
str_format.cache = {}; str_format.cache = {};
str_format.parse = function(fmt) { str_format.parse = function(fmt) {
var _fmt = fmt, match = [], parse_tree = [], arg_names = 0; var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
while (_fmt) { while (_fmt) {
...@@ -2620,24 +2662,24 @@ ...@@ -2620,24 +2662,24 @@
} }
return parse_tree; return parse_tree;
}; };
return str_format; return str_format;
})(); })();
var vsprintf = function(fmt, argv) { var vsprintf = function(fmt, argv) {
argv.unshift(fmt); argv.unshift(fmt);
return sprintf.apply(null, argv); return sprintf.apply(null, argv);
}; };
addPostProcessor("sprintf", function(val, key, opts) { addPostProcessor("sprintf", function(val, key, opts) {
if (!opts.sprintf) return val; if (!opts.sprintf) return val;
if (Object.prototype.toString.apply(opts.sprintf) === '[object Array]') { if (Object.prototype.toString.apply(opts.sprintf) === '[object Array]') {
return vsprintf(val, opts.sprintf); return vsprintf(val, opts.sprintf);
} else if (typeof opts.sprintf === 'object') { } else if (typeof opts.sprintf === 'object') {
return sprintf(val, opts.sprintf); return sprintf(val, opts.sprintf);
} }
return val; return val;
}); });
// public api interface // public api interface
...@@ -2645,6 +2687,7 @@ ...@@ -2645,6 +2687,7 @@
i18n.setLng = setLng; i18n.setLng = setLng;
i18n.preload = preload; i18n.preload = preload;
i18n.addResourceBundle = addResourceBundle; i18n.addResourceBundle = addResourceBundle;
i18n.removeResourceBundle = removeResourceBundle;
i18n.loadNamespace = loadNamespace; i18n.loadNamespace = loadNamespace;
i18n.loadNamespaces = loadNamespaces; i18n.loadNamespaces = loadNamespaces;
i18n.setDefaultNamespace = setDefaultNamespace; i18n.setDefaultNamespace = setDefaultNamespace;
......
/* Modernizr 2.7.0 (Custom Build) | MIT & BSD /*!
* Build: http://modernizr.com/download/#-fontface-backgroundsize-borderimage-borderradius-boxshadow-flexbox-hsla-multiplebgs-opacity-rgba-textshadow-cssanimations-csscolumns-generatedcontent-cssgradients-cssreflections-csstransforms-csstransforms3d-csstransitions-applicationcache-canvas-canvastext-draganddrop-hashchange-history-audio-video-indexeddb-input-inputtypes-localstorage-postmessage-sessionstorage-websockets-websqldatabase-webworkers-geolocation-inlinesvg-smil-svg-svgclippaths-touch-webgl-mq-addtest-prefixed-teststyles-testprop-testallprops-hasevent-prefixes-domprefixes-blob_constructor-cors-file_api-file_filesystem-json * Modernizr v2.7.1
* www.modernizr.com
*
* Copyright (c) Faruk Ates, Paul Irish, Alex Sexton
* Available under the BSD and MIT licenses: www.modernizr.com/license/
*/ */
;
/*
* Modernizr tests which native CSS3 and HTML5 features are available in
* the current UA and makes the results available to you in two ways:
* as properties on a global Modernizr object, and as classes on the
* <html> element. This information allows you to progressively enhance
* your pages with a granular level of control over the experience.
*
* Modernizr has an optional (not included) conditional resource loader
* called Modernizr.load(), based on Yepnope.js (yepnopejs.com).
* To get a build that includes Modernizr.load(), as well as choosing
* which tests to include, go to www.modernizr.com/download/
*
* Authors Faruk Ates, Paul Irish, Alex Sexton
* Contributors Ryan Seddon, Ben Alman
*/
window.Modernizr = (function( window, document, undefined ) { window.Modernizr = (function( window, document, undefined ) {
var version = '2.7.0', var version = '2.7.1',
Modernizr = {}, Modernizr = {},
/*>>cssclasses*/
// option for enabling the HTML classes to be added
enableClasses = true,
/*>>cssclasses*/
docElement = document.documentElement, docElement = document.documentElement,
/**
* Create our "modernizr" element that we do most feature tests on.
*/
mod = 'modernizr', mod = 'modernizr',
modElem = document.createElement(mod), modElem = document.createElement(mod),
mStyle = modElem.style, mStyle = modElem.style,
inputElem = document.createElement('input') , /**
* Create the input element for various Web Forms feature tests.
*/
inputElem /*>>inputelem*/ = document.createElement('input') /*>>inputelem*/ ,
/*>>smile*/
smile = ':)', smile = ':)',
/*>>smile*/
toString = {}.toString, toString = {}.toString,
// TODO :: make the prefixes more granular
/*>>prefixes*/
// List of property values to set for css tests. See ticket #21
prefixes = ' -webkit- -moz- -o- -ms- '.split(' '), prefixes = ' -webkit- -moz- -o- -ms- '.split(' '),
/*>>prefixes*/
/*>>domprefixes*/
// Following spec is to expose vendor-specific style properties as:
// elem.style.WebkitBorderRadius
// and the following would be incorrect:
// elem.style.webkitBorderRadius
// Webkit ghosts their properties in lowercase but Opera & Moz do not.
// Microsoft uses a lowercase `ms` instead of the correct `Ms` in IE8+
// erik.eae.net/archives/2008/03/10/21.48.10/
// More here: github.com/Modernizr/Modernizr/issues/issue/21
omPrefixes = 'Webkit Moz O ms', omPrefixes = 'Webkit Moz O ms',
cssomPrefixes = omPrefixes.split(' '), cssomPrefixes = omPrefixes.split(' '),
domPrefixes = omPrefixes.toLowerCase().split(' '), domPrefixes = omPrefixes.toLowerCase().split(' '),
/*>>domprefixes*/
/*>>ns*/
ns = {'svg': 'http://www.w3.org/2000/svg'}, ns = {'svg': 'http://www.w3.org/2000/svg'},
/*>>ns*/
tests = {}, tests = {},
inputs = {}, inputs = {},
...@@ -44,38 +89,54 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -44,38 +89,54 @@ window.Modernizr = (function( window, document, undefined ) {
slice = classes.slice, slice = classes.slice,
featureName, featureName, // used in testing loop
/*>>teststyles*/
// Inject element with style element and some CSS rules
injectElementWithStyles = function( rule, callback, nodes, testnames ) { injectElementWithStyles = function( rule, callback, nodes, testnames ) {
var style, ret, node, docOverflow, var style, ret, node, docOverflow,
div = document.createElement('div'), div = document.createElement('div'),
body = document.body, // After page load injecting a fake body doesn't work so check if body exists
fakeBody = body || document.createElement('body'); body = document.body,
// IE6 and 7 won't return offsetWidth or offsetHeight unless it's in the body element, so we fake it.
fakeBody = body || document.createElement('body');
if ( parseInt(nodes, 10) ) { if ( parseInt(nodes, 10) ) {
while ( nodes-- ) { // In order not to give false positives we create a node for each test
// This also allows the method to scale for unspecified uses
while ( nodes-- ) {
node = document.createElement('div'); node = document.createElement('div');
node.id = testnames ? testnames[nodes] : mod + (nodes + 1); node.id = testnames ? testnames[nodes] : mod + (nodes + 1);
div.appendChild(node); div.appendChild(node);
} }
} }
style = ['&#173;','<style id="s', mod, '">', rule, '</style>'].join(''); // <style> elements in IE6-9 are considered 'NoScope' elements and therefore will be removed
// when injected with innerHTML. To get around this you need to prepend the 'NoScope' element
// with a 'scoped' element, in our case the soft-hyphen entity as it won't mess with our measurements.
// msdn.microsoft.com/en-us/library/ms533897%28VS.85%29.aspx
// Documents served as xml will throw if using &shy; so use xml friendly encoded version. See issue #277
style = ['&#173;','<style id="s', mod, '">', rule, '</style>'].join('');
div.id = mod; div.id = mod;
(body ? div : fakeBody).innerHTML += style; // IE6 will false positive on some tests due to the style element inside the test div somehow interfering offsetHeight, so insert it into body or fakebody.
// Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270
(body ? div : fakeBody).innerHTML += style;
fakeBody.appendChild(div); fakeBody.appendChild(div);
if ( !body ) { if ( !body ) {
fakeBody.style.background = ''; //avoid crashing IE8, if background image is used
fakeBody.style.overflow = 'hidden'; fakeBody.style.background = '';
//Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible
fakeBody.style.overflow = 'hidden';
docOverflow = docElement.style.overflow; docOverflow = docElement.style.overflow;
docElement.style.overflow = 'hidden'; docElement.style.overflow = 'hidden';
docElement.appendChild(fakeBody); docElement.appendChild(fakeBody);
} }
ret = callback(div, rule); ret = callback(div, rule);
if ( !body ) { // If this is done after page load we don't want to remove the body so check if body exists
if ( !body ) {
fakeBody.parentNode.removeChild(fakeBody); fakeBody.parentNode.removeChild(fakeBody);
docElement.style.overflow = docOverflow; docElement.style.overflow = docOverflow;
} else { } else {
...@@ -85,7 +146,12 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -85,7 +146,12 @@ window.Modernizr = (function( window, document, undefined ) {
return !!ret; return !!ret;
}, },
/*>>teststyles*/
/*>>mq*/
// adapted from matchMedia polyfill
// by Scott Jehl and Paul Irish
// gist.github.com/786768
testMediaQuery = function( mq ) { testMediaQuery = function( mq ) {
var matchMedia = window.matchMedia || window.msMatchMedia; var matchMedia = window.matchMedia || window.msMatchMedia;
...@@ -104,8 +170,18 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -104,8 +170,18 @@ window.Modernizr = (function( window, document, undefined ) {
return bool; return bool;
}, },
/*>>mq*/
/*>>hasevent*/
//
// isEventSupported determines if a given element supports the given event
// kangax.github.com/iseventsupported/
//
// The following results are known incorrects:
// Modernizr.hasEvent("webkitTransitionEnd", elem) // false negative
// Modernizr.hasEvent("textInput") // in Webkit. github.com/Modernizr/Modernizr/issues/333
// ...
isEventSupported = (function() { isEventSupported = (function() {
var TAGNAMES = { var TAGNAMES = {
...@@ -119,17 +195,20 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -119,17 +195,20 @@ window.Modernizr = (function( window, document, undefined ) {
element = element || document.createElement(TAGNAMES[eventName] || 'div'); element = element || document.createElement(TAGNAMES[eventName] || 'div');
eventName = 'on' + eventName; eventName = 'on' + eventName;
var isSupported = eventName in element; // When using `setAttribute`, IE skips "unload", WebKit skips "unload" and "resize", whereas `in` "catches" those
var isSupported = eventName in element;
if ( !isSupported ) { if ( !isSupported ) {
if ( !element.setAttribute ) { // If it has no `setAttribute` (i.e. doesn't implement Node interface), try generic element
if ( !element.setAttribute ) {
element = document.createElement('div'); element = document.createElement('div');
} }
if ( element.setAttribute && element.removeAttribute ) { if ( element.setAttribute && element.removeAttribute ) {
element.setAttribute(eventName, ''); element.setAttribute(eventName, '');
isSupported = is(element[eventName], 'function'); isSupported = is(element[eventName], 'function');
if ( !is(element[eventName], 'undefined') ) { // If property was created, "remove it" (by setting value to `undefined`)
if ( !is(element[eventName], 'undefined') ) {
element[eventName] = undefined; element[eventName] = undefined;
} }
element.removeAttribute(eventName); element.removeAttribute(eventName);
...@@ -141,8 +220,11 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -141,8 +220,11 @@ window.Modernizr = (function( window, document, undefined ) {
} }
return isEventSupported; return isEventSupported;
})(), })(),
/*>>hasevent*/
// TODO :: Add flag for hasownprop ? didn't last time
// hasOwnProperty shim by kangax needed for Safari 2.0 support
_hasOwnProperty = ({}).hasOwnProperty, hasOwnProp; _hasOwnProperty = ({}).hasOwnProperty, hasOwnProp;
if ( !is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined') ) { if ( !is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined') ) {
...@@ -151,11 +233,13 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -151,11 +233,13 @@ window.Modernizr = (function( window, document, undefined ) {
}; };
} }
else { else {
hasOwnProp = function (object, property) { hasOwnProp = function (object, property) { /* yes, this can give false positives/negatives, but most of the time we don't care about those */
return ((property in object) && is(object.constructor.prototype[property], 'undefined')); return ((property in object) && is(object.constructor.prototype[property], 'undefined'));
}; };
} }
// Adapted from ES5-shim https://github.com/kriskowal/es5-shim/blob/master/es5-shim.js
// es5.github.com/#x15.3.4.5
if (!Function.prototype.bind) { if (!Function.prototype.bind) {
Function.prototype.bind = function bind(that) { Function.prototype.bind = function bind(that) {
...@@ -199,22 +283,54 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -199,22 +283,54 @@ window.Modernizr = (function( window, document, undefined ) {
}; };
} }
/**
* setCss applies given styles to the Modernizr DOM node.
*/
function setCss( str ) { function setCss( str ) {
mStyle.cssText = str; mStyle.cssText = str;
} }
/**
* setCssAll extrapolates all vendor-specific css strings.
*/
function setCssAll( str1, str2 ) { function setCssAll( str1, str2 ) {
return setCss(prefixes.join(str1 + ';') + ( str2 || '' )); return setCss(prefixes.join(str1 + ';') + ( str2 || '' ));
} }
/**
* is returns a boolean for if typeof obj is exactly type.
*/
function is( obj, type ) { function is( obj, type ) {
return typeof obj === type; return typeof obj === type;
} }
/**
* contains returns a boolean for if substr is found within str.
*/
function contains( str, substr ) { function contains( str, substr ) {
return !!~('' + str).indexOf(substr); return !!~('' + str).indexOf(substr);
} }
/*>>testprop*/
// testProps is a generic CSS / DOM property test.
// In testing support for a given CSS property, it's legit to test:
// `elem.style[styleName] !== undefined`
// If the property is supported it will return an empty string,
// if unsupported it will return undefined.
// We'll take advantage of this quick test and skip setting a style
// on our modernizr element, but instead just testing undefined vs
// empty string.
// Because the testing of the CSS property names (with "-", as
// opposed to the camelCase DOM properties) is non-portable and
// non-standard but works in WebKit and IE (but not Gecko or Opera),
// we explicitly reject properties with dashes so that authors
// developing in WebKit or IE first don't end up with
// browser-specific content by accident.
function testProps( props, prefixed ) { function testProps( props, prefixed ) {
for ( var i in props ) { for ( var i in props ) {
var prop = props[i]; var prop = props[i];
...@@ -224,39 +340,83 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -224,39 +340,83 @@ window.Modernizr = (function( window, document, undefined ) {
} }
return false; return false;
} }
/*>>testprop*/
// TODO :: add testDOMProps
/**
* testDOMProps is a generic DOM property test; if a browser supports
* a certain property, it won't return undefined for it.
*/
function testDOMProps( props, obj, elem ) { function testDOMProps( props, obj, elem ) {
for ( var i in props ) { for ( var i in props ) {
var item = obj[props[i]]; var item = obj[props[i]];
if ( item !== undefined) { if ( item !== undefined) {
if (elem === false) return props[i]; // return the property name as a string
if (elem === false) return props[i];
if (is(item, 'function')){ // let's bind a function
return item.bind(elem || obj); if (is(item, 'function')){
// default to autobind unless override
return item.bind(elem || obj);
} }
return item; // return the unbound function or obj or value
return item;
} }
} }
return false; return false;
} }
/*>>testallprops*/
/**
* testPropsAll tests a list of DOM properties we want to check against.
* We specify literally ALL possible (known and/or likely) properties on
* the element including the non-vendor prefixed one, for forward-
* compatibility.
*/
function testPropsAll( prop, prefixed, elem ) { function testPropsAll( prop, prefixed, elem ) {
var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1), var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1),
props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' '); props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' ');
if(is(prefixed, "string") || is(prefixed, "undefined")) { // did they call .prefixed('boxSizing') or are we just testing a prop?
if(is(prefixed, "string") || is(prefixed, "undefined")) {
return testProps(props, prefixed); return testProps(props, prefixed);
} else { // otherwise, they called .prefixed('requestAnimationFrame', window[, elem])
} else {
props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' '); props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' ');
return testDOMProps(props, prefixed, elem); return testDOMProps(props, prefixed, elem);
} }
} tests['flexbox'] = function() { }
/*>>testallprops*/
/**
* Tests
* -----
*/
// The *new* flexbox
// dev.w3.org/csswg/css3-flexbox
tests['flexbox'] = function() {
return testPropsAll('flexWrap'); return testPropsAll('flexWrap');
}; tests['canvas'] = function() { };
// The *old* flexbox
// www.w3.org/TR/2009/WD-css3-flexbox-20090723/
tests['flexboxlegacy'] = function() {
return testPropsAll('boxDirection');
};
// On the S60 and BB Storm, getContext exists, but always returns undefined
// so we actually have to call getContext() to verify
// github.com/Modernizr/Modernizr/issues/issue/97/
tests['canvas'] = function() {
var elem = document.createElement('canvas'); var elem = document.createElement('canvas');
return !!(elem.getContext && elem.getContext('2d')); return !!(elem.getContext && elem.getContext('2d'));
}; };
...@@ -265,12 +425,28 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -265,12 +425,28 @@ window.Modernizr = (function( window, document, undefined ) {
return !!(Modernizr['canvas'] && is(document.createElement('canvas').getContext('2d').fillText, 'function')); return !!(Modernizr['canvas'] && is(document.createElement('canvas').getContext('2d').fillText, 'function'));
}; };
// webk.it/70117 is tracking a legit WebGL feature detect proposal
// We do a soft detect which may false positive in order to avoid
// an expensive context creation: bugzil.la/732441
tests['webgl'] = function() { tests['webgl'] = function() {
return !!window.WebGLRenderingContext; return !!window.WebGLRenderingContext;
}; };
/*
* The Modernizr.touch test only indicates if the browser supports
* touch events, which does not necessarily reflect a touchscreen
* device, as evidenced by tablets running Windows 7 or, alas,
* the Palm Pre / WebOS (touch) phones.
*
* Additionally, Chrome (desktop) used to lie about its support on this,
* but that has since been rectified: crbug.com/36415
*
* We also test for Firefox 4 Multitouch Support.
*
* For more info, see: modernizr.github.com/Modernizr/touch.html
*/
tests['touch'] = function() { tests['touch'] = function() {
var bool; var bool;
...@@ -287,6 +463,15 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -287,6 +463,15 @@ window.Modernizr = (function( window, document, undefined ) {
}; };
// geolocation is often considered a trivial feature detect...
// Turns out, it's quite tricky to get right:
//
// Using !!navigator.geolocation does two things we don't want. It:
// 1. Leaks memory in IE9: github.com/Modernizr/Modernizr/issues/513
// 2. Disables page caching in WebKit: webk.it/43956
//
// Meanwhile, in Firefox < 8, an about:config setting could expose
// a false positive that would throw an exception: bugzil.la/688158
tests['geolocation'] = function() { tests['geolocation'] = function() {
return 'geolocation' in navigator; return 'geolocation' in navigator;
...@@ -298,18 +483,31 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -298,18 +483,31 @@ window.Modernizr = (function( window, document, undefined ) {
}; };
// Chrome incognito mode used to throw an exception when using openDatabase
// It doesn't anymore.
tests['websqldatabase'] = function() { tests['websqldatabase'] = function() {
return !!window.openDatabase; return !!window.openDatabase;
}; };
// Vendors had inconsistent prefixing with the experimental Indexed DB:
// - Webkit's implementation is accessible through webkitIndexedDB
// - Firefox shipped moz_indexedDB before FF4b9, but since then has been mozIndexedDB
// For speed, we don't test the legacy (and beta-only) indexedDB
tests['indexedDB'] = function() { tests['indexedDB'] = function() {
return !!testPropsAll("indexedDB", window); return !!testPropsAll("indexedDB", window);
}; };
// documentMode logic from YUI to filter out IE8 Compat Mode
// which false positives.
tests['hashchange'] = function() { tests['hashchange'] = function() {
return isEventSupported('hashchange', window) && (document.documentMode === undefined || document.documentMode > 7); return isEventSupported('hashchange', window) && (document.documentMode === undefined || document.documentMode > 7);
}; };
// Per 1.6:
// This used to be Modernizr.historymanagement but the longer
// name has been deprecated in favor of a shorter and property-matching one.
// The old API is still available in 1.6, but as of 2.0 will throw a warning,
// and in the first release thereafter disappear entirely.
tests['history'] = function() { tests['history'] = function() {
return !!(window.history && history.pushState); return !!(window.history && history.pushState);
}; };
...@@ -319,28 +517,52 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -319,28 +517,52 @@ window.Modernizr = (function( window, document, undefined ) {
return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div); return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div);
}; };
// FF3.6 was EOL'ed on 4/24/12, but the ESR version of FF10
// will be supported until FF19 (2/12/13), at which time, ESR becomes FF17.
// FF10 still uses prefixes, so check for it until then.
// for more ESR info, see: mozilla.org/en-US/firefox/organizations/faq/
tests['websockets'] = function() { tests['websockets'] = function() {
return 'WebSocket' in window || 'MozWebSocket' in window; return 'WebSocket' in window || 'MozWebSocket' in window;
}; };
// css-tricks.com/rgba-browser-support/
tests['rgba'] = function() { tests['rgba'] = function() {
// Set an rgba() color and check the returned value
setCss('background-color:rgba(150,255,150,.5)'); setCss('background-color:rgba(150,255,150,.5)');
return contains(mStyle.backgroundColor, 'rgba'); return contains(mStyle.backgroundColor, 'rgba');
}; };
tests['hsla'] = function() { tests['hsla'] = function() {
setCss('background-color:hsla(120,40%,100%,.5)'); // Same as rgba(), in fact, browsers re-map hsla() to rgba() internally,
// except IE9 who retains it as hsla
setCss('background-color:hsla(120,40%,100%,.5)');
return contains(mStyle.backgroundColor, 'rgba') || contains(mStyle.backgroundColor, 'hsla'); return contains(mStyle.backgroundColor, 'rgba') || contains(mStyle.backgroundColor, 'hsla');
}; };
tests['multiplebgs'] = function() { tests['multiplebgs'] = function() {
setCss('background:url(https://),url(https://),red url(https://)'); // Setting multiple images AND a color on the background shorthand property
// and then querying the style.background property value for the number of
// occurrences of "url(" is a reliable method for detecting ACTUAL support for this!
setCss('background:url(https://),url(https://),red url(https://)');
return (/(url\s*\(.*?){3}/).test(mStyle.background); // If the UA supports multiple backgrounds, there should be three occurrences
}; tests['backgroundsize'] = function() { // of the string "url(" in the return value for elemStyle.background
return (/(url\s*\(.*?){3}/).test(mStyle.background);
};
// this will false positive in Opera Mini
// github.com/Modernizr/Modernizr/issues/396
tests['backgroundsize'] = function() {
return testPropsAll('backgroundSize'); return testPropsAll('backgroundSize');
}; };
...@@ -349,27 +571,41 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -349,27 +571,41 @@ window.Modernizr = (function( window, document, undefined ) {
}; };
// Super comprehensive table about all the unique implementations of
// border-radius: muddledramblings.com/table-of-css3-border-radius-compliance
tests['borderradius'] = function() { tests['borderradius'] = function() {
return testPropsAll('borderRadius'); return testPropsAll('borderRadius');
}; };
// WebOS unfortunately false positives on this test.
tests['boxshadow'] = function() { tests['boxshadow'] = function() {
return testPropsAll('boxShadow'); return testPropsAll('boxShadow');
}; };
// FF3.0 will false positive on this test
tests['textshadow'] = function() { tests['textshadow'] = function() {
return document.createElement('div').style.textShadow === ''; return document.createElement('div').style.textShadow === '';
}; };
tests['opacity'] = function() { tests['opacity'] = function() {
setCssAll('opacity:.55'); // Browsers that actually have CSS Opacity implemented have done so
// according to spec, which means their return values are within the
// range of [0.0,1.0] - including the leading zero.
return (/^0.55$/).test(mStyle.opacity); setCssAll('opacity:.55');
// The non-literal . in this regex is intentional:
// German Chrome returns this value as 0,55
// github.com/Modernizr/Modernizr/issues/#issue/59/comment/516632
return (/^0.55$/).test(mStyle.opacity);
}; };
// Note, Android < 4 will pass this test, but can only animate
// a single property at a time
// daneden.me/2011/12/putting-up-with-androids-bullshit/
tests['cssanimations'] = function() { tests['cssanimations'] = function() {
return testPropsAll('animationName'); return testPropsAll('animationName');
}; };
...@@ -381,13 +617,23 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -381,13 +617,23 @@ window.Modernizr = (function( window, document, undefined ) {
tests['cssgradients'] = function() { tests['cssgradients'] = function() {
/**
* For CSS Gradients syntax, please see:
* webkit.org/blog/175/introducing-css-gradients/
* developer.mozilla.org/en/CSS/-moz-linear-gradient
* developer.mozilla.org/en/CSS/-moz-radial-gradient
* dev.w3.org/csswg/css3-images/#gradients-
*/
var str1 = 'background-image:', var str1 = 'background-image:',
str2 = 'gradient(linear,left top,right bottom,from(#9f9),to(white));', str2 = 'gradient(linear,left top,right bottom,from(#9f9),to(white));',
str3 = 'linear-gradient(left top,#9f9, white);'; str3 = 'linear-gradient(left top,#9f9, white);';
setCss( setCss(
(str1 + '-webkit- '.split(' ').join(str2 + str1) + // legacy webkit syntax (FIXME: remove when syntax not in use anymore)
prefixes.join(str3 + str1)).slice(0, -str1.length) (str1 + '-webkit- '.split(' ').join(str2 + str1) +
// standard syntax // trailing 'background-image:'
prefixes.join(str3 + str1)).slice(0, -str1.length)
); );
return contains(mStyle.backgroundImage, 'gradient'); return contains(mStyle.backgroundImage, 'gradient');
...@@ -408,9 +654,15 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -408,9 +654,15 @@ window.Modernizr = (function( window, document, undefined ) {
var ret = !!testPropsAll('perspective'); var ret = !!testPropsAll('perspective');
if ( ret && 'webkitPerspective' in docElement.style ) { // Webkit's 3D transforms are passed off to the browser's own graphics renderer.
// It works fine in Safari on Leopard and Snow Leopard, but not in Chrome in
// some conditions. As a result, Webkit typically recognizes the syntax but
// will sometimes throw a false positive, thus we must do a more thorough check:
if ( ret && 'webkitPerspective' in docElement.style ) {
injectElementWithStyles('@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}', function( node, rule ) { // Webkit allows this media query to succeed only if the feature is enabled.
// `@media (transform-3d),(-webkit-transform-3d){ ... }`
injectElementWithStyles('@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}', function( node, rule ) {
ret = node.offsetLeft === 9 && node.offsetHeight === 3; ret = node.offsetLeft === 9 && node.offsetHeight === 3;
}); });
} }
...@@ -423,7 +675,13 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -423,7 +675,13 @@ window.Modernizr = (function( window, document, undefined ) {
}; };
/*>>fontface*/
// @font-face detection routine by Diego Perini
// javascript.nwbox.com/CSSSupport/
// false positives:
// WebOS github.com/Modernizr/Modernizr/issues/342
// WP7 github.com/Modernizr/Modernizr/issues/538
tests['fontface'] = function() { tests['fontface'] = function() {
var bool; var bool;
...@@ -437,7 +695,9 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -437,7 +695,9 @@ window.Modernizr = (function( window, document, undefined ) {
return bool; return bool;
}; };
/*>>fontface*/
// CSS generated content detection
tests['generatedcontent'] = function() { tests['generatedcontent'] = function() {
var bool; var bool;
...@@ -447,16 +707,35 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -447,16 +707,35 @@ window.Modernizr = (function( window, document, undefined ) {
return bool; return bool;
}; };
// These tests evaluate support of the video/audio elements, as well as
// testing what types of content they support.
//
// We're using the Boolean constructor here, so that we can extend the value
// e.g. Modernizr.video // true
// Modernizr.video.ogg // 'probably'
//
// Codec values from : github.com/NielsLeenheer/html5test/blob/9106a8/index.html#L845
// thx to NielsLeenheer and zcorpan
// Note: in some older browsers, "no" was a return value instead of empty string.
// It was live in FF3.5.0 and 3.5.1, but fixed in 3.5.2
// It was also live in Safari 4.0.0 - 4.0.4, but fixed in 4.0.5
tests['video'] = function() { tests['video'] = function() {
var elem = document.createElement('video'), var elem = document.createElement('video'),
bool = false; bool = false;
try { // IE9 Running on Windows Server SKU can cause an exception to be thrown, bug #224
try {
if ( bool = !!elem.canPlayType ) { if ( bool = !!elem.canPlayType ) {
bool = new Boolean(bool); bool = new Boolean(bool);
bool.ogg = elem.canPlayType('video/ogg; codecs="theora"') .replace(/^no$/,''); bool.ogg = elem.canPlayType('video/ogg; codecs="theora"') .replace(/^no$/,'');
bool.h264 = elem.canPlayType('video/mp4; codecs="avc1.42E01E"') .replace(/^no$/,''); // Without QuickTime, this value will be `undefined`. github.com/Modernizr/Modernizr/issues/546
bool.h264 = elem.canPlayType('video/mp4; codecs="avc1.42E01E"') .replace(/^no$/,'');
bool.webm = elem.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,''); bool.webm = elem.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,'');
} }
...@@ -476,7 +755,10 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -476,7 +755,10 @@ window.Modernizr = (function( window, document, undefined ) {
bool.ogg = elem.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,''); bool.ogg = elem.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,'');
bool.mp3 = elem.canPlayType('audio/mpeg;') .replace(/^no$/,''); bool.mp3 = elem.canPlayType('audio/mpeg;') .replace(/^no$/,'');
bool.wav = elem.canPlayType('audio/wav; codecs="1"') .replace(/^no$/,''); // Mimetypes accepted:
// developer.mozilla.org/En/Media_formats_supported_by_the_audio_and_video_elements
// bit.ly/iphoneoscodecs
bool.wav = elem.canPlayType('audio/wav; codecs="1"') .replace(/^no$/,'');
bool.m4a = ( elem.canPlayType('audio/x-m4a;') || bool.m4a = ( elem.canPlayType('audio/x-m4a;') ||
elem.canPlayType('audio/aac;')) .replace(/^no$/,''); elem.canPlayType('audio/aac;')) .replace(/^no$/,'');
} }
...@@ -486,6 +768,23 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -486,6 +768,23 @@ window.Modernizr = (function( window, document, undefined ) {
}; };
// In FF4, if disabled, window.localStorage should === null.
// Normally, we could not test that directly and need to do a
// `('localStorage' in window) && ` test first because otherwise Firefox will
// throw bugzil.la/365772 if cookies are disabled
// Also in iOS5 Private Browsing mode, attempting to use localStorage.setItem
// will throw the exception:
// QUOTA_EXCEEDED_ERRROR DOM Exception 22.
// Peculiarly, getItem and removeItem calls do not throw.
// Because we are forced to try/catch this, we'll go aggressive.
// Just FWIW: IE8 Compat mode supports these features completely:
// www.quirksmode.org/dom/html5.html
// But IE8 doesn't support either with local files
tests['localstorage'] = function() { tests['localstorage'] = function() {
try { try {
localStorage.setItem(mod, mod); localStorage.setItem(mod, mod);
...@@ -517,43 +816,79 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -517,43 +816,79 @@ window.Modernizr = (function( window, document, undefined ) {
}; };
// Thanks to Erik Dahlstrom
tests['svg'] = function() { tests['svg'] = function() {
return !!document.createElementNS && !!document.createElementNS(ns.svg, 'svg').createSVGRect; return !!document.createElementNS && !!document.createElementNS(ns.svg, 'svg').createSVGRect;
}; };
// specifically for SVG inline in HTML, not within XHTML
// test page: paulirish.com/demo/inline-svg
tests['inlinesvg'] = function() { tests['inlinesvg'] = function() {
var div = document.createElement('div'); var div = document.createElement('div');
div.innerHTML = '<svg/>'; div.innerHTML = '<svg/>';
return (div.firstChild && div.firstChild.namespaceURI) == ns.svg; return (div.firstChild && div.firstChild.namespaceURI) == ns.svg;
}; };
// SVG SMIL animation
tests['smil'] = function() { tests['smil'] = function() {
return !!document.createElementNS && /SVGAnimate/.test(toString.call(document.createElementNS(ns.svg, 'animate'))); return !!document.createElementNS && /SVGAnimate/.test(toString.call(document.createElementNS(ns.svg, 'animate')));
}; };
// This test is only for clip paths in SVG proper, not clip paths on HTML content
// demo: srufaculty.sru.edu/david.dailey/svg/newstuff/clipPath4.svg
// However read the comments to dig into applying SVG clippaths to HTML content here:
// github.com/Modernizr/Modernizr/issues/213#issuecomment-1149491
tests['svgclippaths'] = function() { tests['svgclippaths'] = function() {
return !!document.createElementNS && /SVGClipPath/.test(toString.call(document.createElementNS(ns.svg, 'clipPath'))); return !!document.createElementNS && /SVGClipPath/.test(toString.call(document.createElementNS(ns.svg, 'clipPath')));
}; };
/*>>webforms*/
// input features and input types go directly onto the ret object, bypassing the tests loop.
// Hold this guy to execute in a moment.
function webforms() { function webforms() {
Modernizr['input'] = (function( props ) { /*>>input*/
// Run through HTML5's new input attributes to see if the UA understands any.
// We're using f which is the <input> element created early on
// Mike Taylr has created a comprehensive resource for testing these attributes
// when applied to all input types:
// miketaylr.com/code/input-type-attr.html
// spec: www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary
// Only input placeholder is tested while textarea's placeholder is not.
// Currently Safari 4 and Opera 11 have support only for the input placeholder
// Both tests are available in feature-detects/forms-placeholder.js
Modernizr['input'] = (function( props ) {
for ( var i = 0, len = props.length; i < len; i++ ) { for ( var i = 0, len = props.length; i < len; i++ ) {
attrs[ props[i] ] = !!(props[i] in inputElem); attrs[ props[i] ] = !!(props[i] in inputElem);
} }
if (attrs.list){ if (attrs.list){
attrs.list = !!(document.createElement('datalist') && window.HTMLDataListElement); // safari false positive's on datalist: webk.it/74252
// see also github.com/Modernizr/Modernizr/issues/146
attrs.list = !!(document.createElement('datalist') && window.HTMLDataListElement);
} }
return attrs; return attrs;
})('autocomplete autofocus list placeholder max min multiple pattern required step'.split(' ')); })('autocomplete autofocus list placeholder max min multiple pattern required step'.split(' '));
Modernizr['inputtypes'] = (function(props) { /*>>input*/
/*>>inputtypes*/
// Run through HTML5's new input types to see if the UA understands any.
// This is put behind the tests runloop because it doesn't return a
// true/false like all the other tests; instead, it returns an object
// containing each input type with its corresponding true/false value
// Big thanks to @miketaylr for the html5 forms expertise. miketaylr.com/
Modernizr['inputtypes'] = (function(props) {
for ( var i = 0, bool, inputElemType, defaultView, len = props.length; i < len; i++ ) { for ( var i = 0, bool, inputElemType, defaultView, len = props.length; i < len; i++ ) {
inputElem.setAttribute('type', inputElemType = props[i]); inputElem.setAttribute('type', inputElemType = props[i]);
bool = inputElem.type !== 'text'; bool = inputElem.type !== 'text';
if ( bool ) { // We first check to see if the type we give it sticks..
// If the type does, we feed it a textual value, which shouldn't be valid.
// If the value doesn't stick, we know there's input sanitization which infers a custom UI
if ( bool ) {
inputElem.value = smile; inputElem.value = smile;
inputElem.style.cssText = 'position:absolute;visibility:hidden;'; inputElem.style.cssText = 'position:absolute;visibility:hidden;';
...@@ -563,18 +898,29 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -563,18 +898,29 @@ window.Modernizr = (function( window, document, undefined ) {
docElement.appendChild(inputElem); docElement.appendChild(inputElem);
defaultView = document.defaultView; defaultView = document.defaultView;
bool = defaultView.getComputedStyle && // Safari 2-4 allows the smiley as a value, despite making a slider
bool = defaultView.getComputedStyle &&
defaultView.getComputedStyle(inputElem, null).WebkitAppearance !== 'textfield' && defaultView.getComputedStyle(inputElem, null).WebkitAppearance !== 'textfield' &&
(inputElem.offsetHeight !== 0); // Mobile android web browser has false positive, so must
// check the height to see if the widget is actually there.
(inputElem.offsetHeight !== 0);
docElement.removeChild(inputElem); docElement.removeChild(inputElem);
} else if ( /^(search|tel)$/.test(inputElemType) ){ } else if ( /^(search|tel)$/.test(inputElemType) ){
} else if ( /^(url|email)$/.test(inputElemType) ) { // Spec doesn't define any special parsing or detectable UI
bool = inputElem.checkValidity && inputElem.checkValidity() === false; // behaviors so we pass these through as true
// Interestingly, opera fails the earlier test, so it doesn't
// even make it here.
} else if ( /^(url|email)$/.test(inputElemType) ) {
// Real url and email support comes with prebaked validation.
bool = inputElem.checkValidity && inputElem.checkValidity() === false;
} else { } else {
bool = inputElem.value != smile; // If the upgraded input compontent rejects the :) text, we got a winner
bool = inputElem.value != smile;
} }
} }
...@@ -582,19 +928,44 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -582,19 +928,44 @@ window.Modernizr = (function( window, document, undefined ) {
} }
return inputs; return inputs;
})('search tel url email datetime date month week time datetime-local number range color'.split(' ')); })('search tel url email datetime date month week time datetime-local number range color'.split(' '));
} /*>>inputtypes*/
}
/*>>webforms*/
// End of test definitions
// -----------------------
// Run through all tests and detect their support in the current UA.
// todo: hypothetically we could be doing an array of tests and use a basic loop here.
for ( var feature in tests ) { for ( var feature in tests ) {
if ( hasOwnProp(tests, feature) ) { if ( hasOwnProp(tests, feature) ) {
featureName = feature.toLowerCase(); // run the test, throw the return value into the Modernizr,
// then based on that boolean, define an appropriate className
// and push it into an array of classes we'll join later.
featureName = feature.toLowerCase();
Modernizr[featureName] = tests[feature](); Modernizr[featureName] = tests[feature]();
classes.push((Modernizr[featureName] ? '' : 'no-') + featureName); classes.push((Modernizr[featureName] ? '' : 'no-') + featureName);
} }
} }
/*>>webforms*/
// input tests need to run.
Modernizr.input || webforms(); Modernizr.input || webforms();
/*>>webforms*/
/**
* addTest allows the user to define their own feature tests
* the result will be added onto the Modernizr object,
* as well as an appropriate className set on the html element
*
* @param feature - String naming the feature
* @param test - Function returning true if feature is supported, false if not
*/
Modernizr.addTest = function ( feature, test ) { Modernizr.addTest = function ( feature, test ) {
if ( typeof feature == 'object' ) { if ( typeof feature == 'object' ) {
for ( var key in feature ) { for ( var key in feature ) {
...@@ -607,7 +978,12 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -607,7 +978,12 @@ window.Modernizr = (function( window, document, undefined ) {
feature = feature.toLowerCase(); feature = feature.toLowerCase();
if ( Modernizr[feature] !== undefined ) { if ( Modernizr[feature] !== undefined ) {
return Modernizr; // we're going to quit if you're trying to overwrite an existing test
// if we were to allow it, we'd do this:
// var re = new RegExp("\\b(no-)?" + feature + "\\b");
// docElement.className = docElement.className.replace( re, '' );
// but, no rly, stuff 'em.
return Modernizr;
} }
test = typeof test == 'function' ? test() : test; test = typeof test == 'function' ? test() : test;
...@@ -619,82 +995,412 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -619,82 +995,412 @@ window.Modernizr = (function( window, document, undefined ) {
} }
return Modernizr; return Modernizr; // allow chaining.
}; };
// Reset modElem.cssText to nothing to reduce memory footprint.
setCss(''); setCss('');
modElem = inputElem = null; modElem = inputElem = null;
/*>>shiv*/
/**
* @preserve HTML5 Shiv prev3.7.1 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
*/
;(function(window, document) {
/*jshint evil:true */
/** version */
var version = '3.7.0';
/** Preset options */
var options = window.html5 || {};
/** Used to skip problem elements */
var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i;
/** Not all elements can be cloned in IE **/
var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i;
/** Detect whether the browser supports default html5 styles */
var supportsHtml5Styles;
/** Name of the expando, to work with multiple documents or to re-shiv one document */
var expando = '_html5shiv';
/** The id for the the documents expando */
var expanID = 0;
/** Cached data for each document */
var expandoData = {};
/** Detect whether the browser supports unknown elements */
var supportsUnknownElements;
(function() {
try {
var a = document.createElement('a');
a.innerHTML = '<xyz></xyz>';
//if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles
supportsHtml5Styles = ('hidden' in a);
supportsUnknownElements = a.childNodes.length == 1 || (function() {
// assign a false positive if unable to shiv
(document.createElement)('a');
var frag = document.createDocumentFragment();
return (
typeof frag.cloneNode == 'undefined' ||
typeof frag.createDocumentFragment == 'undefined' ||
typeof frag.createElement == 'undefined'
);
}());
} catch(e) {
// assign a false positive if detection fails => unable to shiv
supportsHtml5Styles = true;
supportsUnknownElements = true;
}
}());
/*--------------------------------------------------------------------------*/
/**
* Creates a style sheet with the given CSS text and adds it to the document.
* @private
* @param {Document} ownerDocument The document.
* @param {String} cssText The CSS text.
* @returns {StyleSheet} The style element.
*/
function addStyleSheet(ownerDocument, cssText) {
var p = ownerDocument.createElement('p'),
parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement;
p.innerHTML = 'x<style>' + cssText + '</style>';
return parent.insertBefore(p.lastChild, parent.firstChild);
}
/**
* Returns the value of `html5.elements` as an array.
* @private
* @returns {Array} An array of shived element node names.
*/
function getElements() {
var elements = html5.elements;
return typeof elements == 'string' ? elements.split(' ') : elements;
}
/**
* Returns the data associated to the given document
* @private
* @param {Document} ownerDocument The document.
* @returns {Object} An object of data.
*/
function getExpandoData(ownerDocument) {
var data = expandoData[ownerDocument[expando]];
if (!data) {
data = {};
expanID++;
ownerDocument[expando] = expanID;
expandoData[expanID] = data;
}
return data;
}
/**
* returns a shived element for the given nodeName and document
* @memberOf html5
* @param {String} nodeName name of the element
* @param {Document} ownerDocument The context document.
* @returns {Object} The shived element.
*/
function createElement(nodeName, ownerDocument, data){
if (!ownerDocument) {
ownerDocument = document;
}
if(supportsUnknownElements){
return ownerDocument.createElement(nodeName);
}
if (!data) {
data = getExpandoData(ownerDocument);
}
var node;
if (data.cache[nodeName]) {
node = data.cache[nodeName].cloneNode();
} else if (saveClones.test(nodeName)) {
node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode();
} else {
node = data.createElem(nodeName);
}
// Avoid adding some elements to fragments in IE < 9 because
// * Attributes like `name` or `type` cannot be set/changed once an element
// is inserted into a document/fragment
// * Link elements with `src` attributes that are inaccessible, as with
// a 403 response, will cause the tab/window to crash
// * Script elements appended to fragments will execute when their `src`
// or `text` property is set
return node.canHaveChildren && !reSkip.test(nodeName) && !node.tagUrn ? data.frag.appendChild(node) : node;
}
/**
* returns a shived DocumentFragment for the given document
* @memberOf html5
* @param {Document} ownerDocument The context document.
* @returns {Object} The shived DocumentFragment.
*/
function createDocumentFragment(ownerDocument, data){
if (!ownerDocument) {
ownerDocument = document;
}
if(supportsUnknownElements){
return ownerDocument.createDocumentFragment();
}
data = data || getExpandoData(ownerDocument);
var clone = data.frag.cloneNode(),
i = 0,
elems = getElements(),
l = elems.length;
for(;i<l;i++){
clone.createElement(elems[i]);
}
return clone;
}
/**
* Shivs the `createElement` and `createDocumentFragment` methods of the document.
* @private
* @param {Document|DocumentFragment} ownerDocument The document.
* @param {Object} data of the document.
*/
function shivMethods(ownerDocument, data) {
if (!data.cache) {
data.cache = {};
data.createElem = ownerDocument.createElement;
data.createFrag = ownerDocument.createDocumentFragment;
data.frag = data.createFrag();
}
ownerDocument.createElement = function(nodeName) {
//abort shiv
if (!html5.shivMethods) {
return data.createElem(nodeName);
}
return createElement(nodeName, ownerDocument, data);
};
ownerDocument.createDocumentFragment = Function('h,f', 'return function(){' +
'var n=f.cloneNode(),c=n.createElement;' +
'h.shivMethods&&(' +
// unroll the `createElement` calls
getElements().join().replace(/[\w\-]+/g, function(nodeName) {
data.createElem(nodeName);
data.frag.createElement(nodeName);
return 'c("' + nodeName + '")';
}) +
');return n}'
)(html5, data.frag);
}
/*--------------------------------------------------------------------------*/
/**
* Shivs the given document.
* @memberOf html5
* @param {Document} ownerDocument The document to shiv.
* @returns {Document} The shived document.
*/
function shivDocument(ownerDocument) {
if (!ownerDocument) {
ownerDocument = document;
}
var data = getExpandoData(ownerDocument);
if (html5.shivCSS && !supportsHtml5Styles && !data.hasCSS) {
data.hasCSS = !!addStyleSheet(ownerDocument,
// corrects block display not defined in IE6/7/8/9
'article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}' +
// adds styling not present in IE6/7/8/9
'mark{background:#FF0;color:#000}' +
// hides non-rendered elements
'template{display:none}'
);
}
if (!supportsUnknownElements) {
shivMethods(ownerDocument, data);
}
return ownerDocument;
}
/*--------------------------------------------------------------------------*/
/**
* The `html5` object is exposed so that more elements can be shived and
* existing shiving can be detected on iframes.
* @type Object
* @example
*
* // options can be changed before the script is included
* html5 = { 'elements': 'mark section', 'shivCSS': false, 'shivMethods': false };
*/
var html5 = {
/**
* An array or space separated string of node names of the elements to shiv.
* @memberOf html5
* @type Array|String
*/
'elements': options.elements || 'abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video',
/**
* current version of html5shiv
*/
'version': version,
/**
* A flag to indicate that the HTML5 style sheet should be inserted.
* @memberOf html5
* @type Boolean
*/
'shivCSS': (options.shivCSS !== false),
/**
* Is equal to true if a browser supports creating unknown/HTML5 elements
* @memberOf html5
* @type boolean
*/
'supportsUnknownElements': supportsUnknownElements,
/**
* A flag to indicate that the document's `createElement` and `createDocumentFragment`
* methods should be overwritten.
* @memberOf html5
* @type Boolean
*/
'shivMethods': (options.shivMethods !== false),
/**
* A string to describe the type of `html5` object ("default" or "default print").
* @memberOf html5
* @type String
*/
'type': 'default',
// shivs the document according to the specified `html5` object options
'shivDocument': shivDocument,
//creates a shived element
createElement: createElement,
//creates a shived documentFragment
createDocumentFragment: createDocumentFragment
};
/*--------------------------------------------------------------------------*/
// expose html5
window.html5 = html5;
// shiv the document
shivDocument(document);
}(this, document));
/*>>shiv*/
// Assign private properties to the return object with prefix
Modernizr._version = version; Modernizr._version = version;
// expose these for the plugin API. Look in the source for how to join() them against your input
/*>>prefixes*/
Modernizr._prefixes = prefixes; Modernizr._prefixes = prefixes;
/*>>prefixes*/
/*>>domprefixes*/
Modernizr._domPrefixes = domPrefixes; Modernizr._domPrefixes = domPrefixes;
Modernizr._cssomPrefixes = cssomPrefixes; Modernizr._cssomPrefixes = cssomPrefixes;
/*>>domprefixes*/
/*>>mq*/
// Modernizr.mq tests a given media query, live against the current state of the window
// A few important notes:
// * If a browser does not support media queries at all (eg. oldIE) the mq() will always return false
// * A max-width or orientation query will be evaluated against the current state, which may change later.
// * You must specify values. Eg. If you are testing support for the min-width media query use:
// Modernizr.mq('(min-width:0)')
// usage:
// Modernizr.mq('only screen and (max-width:768)')
Modernizr.mq = testMediaQuery; Modernizr.mq = testMediaQuery;
/*>>mq*/
/*>>hasevent*/
// Modernizr.hasEvent() detects support for a given event, with an optional element to test on
// Modernizr.hasEvent('gesturestart', elem)
Modernizr.hasEvent = isEventSupported; Modernizr.hasEvent = isEventSupported;
/*>>hasevent*/
/*>>testprop*/
// Modernizr.testProp() investigates whether a given style property is recognized
// Note that the property names must be provided in the camelCase variant.
// Modernizr.testProp('pointerEvents')
Modernizr.testProp = function(prop){ Modernizr.testProp = function(prop){
return testProps([prop]); return testProps([prop]);
}; };
/*>>testprop*/
/*>>testallprops*/
// Modernizr.testAllProps() investigates whether a given style property,
// or any of its vendor-prefixed variants, is recognized
// Note that the property names must be provided in the camelCase variant.
// Modernizr.testAllProps('boxSizing')
Modernizr.testAllProps = testPropsAll; Modernizr.testAllProps = testPropsAll;
/*>>testallprops*/
/*>>teststyles*/
// Modernizr.testStyles() allows you to add custom styles to the document and test an element afterwards
// Modernizr.testStyles('#modernizr { position:absolute }', function(elem, rule){ ... })
Modernizr.testStyles = injectElementWithStyles; Modernizr.testStyles = injectElementWithStyles;
/*>>teststyles*/
/*>>prefixed*/
// Modernizr.prefixed() returns the prefixed or nonprefixed property name variant of your input
// Modernizr.prefixed('boxSizing') // 'MozBoxSizing'
// Properties must be passed as dom-style camelcase, rather than `box-sizing` hypentated style.
// Return values will also be the camelCase variant, if you need to translate that to hypenated style use:
//
// str.replace(/([A-Z])/g, function(str,m1){ return '-' + m1.toLowerCase(); }).replace(/^ms-/,'-ms-');
// If you're trying to ascertain which transition end event to bind to, you might do something like...
//
// var transEndEventNames = {
// 'WebkitTransition' : 'webkitTransitionEnd',
// 'MozTransition' : 'transitionend',
// 'OTransition' : 'oTransitionEnd',
// 'msTransition' : 'MSTransitionEnd',
// 'transition' : 'transitionend'
// },
// transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ];
Modernizr.prefixed = function(prop, obj, elem){ Modernizr.prefixed = function(prop, obj, elem){
if(!obj) { if(!obj) {
return testPropsAll(prop, 'pfx'); return testPropsAll(prop, 'pfx');
} else { } else {
return testPropsAll(prop, obj, elem); // Testing DOM property e.g. Modernizr.prefixed('requestAnimationFrame', window) // 'mozRequestAnimationFrame'
return testPropsAll(prop, obj, elem);
} }
}; };
/*>>prefixed*/
/*>>cssclasses*/
// Remove "no-js" class from <html> element, if it exists:
docElement.className = docElement.className.replace(/(^|\s)no-js(\s|$)/, '$1$2') +
// Add the new classes to the <html> element.
(enableClasses ? ' js ' + classes.join(' ') : '');
/*>>cssclasses*/
return Modernizr; return Modernizr;
})(this, this.document); })(this, this.document);
// Blob constructor
// http://dev.w3.org/2006/webapi/FileAPI/#constructorBlob
Modernizr.addTest('blobconstructor', function () {
try {
return !!new Blob();
} catch (e) {
return false;
}
});
// cors
// By Theodoor van Donge
Modernizr.addTest('cors', !!(window.XMLHttpRequest && 'withCredentials' in new XMLHttpRequest()));/**
* file tests for the File API specification
* Tests for objects specific to the File API W3C specification without
* being redundant (don't bother testing for Blob since it is assumed
* to be the File object's prototype.
*
* Will fail in Safari 5 due to its lack of support for the standards
* defined FileReader object
*/
Modernizr.addTest('filereader', function () {
return !!(window.File && window.FileList && window.FileReader);
});
// Filesystem API
// dev.w3.org/2009/dap/file-system/file-dir-sys.html
// The API will be present in Chrome incognito, but will throw an exception.
// See crbug.com/93417
//
// By Eric Bidelman (@ebidel)
Modernizr.addTest('filesystem', !!Modernizr.prefixed('requestFileSystem', window));// native JSON support.
// developer.mozilla.org/en/JSON
// this will also succeed if you've loaded the JSON2.js polyfill ahead of time
// ... but that should be obvious. :)
Modernizr.addTest('json', !!window.JSON && !!JSON.parse);
;
\ No newline at end of file
/*
* jQuery validVal version 5.0.2
* demo's and documentation:
* validval.frebsite.nl
*
* Copyright (c) 2013 Fred Heusschen
* www.frebsite.nl
*
* Dual licensed under the MIT and GPL licenses.
* http://en.wikipedia.org/wiki/MIT_License
* http://en.wikipedia.org/wiki/GNU_General_Public_License
*/
(function( $ )
{
var _PLUGIN_ = 'validVal',
_FIELD_ = 'validValField',
_VERSION_ = '5.0.2',
_INPUTS_ = 'textarea, select, input:not( [type="button"], [type="submit"], [type="reset"] )';
// validVal already excists
if ( $.fn[ _PLUGIN_ ] )
{
return;
}
function ValidVal( form, opts )
{
this.form = form;
this.opts = $.extend( true, {}, $.fn[ _PLUGIN_ ].defaults, opts );
this._gatherValidation();
this._bindEvents();
this._bindCustomEvents();
this.addField( this.opts.validate.fields.filter( $(_INPUTS_, this.form) ) );
}
ValidVal.prototype = {
// Public methods
addField: function( $field )
{
if ( isHtmlElement( $field ) || typeof $field == 'string' )
{
$field = $( $field );
}
if ( !( $field instanceof $ ) )
{
$.fn[ _PLUGIN_ ].debug( 'Not a valid argument for "$field"' );
return this;
}
var that = this;
return $field.each(
function()
{
var $f = $(this),
vf = $f.data( _FIELD_ );
if ( vf )
{
vf.destroy();
}
$f.data( _FIELD_, new ValidValField( $f, that ) );
}
);
},
validate: function( body, callCallback )
{
var that = this;
// Complement arguments
if ( typeof body == 'undefined' )
{
body = this.form;
callCallback = true;
}
else if ( typeof callCallback != 'boolean' )
{
callCallback = false;
}
if ( typeof this.opts.form.onValidate == 'function' )
{
this.opts.form.onValidate.call( this.form[ 0 ], this.opts.language );
}
// Set varialbes
var miss_arr = $(),
data_obj = {};
// Validate fields
this.opts.validate.fields.filter( $(_INPUTS_, body) ).each(
function()
{
var $f = $(this),
vf = $f.data( _FIELD_ );
if ( vf )
{
vf.validate( that.opts.validate.onSubmit )
if ( vf.valid )
{
var v = $f.val();
if ( $f.is( '[type="radio"]' ) || $f.is( '[type="checkbox"]' ) )
{
if ( !$f.is( ':checked' ) )
{
v = '';
}
}
if ( typeof v == 'undefined' || v == null )
{
v = '';
}
data_obj[ $f.attr( 'name' ) ] = v;
}
else
{
if ( that.opts.validate.onSubmit !== false )
{
miss_arr = miss_arr.add( $f );
}
}
}
}
);
// Not valid
if ( miss_arr.length > 0 )
{
if ( typeof this.opts.form.onInvalid == 'function' && callCallback )
{
this.opts.form.onInvalid.call( this.form[ 0 ], miss_arr, this.opts.language );
}
return false;
}
// Valid
else
{
if ( typeof this.opts.form.onValid == 'function' && callCallback )
{
this.opts.form.onValid.call( this.form[ 0 ], this.opts.language );
}
return data_obj;
}
},
submitForm: function()
{
var result = this.validate();
if ( result )
{
this.opts.validate.fields.filter( $(_INPUTS_, this.form) ).each(
function()
{
var vf = $(this).data( _FIELD_ );
if ( vf )
{
vf.clearPlaceholderValue();
}
}
);
}
return result;
},
resetForm: function()
{
var that = this;
if ( typeof this.opts.form.onReset == 'function' )
{
this.opts.form.onReset.call( this.form[ 0 ], this.opts.language );
}
this.opts.validate.fields.filter( $(_INPUTS_, this.form) ).each(
function()
{
var $f = $(this),
vf = $f.data( _FIELD_ );
if ( vf )
{
if ( vf.placeholderValue !== false )
{
$f.addClass( 'inactive' );
$f.val( vf.placeholderValue );
}
else
{
$f.val( vf.originalValue );
}
vf.isValid( true, true );
}
}
);
return true;
},
options: function( o )
{
if ( typeof o == 'object' )
{
this.opts = $.extend( this.opts, o );
}
return this.opts;
},
destroy: function()
{
this.form.unbind( '.vv' );
this.form.data( _PLUGIN_, null );
this.opts.validate.fields.filter( $(_INPUTS_, this.form) ).each(
function()
{
var vf = $(this).data( _FIELD_ );
if ( vf )
{
vf.destroy();
}
}
);
return true;
},
// Protected methods
_gatherValidation: function()
{
this.opts.validations = {};
if ( $.fn[ _PLUGIN_ ].customValidations )
{
this.opts.validations = $.extend( this.opts.validations, $.fn[ _PLUGIN_ ].customValidations );
}
if ( this.opts.customValidations )
{
this.opts.validations = $.extend( this.opts.validations, this.opts.customValidations );
}
this.opts.validations = $.extend( this.opts.validations, $.fn[ _PLUGIN_ ].defaultValidations );
return this;
},
_bindEvents: function ()
{
var that = this;
if ( this.form.is( 'form' ) )
{
this.form.attr( 'novalidate', 'novalidate' );
this.form.bind(
namespace( 'submit' ),
function( event, validate )
{
if ( typeof validate != 'boolean' )
{
validate = true;
}
return ( validate )
? that.submitForm()
: true;
}
);
this.form.bind(
namespace( 'reset' ),
function( event )
{
return that.resetForm();
}
);
}
return this;
},
_bindCustomEvents: function()
{
var that = this;
this.form.bind(
namespace([
'addField',
'destroy',
'validate',
'submitForm',
'resetForm',
'options'
]),
function()
{
arguments = Array.prototype.slice.call( arguments );
var event = arguments.shift(),
type = event.type;
event.stopPropagation();
if ( typeof that[ type ] != 'function' )
{
$.fn.validVal.debug( 'Public method "' + type + '" not found.' );
return false;
}
return that[ type ].apply( that, arguments );
}
);
return this;
}
};
function ValidValField( field, form )
{
this.field = field;
this.form = form;
this.originalValue = this.field.attr( 'value' ) || '';
this._gatherValidations();
this._bindEvents();
this._bindCustomEvents();
this._init();
}
ValidValField.prototype = {
// Public methods
validate: function( onEvent, fixPlaceholder )
{
var that = this;
if ( onEvent === false )
{
return;
}
if ( typeof fixPlaceholder != 'boolean' )
{
fixPlaceholder = true;
}
this.valid = true;
if ( ( this.field.is( ':hidden' ) && !this.form.opts.validate.fields.hidden ) ||
( this.field.is( ':disabled' ) && !this.form.opts.validate.fields.disabled )
) {
return true;
}
if ( fixPlaceholder )
{
this.clearPlaceholderValue();
}
if ( typeof this.form.opts.fields.onValidate == 'function' )
{
this.form.opts.fields.onValidate.call( this.field[ 0 ], this.form.form, this.form.opts.language );
}
var invalid_check = false,
val = trim( this.field.val() );
for ( var v in this.form.opts.validations )
{
var f = this.form.opts.validations[ v ];
if ( typeof f == 'function' && $.inArray( v, this.validations ) != -1 )
{
if ( !f.call( this.field[ 0 ], val ) )
{
invalid_check = v;
break;
}
}
}
this.valid = ( invalid_check ) ? false : true;
var callCallback = ( this.valid )
? ( onEvent !== 'invalid' )
: ( onEvent !== 'valid' );
this.isValid( this.valid, callCallback, invalid_check );
if ( this.validationgroup !== false )
{
$(_INPUTS_).not( this.field ).each(
function()
{
var vf = $(this).data( _FIELD_ );
if ( vf && vf.validationgroup == that.validationgroup )
{
vf.isValid( that.valid, true );
}
}
);
}
if ( fixPlaceholder )
{
this.restorePlaceholderValue();
}
if ( invalid_check )
{
$.fn[ _PLUGIN_ ].debug( 'invalid validation: ' + invalid_check );
}
return this.valid;
},
isValid: function( valid, callCallback )
{
if ( typeof valid == 'boolean' )
{
this.valid = valid;
if ( callCallback )
{
var fn = ( valid ) ? 'onValid' : 'onInvalid';
if ( typeof this.form.opts.fields[ fn ] == 'function' )
{
this.form.opts.fields[ fn ].call( this.field[ 0 ], this.form.form, this.form.opts.language );
}
}
}
return this.valid;
},
getValidations: function()
{
return this.validations;
},
setValidations: function( validations )
{
if ( typeof validations == 'string' )
{
this.validations = validations.split( ' ' );
}
else if ( validations instanceof Array )
{
this.validations = validations;
}
else
{
$.fn.validVal.debug( 'Argument "validations" should be an array.' );
}
return this.validations;
},
addValidation: function( validation )
{
if ( typeof validation == 'string' )
{
validation = validation.split( ' ' );
}
for( var v in validation )
{
this.validations.push( validation[ v ] );
}
return this.validations;
},
removeValidation: function( validation )
{
if ( typeof validation == 'string' )
{
validation = validation.split( ' ' );
}
for( var v in validation )
{
var pos = $.inArray( validation[ v ], this.validations );
if ( pos != -1 )
{
this.validations.splice( pos, 1 );
}
}
return this.validations;
},
clearPlaceholderValue: function()
{
this._togglePlaceholderValue( 'clear' );
return true;
},
restorePlaceholderValue: function()
{
this._togglePlaceholderValue( 'restore' );
return true;
},
destroy: function()
{
this.field.unbind( '.vv' );
this.field.data( _FIELD_, null );
return true;
},
// Protected methods
_gatherValidations: function()
{
this.autotab = false;
this.corresponding = false;
this.requiredgroup = false;
this.validationgroup = false;
this.placeholderValue = false;
this.placeholderNumber = false;
this.passwordplaceholder = false;
this.validations = [];
if ( this.field.is( 'select' ) )
{
this.originalValue = this.field.find( 'option:first' ).attr( 'value' ) || '';
}
else if ( this.field.is( 'textarea' ) )
{
this.originalValue = this.field.text();
}
// Refactor HTML5 usage
if ( this.form.opts.supportHtml5 )
{
var valids = this.field.data( 'vv-validations' );
if ( valids )
{
this.validations.push( valids );
this.__removeAttr( 'data-vv-validations' );
}
// Placeholder attribute, only use if placeholder not supported by browser or placeholder not in keepAttributes-option
if ( this.__hasHtml5Attr( 'placeholder' ) && this.field.attr( 'placeholder' ).length > 0 )
{
if ( !$.fn[ _PLUGIN_ ].support.placeholder || $.inArray( 'placeholder', this.form.opts.keepAttributes ) == -1 )
{
this.placeholderValue = this.field.attr( 'placeholder' );
}
}
if ( this.placeholderValue !== false )
{
this.__removeAttr( 'placeholder' );
}
// Pattern attribute
if ( this.__hasHtml5Attr( 'pattern' ) && this.field.attr( 'pattern' ).length > 0 )
{
this.pattern = this.field.attr( 'pattern' );
this.validations.push( 'pattern' );
this.__removeAttr( 'pattern' );
}
// Corresponding, required group and validation group
var dts = [ 'corresponding', 'requiredgroup', 'validationgroup' ];
for ( var d = 0, l = dts.length; d < l; d++ )
{
var dt = this.field.data( 'vv-' + dts[ d ] );
if ( dt )
{
this[ dts[ d ] ] = dt;
this.validations.push( dts[ d ] );
this.__removeAttr( 'data-vv-' + dts[ d ] );
}
}
// Attributes
var atr = [ 'required', 'autofocus' ];
for ( var a = 0, l = atr.length; a < l; a++ )
{
if ( this.__hasHtml5Attr( atr[ a ] ) )
{
this.validations.push( atr[ a ] );
this.__removeAttr( atr[ a ] );
}
}
// Type-values
var typ = [ 'number', 'email', 'url' ];
for ( var t = 0, l = typ.length; t < l; t++ )
{
if ( this.__hasHtml5Type( typ[ t ] ) )
{
this.validations.push( typ[ t ] );
}
}
// Autotab
if ( this.field.data( 'vv-autotab' ) )
{
this.autotab = true;
this.__removeAttr( 'data-vv-autotab' );
}
}
// Refactor non-HTML5 usage
var classes = this.field.attr( 'class' );
if ( classes && classes.length )
{
// Placeholder
if ( this.field.hasClass( 'placeholder' ) )
{
if ( this.field.is( 'select' ) )
{
var num = 0,
opt = this.field.data( 'vv-placeholder-number' );
if ( opt )
{
num = opt;
this.__removeAttr( 'data-vv-placeholder-number' );
}
else if ( typeof this.form.opts.selectPlaceholder == 'number' )
{
num = this.form.opts.selectPlaceholder;
}
else
{
var $options = this.field.find( 'option' ),
selected = $options.index( $options.filter( '[selected]' ) );
if ( selected > -1 )
{
num = selected;
}
}
this.placeholderNumber = num;
this.originalValue = this.field.find( 'option:eq( ' + num + ' )' ).attr( 'value' ) || '';
}
this.placeholderValue = this.originalValue;
this.originalValue = '';
this.__removeClass( 'placeholder' );
}
// Corresponding
var corsp = 'corresponding:',
start = classes.indexOf( corsp );
if ( start != -1 )
{
var corrcls = classes.substr( start ).split( ' ' )[ 0 ],
corresp = corrcls.substr( corsp.length );
if ( corresp.length )
{
this.corresponding = corresp;
this.validations.push( 'corresponding' );
this.field.removeClass( corrcls );
}
}
// Pattern
// still uses alt-attribute...
if ( this.field.hasClass( 'pattern' ) )
{
this.pattern = this.field.attr( 'alt' ) || '';
this.validations.push( 'pattern' );
this.__removeAttr( 'alt' );
this.__removeClass( 'pattern' );
}
// Groups
var grp = [ 'requiredgroup', 'validationgroup' ];
for ( var g = 0, l = grp.length; g < l; g++ )
{
var group = grp[ g ] + ':',
start = classes.indexOf( group );
if ( start != -1 )
{
var groupclass = classes.substr( start ).split( ' ' )[ 0 ],
groupname = groupclass.substr( group.length );
if ( groupname.length )
{
this[ grp[ g ] ] = groupname;
this.validations.push( grp[ g ]);
this.field.removeClass( groupclass );
}
}
}
// Autotab
if ( this.field.hasClass( 'autotab' ) )
{
this.autotab = true;
this.__removeClass( 'autotab' );
}
}
// Password placeholder
if ( this.placeholderValue !== false && this.field.is( '[type="password"]' ) )
{
this.passwordplaceholder = true;
}
// Add all remaining classes
var classes = this.field.attr( 'class' );
if ( classes && classes.length )
{
this.validations.push( classes );
}
this.validations = unique( this.validations.join( ' ' ).split( ' ' ) );
return this;
},
_bindEvents: function()
{
var that = this;
this.field.bind(
namespace( 'focus' ),
function( event )
{
$(this).addClass( 'focus' );
that.clearPlaceholderValue();
}
);
this.field.bind(
namespace( 'blur' ),
function( event )
{
$(this).removeClass( 'focus' );
that.validate( that.form.opts.validate.onBlur );
}
);
this.field.bind(
namespace( 'keyup' ),
function( event )
{
if ( !preventkeyup( event.keyCode ) )
{
that.validate( that.form.opts.validate.onKeyup, false );
}
}
);
if ( this.field.is( 'select, input[type="checkbox"], input[type="radio"]' ) )
{
this.field.bind(
namespace( 'change' ),
function( event )
{
$(this).trigger( namespace( 'blur' ) );
}
);
}
return this;
},
_bindCustomEvents: function()
{
var that = this;
this.field.bind(
namespace([
'validate',
'isValid',
'destroy',
'addValidation',
'removeValidation'
]),
function()
{
arguments = Array.prototype.slice.call( arguments );
var event = arguments.shift(),
type = event.type;
event.stopPropagation();
if ( typeof that[ type ] != 'function' )
{
$.fn.validVal.debug( 'Public method "' + type + '" not found.' );
return false;
}
return that[ type ].apply( that, arguments );
}
);
this.field.bind(
namespace([ 'validations' ]),
function( event, validations, callCallback )
{
if ( typeof validations == 'undefined' )
{
return this.getValidations();
}
else
{
return this.setValidations( validations, callCallback );
}
}
);
return this;
},
_init: function()
{
var that = this;
// Placeholder
if ( this.placeholderValue !== false )
{
if ( this.field.val() == '' )
{
this.field.val( this.placeholderValue );
}
if ( this.passwordplaceholder )
{
if ( this.field.val() == this.placeholderValue ) try
{
this.field[ 0 ].type = 'text';
}
catch( err ) {};
}
if ( this.field.val() == this.placeholderValue )
{
this.field.addClass( 'inactive' );
}
if ( this.field.is( 'select' ) )
{
this.field.find( 'option:eq(' + this.placeholderNumber + ')' ).addClass( 'inactive' );
this.field.bind(
namespace( 'change' ),
function( event )
{
$(this)[ that.field.val() == that.placeholderValue ? 'addClass' : 'removeClass' ]( 'inactive' );
}
);
}
}
// Corresponding
if ( this.corresponding !== false )
{
$(_INPUTS_).filter('[name="' + this.corresponding + '"]').bind(
namespace( 'blur' ),
function( event )
{
that.validate( that.form.opts.validate.onBlur );
}
).bind(
namespace( 'keyup' ),
function( event )
{
if ( !preventkeyup( event.keyCode ) )
{
that.validate( that.form.opts.validate.onKeyup, false );
}
}
);
}
// Autotabbing
if ( this.autotab )
{
var max = this.field.attr( 'maxlength' ),
tab = this.field.attr( 'tabindex' ),
$next = $(_INPUTS_).filter('[tabindex="' + ( parseInt( tab ) + 1 ) + '"]');
if ( this.field.is( 'select' ) )
{
if ( tab )
{
this.field.bind(
namespace( 'change' ),
function( event )
{
if ( $next.length )
{
$next.focus();
}
}
);
}
}
else
{
if ( max && tab )
{
this.field.bind(
namespace( 'keyup' ),
function( event )
{
if ( $(this).val().length == max )
{
if ( !preventkeyup( event.keyCode ) )
{
$(this).trigger( namespace( 'blur' ) );
if ( $next.length )
{
$next.focus();
}
}
}
}
);
}
}
}
// Autofocus
if ( $.inArray( 'autofocus', this.validations ) != -1 && !this.field.is( ':disabled' ) )
{
this.field.focus();
}
return this;
},
_togglePlaceholderValue: function( toggle )
{
if ( this.placeholderValue !== false )
{
if ( toggle == 'clear' )
{
var v1 = this.placeholderValue,
v2 = '',
cl = 'removeClass',
tp = 'password';
}
else
{
var v1 = '',
v2 = this.placeholderValue,
cl = 'addClass',
tp = 'text';
}
if ( this.field.val() == v1 && !this.field.is( 'select' ) )
{
this.field.val( v2 );
this.field[ cl ]( 'inactive' );
if ( this.passwordplaceholder ) try
{
this.field[ 0 ].type = tp;
}
catch( err ) {};
}
}
return this;
},
// Private methods
__hasHtml5Attr: function( a )
{
// non HTML5 browsers
if ( typeof this.field.attr( a ) == 'undefined' )
{
return false;
}
// HTML5 browsers
if ( this.field.attr( a ) === 'false' || this.field.attr( a ) === false )
{
return false;
}
return true;
},
__hasHtml5Type: function( t )
{
// cool HTML5 browsers
if ( this.field.attr( 'type' ) == t )
{
return true;
}
// non-HTML5 but still cool browsers
if ( this.field.is( 'input[type="' + t + '"]' ) )
{
return true;
}
// non-HTML5, non-cool browser
var $c = $( '<div />' ).append( this.field.clone() ).html()
if ( $c.indexOf( 'type="' + t + '"' ) != -1 || $c.indexOf( 'type=\'' + t + '\'' ) != -1 || $c.indexOf( 'type=' + t + '' ) != -1 )
{
return true;
}
return false;
},
__removeAttr: function( a )
{
if ( $.inArray( a, this.form.opts.keepAttributes ) == -1 )
{
this.field.removeAttr( a );
}
return this;
},
__removeClass: function( c )
{
if ( $.inArray( c, this.form.opts.keepClasses ) == -1 )
{
this.field.removeClass( c );
}
return this;
}
};
$.fn[ _PLUGIN_ ] = function( o, c )
{
return this.each(
function()
{
var $t = $(this);
if ( $t.data( _PLUGIN_ ) )
{
$t.data( _PLUGIN_ ).destroy();
}
$t.data( _PLUGIN_, new ValidVal( $t, o, c ) );
}
);
};
$.fn[ _PLUGIN_ ].version = _VERSION_;
$.fn[ _PLUGIN_ ].defaults = {
'selectPlaceholder' : 0,
'supportHtml5' : true,
'language' : 'en',
'customValidations' : {},
'validate' : {
'onBlur' : true,
'onSubmit' : true,
'onKeyup' : false,
'fields' : {
'hidden' : false,
'disabled' : false,
'filter' : function( $i )
{
return $i;
}
}
},
'fields' : {
'onValidate' : null,
'onValid' : function()
{
$(this).add( $(this).parent() ).removeClass( 'invalid' );
},
'onInvalid' : function()
{
$(this).add( $(this).parent() ).addClass( 'invalid' );
}
},
'form' : {
'onReset' : null,
'onValidate': null,
'onValid' : null,
'onInvalid' : function( fieldArr, language )
{
switch ( language )
{
case 'nl':
msg = 'Let op, niet alle velden zijn correct ingevuld.';
break;
case 'de':
msg = 'Achtung, nicht alle Felder sind korrekt ausgefuellt.';
break;
case 'es':
msg = 'Atención, no se han completado todos los campos correctamente.';
break;
case 'en':
default:
msg = 'Attention, not all fields have been filled out correctly.';
break;
}
alert( msg );
fieldArr.first().focus();
}
},
'keepClasses' : [ 'required' ],
'keepAttributes': [ 'pattern', 'placeholder' ]
};
$.fn[ _PLUGIN_ ].defaultValidations = {
'required': function( v )
{
var $f = $(this);
if ( $f.is( '[type="radio"]' ) || $f.is( '[type="checkbox"]' ) )
{
if ( $f.is( '[type="radio"]' ) )
{
var name = $f.attr( 'name' );
if ( name && name.length > 0 )
{
$f = $( 'input[name="' + name + '"]' );
}
}
if ( !$f.is( ':checked' ) )
{
return false;
}
}
else if ( $f.is( 'select' ) )
{
var vf = $f.data( _FIELD_ );
if ( vf && vf.placeholderValue !== false )
{
if ( $f.val() == vf.placeholderValue )
{
return false;
}
}
else
{
if ( v.length == 0 )
{
return false;
}
}
}
else
{
if ( v.length == 0 )
{
return false;
}
}
return true;
},
'Required': function( v )
{
return $.fn[ _PLUGIN_ ].defaultValidations.required.call( this, v );
},
'requiredgroup': function( v )
{
var $f = $(this),
vf = $f.data( _FIELD_ );
if ( vf && vf.requiredgroup !== false )
{
$f = $();
$(_INPUTS_).each(
function()
{
var tf = $(this).data( _FIELD_ );
if ( tf && tf.requiredgroup == vf.requiredgroup )
{
$f = $f.add( this );
}
}
);
}
var result = false;
$f.each(
function()
{
var f = this;
if ( $.fn[ _PLUGIN_ ].defaultValidations.required.call( f, trim( $(f).val() ) ) )
{
result = true;
}
}
);
return result;
},
'corresponding': function( v )
{
var org = '',
vf = $(this).data( _FIELD_ );
if ( vf && vf.corresponding !== false )
{
var $f = $(_INPUTS_).filter('[name="' + vf.corresponding + '"]'),
vf = $f.data( _FIELD_ );
if ( vf )
{
vf.clearPlaceholderValue();
org = trim( $f.val() );
vf.restorePlaceholderValue();
}
return ( v == org );
}
return false;
},
'number': function( v )
{
v = stripWhitespace( v );
if ( v.length == 0 )
{
return true;
}
if ( isNaN( v ) )
{
return false;
}
return true;
},
'email': function( v )
{
if ( v.length == 0 )
{
return true;
}
var r = /^([a-zA-Z0-9_\.\-+])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
return r.test( v );
},
'url': function( v )
{
if ( v.length == 0 )
{
return true;
}
if ( v.match(/^www\./) )
{
v = "http://" + v;
}
return v.match(/^(http\:\/\/|https\:\/\/)(.{4,})$/);
},
'pattern': function( v )
{
if ( v.length == 0 )
{
return true;
}
var $f = $(this),
vf = $f.data( _FIELD_ );
if ( vf )
{
var p = vf.pattern;
if ( p.slice( 0, 1 ) == '/' )
{
p = p.slice( 1 );
}
if ( p.slice( -1 ) == '/' )
{
p = p.slice( 0, -1 );
}
return new RegExp( p ).test( v );
}
}
};
// test for borwser support
$.fn[ _PLUGIN_ ].support = {
touch: (function()
{
return 'ontouchstart' in document.documentElement;
})(),
placeholder: (function()
{
return 'placeholder' in document.createElement( 'input' );
})()
};
$.fn[ _PLUGIN_ ].debug = function( msg ) {};
$.fn[ _PLUGIN_ ].deprecated = function( func, alt )
{
if ( typeof console != 'undefined' )
{
if ( typeof console.error != 'undefined' )
{
console.error( func + ' is DEPRECATED, use ' + alt + ' instead.' );
}
}
};
// Create debugger is it doesn't already excists
if ( !$.fn.validValDebug )
{
$.fn.validValDebug = function( b )
{
$.fn[ _PLUGIN_ ].debug( 'validVal debugger not installed!' );
return this;
}
}
function isHtmlElement( field )
{
if ( typeof HTMLElement != 'undefined' )
{
return field instanceof HTMLElement;
}
return ( field.nodeType && field.nodeType == Node.ELEMENT_NODE );
}
function namespace( events )
{
if ( typeof events == 'string' )
{
events = events.split( ' ' );
}
return events.join( '.vv ' ) + '.vv';
}
function unique( arr )
{
return $.grep(
arr,
function( v, k )
{
return $.inArray( v, arr ) === k;
}
);
}
function trim( str )
{
if ( str === null )
{
return '';
}
if ( typeof str == 'object' )
{
var arr = [];
for ( var a in str )
{
arr[ a ] = trim( str[ a ] );
}
return arr;
}
if ( typeof str != 'string' )
{
return '';
}
if ( str.length == 0 )
{
return '';
}
return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
}
function stripWhitespace( str )
{
if ( str === null )
{
return '';
}
if ( typeof str == 'object' )
{
for ( var a in str )
{
str[ a ] = stripWhitespace( str[ a ] );
}
return str;
}
if ( typeof str != 'string' )
{
return '';
}
if ( str.length == 0 )
{
return '';
}
str = trim( str );
var r = [ ' ', '-', '+', '(', ')', '/', '\\' ];
for ( var i = 0, l = r.length; i < l; i++ )
{
str = str.split( r[ i ] ).join( '' );
}
return str;
}
function preventkeyup( kc )
{
switch( kc ) {
case 9: // tab
case 13: // enter
case 16: // shift
case 17: // control
case 18: // alt
case 37: // left
case 38: // up
case 39: // right
case 40: // down
case 224: // command
return true;
break;
default:
return false;
break;
}
}
})( jQuery );
\ No newline at end of file
...@@ -29,14 +29,15 @@ ...@@ -29,14 +29,15 @@
<script type="text/javascript" src="ext/libs/jio/erp5storage.js"></script> <script type="text/javascript" src="ext/libs/jio/erp5storage.js"></script>
<!-- 3rd party plugins --> <!-- 3rd party plugins -->
<script type="text/javascript" src="js/plugins/state/state.js"></script> <script type="text/javascript" src="ext/plugins/modernizr/modernizr.js"></script>
<script type="text/javascript" src="js/plugins/validval/jquery.validVal.js"></script> <script type="text/javascript" src="ext/plugins/state/state.js"></script>
<script type="text/javascript" src="js/plugins/i18next/i18next.js"></script> <script type="text/javascript" src="ext/plugins/validval/jquery.validVal.js"></script>
<script type="text/javascript" src="js/plugins/modernizr/modernizr.js"></script> <script type="text/javascript" src="ext/plugins/i18next/i18next.js"></script>
<script type="text/javascript" src="js/plugins/selectivzr/selectivzr.js"></script> <script type="text/javascript" src="ext/plugins/hellojs/hellojs.js"></script>
<script type="text/javascript" src="js/plugins/hellojs/hellojs.js"></script> <!-- <script type="text/javascript" src="ext/plugins/jspdf/jspdf.js"></script> -->
<!-- <script type="text/javascript" src="js/plugins/jspdf/jspdf.js"></script> -->
<script type="text/javascript" src="js/shims.js"></script> <!-- IE8 and worse -->
<!-- <script type="text/javascript" src="ext/plugins/selectivzr/selectivzr.js"></script> -->
<!-- stuff happens here --> <!-- stuff happens here -->
<script type="text/javascript" data-storage="data/storages.json" data-config="data/global.json" src="js/erp5_loader.js"></script> <script type="text/javascript" data-storage="data/storages.json" data-config="data/global.json" src="js/erp5_loader.js"></script>
......
/*
* jQuery validVal version 5.0.1
* demo's and documentation:
* validval.frebsite.nl
*
* Copyright (c) 2013 Fred Heusschen
* www.frebsite.nl
*
* Dual licensed under the MIT and GPL licenses.
* http://en.wikipedia.org/wiki/MIT_License
* http://en.wikipedia.org/wiki/GNU_General_Public_License
*/
(function( $ )
{
var _PLUGIN_ = 'validVal',
_FIELD_ = 'validValField',
_VERSION_ = '5.0.1',
_INPUTS_ = 'textarea, select, input:not( [type="button"], [type="submit"], [type="reset"] )';
// validVal already excists
if ( $.fn[ _PLUGIN_ ] )
{
return;
}
function ValidVal( form, opts )
{
this.form = form;
this.opts = $.extend( true, {}, $.fn[ _PLUGIN_ ].defaults, opts );
this._gatherValidation();
this._bindEvents();
this._bindCustomEvents();
this.addField( this.opts.validate.fields.filter( $(_INPUTS_, this.form) ) );
}
ValidVal.prototype = {
// Public methods
addField: function( $field )
{
if ( isHtmlElement( $field ) || typeof $field == 'string' )
{
$field = $( $field );
}
if ( !( $field instanceof $ ) )
{
$.fn[ _PLUGIN_ ].debug( 'Not a valid argument for "$field"' );
return this;
}
var that = this;
return $field.each(
function()
{
var $f = $(this),
vf = $f.data( _FIELD_ );
if ( vf )
{
vf.destroy();
}
$f.data( _FIELD_, new ValidValField( $f, that ) );
}
);
},
validate: function( body, callCallback )
{
var that = this;
// Complement arguments
if ( typeof body == 'undefined' )
{
body = this.form;
callCallback = true;
}
else if ( typeof callCallback != 'boolean' )
{
callCallback = false;
}
if ( typeof this.opts.form.onValidate == 'function' )
{
this.opts.form.onValidate.call( this.form[ 0 ], this.opts.language );
}
// Set varialbes
var miss_arr = $(),
data_obj = {};
// Validate fields
this.opts.validate.fields.filter( $(_INPUTS_, body) ).each(
function()
{
var $f = $(this),
vf = $f.data( _FIELD_ );
if ( vf )
{
vf.validate( that.opts.validate.onSubmit )
if ( vf.valid )
{
var v = $f.val();
if ( $f.is( '[type="radio"]' ) || $f.is( '[type="checkbox"]' ) )
{
if ( !$f.is( ':checked' ) )
{
v = '';
}
}
if ( typeof v == 'undefined' || v == null )
{
v = '';
}
data_obj[ $f.attr( 'name' ) ] = v;
}
else
{
if ( that.opts.validate.onSubmit !== false )
{
miss_arr = miss_arr.add( $f );
}
}
}
}
);
// Not valid
if ( miss_arr.length > 0 )
{
if ( typeof this.opts.form.onInvalid == 'function' && callCallback )
{
this.opts.form.onInvalid.call( this.form[ 0 ], miss_arr, this.opts.language );
}
return false;
}
// Valid
else
{
if ( typeof this.opts.form.onValid == 'function' && callCallback )
{
this.opts.form.onValid.call( this.form[ 0 ], this.opts.language );
}
return data_obj;
}
},
submitForm: function()
{
var result = this.validate();
if ( result )
{
this.opts.validate.fields.filter( $(_INPUTS_, this.form) ).each(
function()
{
var vf = $(this).data( _FIELD_ );
if ( vf )
{
vf.clearPlaceholderValue();
}
}
);
}
return result;
},
resetForm: function()
{
var that = this;
if ( typeof this.opts.form.onReset == 'function' )
{
this.opts.form.onReset.call( this.form[ 0 ], this.opts.language );
}
this.opts.validate.fields.filter( $(_INPUTS_, this.form) ).each(
function()
{
var $f = $(this),
vf = $f.data( _FIELD_ );
if ( vf )
{
if ( vf.placeholderValue !== false )
{
$f.addClass( 'inactive' );
$f.val( vf.placeholderValue );
}
else
{
$f.val( vf.originalValue );
}
vf.isValid( true, true );
}
}
);
return true;
},
options: function( o )
{
if ( typeof o == 'object' )
{
this.opts = $.extend( this.opts, o );
}
return this.opts;
},
destroy: function()
{
this.form.unbind( '.vv' );
this.form.data( _PLUGIN_, null );
this.opts.validate.fields.filter( $(_INPUTS_, this.form) ).each(
function()
{
var vf = $(this).data( _FIELD_ );
if ( vf )
{
vf.destroy();
}
}
);
return true;
},
// Protected methods
_gatherValidation: function()
{
this.opts.validations = {};
if ( $.fn[ _PLUGIN_ ].customValidations )
{
this.opts.validations = $.extend( this.opts.validations, $.fn[ _PLUGIN_ ].customValidations );
}
if ( this.opts.customValidations )
{
this.opts.validations = $.extend( this.opts.validations, this.opts.customValidations );
}
this.opts.validations = $.extend( this.opts.validations, $.fn[ _PLUGIN_ ].defaultValidations );
return this;
},
_bindEvents: function ()
{
var that = this;
if ( this.form.is( 'form' ) )
{
this.form.attr( 'novalidate', 'novalidate' );
this.form.bind(
namespace( 'submit' ),
function( event, validate )
{
if ( typeof validate != 'boolean' )
{
validate = true;
}
return ( validate )
? that.submitForm()
: true;
}
);
this.form.bind(
namespace( 'reset' ),
function( event )
{
return that.resetForm();
}
);
}
return this;
},
_bindCustomEvents: function()
{
var that = this;
this.form.bind(
namespace([
'addField',
'destroy',
'validate',
'submitForm',
'resetForm',
'options'
]),
function()
{
arguments = Array.prototype.slice.call( arguments );
var event = arguments.shift(),
type = event.type;
event.stopPropagation();
if ( typeof that[ type ] != 'function' )
{
$.fn.validVal.debug( 'Public method "' + type + '" not found.' );
return false;
}
return that[ type ].apply( that, arguments );
}
);
return this;
}
};
function ValidValField( field, form )
{
this.field = field;
this.form = form;
this.originalValue = this.field.attr( 'value' ) || '';
this._gatherValidations();
this._bindEvents();
this._bindCustomEvents();
this._init();
}
ValidValField.prototype = {
// Public methods
validate: function( onEvent, fixPlaceholder )
{
var that = this;
if ( onEvent === false )
{
return;
}
if ( typeof fixPlaceholder != 'boolean' )
{
fixPlaceholder = true;
}
this.valid = true;
if ( ( this.field.is( ':hidden' ) && !this.form.opts.validate.fields.hidden ) ||
( this.field.is( ':disabled' ) && !this.form.opts.validate.fields.disabled )
) {
return true;
}
if ( fixPlaceholder )
{
this.clearPlaceholderValue();
}
if ( typeof this.form.opts.fields.onValidate == 'function' )
{
this.form.opts.fields.onValidate.call( this.field[ 0 ], this.form.form, this.form.opts.language );
}
var invalid_check = false,
val = trim( this.field.val() );
for ( var v in this.form.opts.validations )
{
var f = this.form.opts.validations[ v ];
if ( typeof f == 'function' && $.inArray( v, this.validations ) != -1 )
{
if ( !f.call( this.field[ 0 ], val ) )
{
invalid_check = v;
break;
}
}
}
this.valid = ( invalid_check ) ? false : true;
var callCallback = ( this.valid )
? ( onEvent !== 'invalid' )
: ( onEvent !== 'valid' );
this.isValid( this.valid, callCallback, invalid_check );
if ( this.validationgroup !== false )
{
$(_INPUTS_).not( this.field ).each(
function()
{
var vf = $(this).data( _FIELD_ );
if ( vf && vf.validationgroup == that.validationgroup )
{
vf.isValid( that.valid, true );
}
}
);
}
if ( fixPlaceholder )
{
this.restorePlaceholderValue();
}
if ( invalid_check )
{
$.fn[ _PLUGIN_ ].debug( 'invalid validation: ' + invalid_check );
}
return this.valid;
},
isValid: function( valid, callCallback )
{
if ( typeof valid == 'boolean' )
{
this.valid = valid;
if ( callCallback )
{
var fn = ( valid ) ? 'onValid' : 'onInvalid';
if ( typeof this.form.opts.fields[ fn ] == 'function' )
{
this.form.opts.fields[ fn ].call( this.field[ 0 ], this.form.form, this.form.opts.language );
}
}
}
return this.valid;
},
getValidations: function()
{
return this.validations;
},
setValidations: function( validations )
{
if ( typeof validations == 'string' )
{
this.validations = validations.split( ' ' );
}
else if ( validations instanceof Array )
{
this.validations = validations;
}
else
{
$.fn.validVal.debug( 'Argument "validations" should be an array.' );
}
return this.validations;
},
addValidation: function( validation )
{
if ( typeof validation == 'string' )
{
validation = validation.split( ' ' );
}
for( var v in validation )
{
this.validations.push( validation[ v ] );
}
return this.validations;
},
removeValidation: function( validation )
{
if ( typeof validation == 'string' )
{
validation = validation.split( ' ' );
}
for( var v in validation )
{
var pos = $.inArray( validation[ v ], this.validations );
if ( pos != -1 )
{
this.validations.splice( pos, 1 );
}
}
return this.validations;
},
clearPlaceholderValue: function()
{
this._togglePlaceholderValue( 'clear' );
return true;
},
restorePlaceholderValue: function()
{
this._togglePlaceholderValue( 'restore' );
return true;
},
destroy: function()
{
this.field.unbind( '.vv' );
this.field.data( _FIELD_, null );
return true;
},
// Protected methods
_gatherValidations: function()
{
this.autotab = false;
this.corresponding = false;
this.requiredgroup = false;
this.validationgroup = false;
this.placeholderValue = false;
this.placeholderNumber = false;
this.passwordplaceholder = false;
this.validations = [];
if ( this.field.is( 'select' ) )
{
this.originalValue = this.field.find( 'option:first' ).attr( 'value' ) || '';
}
else if ( this.field.is( 'textarea' ) )
{
this.originalValue = this.field.text();
}
// Refactor HTML5 usage
if ( this.form.opts.supportHtml5 )
{
var valids = this.field.data( 'vv-validations' );
if ( valids )
{
this.validations.push( valids );
this.__removeAttr( 'data-vv-validations' );
}
// Placeholder attribute, only use if placeholder not supported by browser or placeholder not in keepAttributes-option
if ( this.__hasHtml5Attr( 'placeholder' ) && this.field.attr( 'placeholder' ).length > 0 )
{
if ( !$.fn[ _PLUGIN_ ].support.placeholder || $.inArray( 'placeholder', this.form.opts.keepAttributes ) == -1 )
{
this.placeholderValue = this.field.attr( 'placeholder' );
}
}
if ( this.placeholderValue !== false )
{
this.__removeAttr( 'placeholder' );
}
// Pattern attribute
if ( this.__hasHtml5Attr( 'pattern' ) && this.field.attr( 'pattern' ).length > 0 )
{
this.pattern = this.field.attr( 'pattern' );
this.validations.push( 'pattern' );
this.__removeAttr( 'pattern' );
}
// Corresponding, required group and validation group
var dts = [ 'corresponding', 'requiredgroup', 'validationgroup' ];
for ( var d = 0, l = dts.length; d < l; d++ )
{
var dt = this.field.data( 'vv-' + dts[ d ] );
if ( dt )
{
this[ dts[ d ] ] = dt;
this.validations.push( dts[ d ] );
this.__removeAttr( 'data-vv-' + dts[ d ] );
}
}
// Attributes
var atr = [ 'required', 'autofocus' ];
for ( var a = 0, l = atr.length; a < l; a++ )
{
if ( this.__hasHtml5Attr( atr[ a ] ) )
{
this.validations.push( atr[ a ] );
this.__removeAttr( atr[ a ] );
}
}
// Type-values
var typ = [ 'number', 'email', 'url' ];
for ( var t = 0, l = typ.length; t < l; t++ )
{
if ( this.__hasHtml5Type( typ[ t ] ) )
{
this.validations.push( typ[ t ] );
}
}
// Autotab
if ( this.field.data( 'vv-autotab' ) )
{
this.autotab = true;
this.__removeAttr( 'data-vv-autotab' );
}
}
// Refactor non-HTML5 usage
var classes = this.field.attr( 'class' );
if ( classes && classes.length )
{
// Placeholder
if ( this.field.hasClass( 'placeholder' ) )
{
if ( this.field.is( 'select' ) )
{
var num = 0,
opt = this.field.data( 'vv-placeholder-number' );
if ( opt )
{
num = opt;
this.__removeAttr( 'data-vv-placeholder-number' );
}
else if ( typeof this.form.opts.selectPlaceholder == 'number' )
{
num = this.form.opts.selectPlaceholder;
}
else
{
var $options = this.field.find( 'option' ),
selected = $options.index( $options.filter( '[selected]' ) );
if ( selected > -1 )
{
num = selected;
}
}
this.placeholderNumber = num;
this.originalValue = this.field.find( 'option:eq( ' + num + ' )' ).attr( 'value' ) || '';
}
this.placeholderValue = this.originalValue;
this.originalValue = '';
this.__removeClass( 'placeholder' );
}
// Corresponding
var corsp = 'corresponding:',
start = classes.indexOf( corsp );
if ( start != -1 )
{
var corrcls = classes.substr( start ).split( ' ' )[ 0 ],
corresp = corrcls.substr( corsp.length );
if ( corresp.length )
{
this.corresponding = corresp;
this.validations.push( 'corresponding' );
this.field.removeClass( corrcls );
}
}
// Pattern
// still uses alt-attribute...
if ( this.field.hasClass( 'pattern' ) )
{
this.pattern = this.field.attr( 'alt' ) || '';
this.validations.push( 'pattern' );
this.__removeAttr( 'alt' );
this.__removeClass( 'pattern' );
}
// Groups
var grp = [ 'requiredgroup', 'validationgroup' ];
for ( var g = 0, l = grp.length; g < l; g++ )
{
var group = grp[ g ] + ':',
start = classes.indexOf( group );
if ( start != -1 )
{
var groupclass = classes.substr( start ).split( ' ' )[ 0 ],
groupname = groupclass.substr( group.length );
if ( groupname.length )
{
this[ grp[ g ] ] = groupname;
this.validations.push( grp[ g ]);
this.field.removeClass( groupclass );
}
}
}
// Autotab
if ( this.field.hasClass( 'autotab' ) )
{
this.autotab = true;
this.__removeClass( 'autotab' );
}
}
// Password placeholder
if ( this.placeholderValue !== false && this.field.is( '[type="password"]' ) )
{
this.passwordplaceholder = true;
}
// Add all remaining classes
var classes = this.field.attr( 'class' );
if ( classes && classes.length )
{
this.validations.push( classes );
}
this.validations = unique( this.validations.join( ' ' ).split( ' ' ) );
return this;
},
_bindEvents: function()
{
var that = this;
this.field.bind(
namespace( 'focus' ),
function( event )
{
$(this).addClass( 'focus' );
that.clearPlaceholderValue();
}
);
this.field.bind(
namespace( 'blur' ),
function( event )
{
$(this).removeClass( 'focus' );
that.validate( that.form.opts.validate.onBlur );
}
);
this.field.bind(
namespace( 'keyup' ),
function( event )
{
if ( !preventkeyup( event.keyCode ) )
{
that.validate( that.form.opts.validate.onKeyup, false );
}
}
);
if ( this.field.is( 'select, input[type="checkbox"], input[type="radio"]' ) )
{
this.field.bind(
namespace( 'change' ),
function( event )
{
$(this).trigger( namespace( 'blur' ) );
}
);
}
return this;
},
_bindCustomEvents: function()
{
var that = this;
this.field.bind(
namespace([
'validate',
'isValid',
'destroy',
'addValidation',
'removeValidation'
]),
function()
{
arguments = Array.prototype.slice.call( arguments );
var event = arguments.shift(),
type = event.type;
event.stopPropagation();
if ( typeof that[ type ] != 'function' )
{
$.fn.validVal.debug( 'Public method "' + type + '" not found.' );
return false;
}
return that[ type ].apply( that, arguments );
}
);
this.field.bind(
namespace([ 'validations' ]),
function( event, validations, callCallback )
{
if ( typeof validations == 'undefined' )
{
return this.getValidations();
}
else
{
return this.setValidations( validations, callCallback );
}
}
);
return this;
},
_init: function()
{
var that = this;
// Placeholder
if ( this.placeholderValue !== false )
{
if ( this.field.val() == '' )
{
this.field.val( this.placeholderValue );
}
if ( this.passwordplaceholder )
{
if ( this.field.val() == this.placeholderValue ) try
{
this.field[ 0 ].type = 'text';
}
catch( err ) {};
}
if ( this.field.val() == this.placeholderValue )
{
this.field.addClass( 'inactive' );
}
if ( this.field.is( 'select' ) )
{
this.field.find( 'option:eq(' + this.placeholderNumber + ')' ).addClass( 'inactive' );
this.field.bind(
namespace( 'change' ),
function( event )
{
$(this)[ that.field.val() == that.placeholderValue ? 'addClass' : 'removeClass' ]( 'inactive' );
}
);
}
}
// Corresponding
if ( this.corresponding !== false )
{
$(_INPUTS_).filter('[name="' + this.corresponding + '"]').bind(
namespace( 'blur' ),
function( event )
{
that.validate( that.form.opts.validate.onBlur );
}
).bind(
namespace( 'keyup' ),
function( event )
{
if ( !preventkeyup( event.keyCode ) )
{
that.validate( that.form.opts.validate.onKeyup, false );
}
}
);
}
// Autotabbing
if ( this.autotab )
{
var max = this.field.attr( 'maxlength' ),
tab = this.field.attr( 'tabindex' ),
$next = $(_INPUTS_).filter('[tabindex="' + ( parseInt( tab ) + 1 ) + '"]');
if ( this.field.is( 'select' ) )
{
if ( tab )
{
this.field.bind(
namespace( 'change' ),
function( event )
{
if ( $next.length )
{
$next.focus();
}
}
);
}
}
else
{
if ( max && tab )
{
this.field.bind(
namespace( 'keyup' ),
function( event )
{
if ( $(this).val().length == max )
{
if ( !preventkeyup( event.keyCode ) )
{
$(this).trigger( namespace( 'blur' ) );
if ( $next.length )
{
$next.focus();
}
}
}
}
);
}
}
}
// Autofocus
if ( $.inArray( 'autofocus', this.validations ) != -1 && !this.field.is( ':disabled' ) )
{
this.field.focus();
}
return this;
},
_togglePlaceholderValue: function( toggle )
{
if ( this.placeholderValue !== false )
{
if ( toggle == 'clear' )
{
var v1 = this.placeholderValue,
v2 = '',
cl = 'removeClass',
tp = 'password';
}
else
{
var v1 = '',
v2 = this.placeholderValue,
cl = 'addClass',
tp = 'text';
}
if ( this.field.val() == v1 && !this.field.is( 'select' ) )
{
this.field.val( v2 );
this.field[ cl ]( 'inactive' );
if ( this.passwordplaceholder ) try
{
this.field[ 0 ].type = tp;
}
catch( err ) {};
}
}
return this;
},
// Private methods
__hasHtml5Attr: function( a )
{
// non HTML5 browsers
if ( typeof this.field.attr( a ) == 'undefined' )
{
return false;
}
// HTML5 browsers
if ( this.field.attr( a ) === 'false' || this.field.attr( a ) === false )
{
return false;
}
return true;
},
__hasHtml5Type: function( t )
{
// cool HTML5 browsers
if ( this.field.attr( 'type' ) == t )
{
return true;
}
// non-HTML5 but still cool browsers
if ( this.field.is( 'input[type="' + t + '"]' ) )
{
return true;
}
// non-HTML5, non-cool browser
var $c = $( '<div />' ).append( this.field.clone() ).html()
if ( $c.indexOf( 'type="' + t + '"' ) != -1 || $c.indexOf( 'type=\'' + t + '\'' ) != -1 || $c.indexOf( 'type=' + t + '' ) != -1 )
{
return true;
}
return false;
},
__removeAttr: function( a )
{
if ( $.inArray( a, this.form.opts.keepAttributes ) == -1 )
{
this.field.removeAttr( a );
}
return this;
},
__removeClass: function( c )
{
if ( $.inArray( c, this.form.opts.keepClasses ) == -1 )
{
this.field.removeClass( c );
}
return this;
}
};
$.fn[ _PLUGIN_ ] = function( o, c )
{
return this.each(
function()
{
var $t = $(this);
if ( $t.data( _PLUGIN_ ) )
{
$t.data( _PLUGIN_ ).destroy();
}
$t.data( _PLUGIN_, new ValidVal( $t, o, c ) );
}
);
};
$.fn[ _PLUGIN_ ].version = _VERSION_;
$.fn[ _PLUGIN_ ].defaults = {
'selectPlaceholder' : 0,
'supportHtml5' : true,
'language' : 'en',
'customValidations' : {},
'validate' : {
'onBlur' : true,
'onSubmit' : true,
'onKeyup' : false,
'fields' : {
'hidden' : false,
'disabled' : false,
'filter' : function( $i )
{
return $i;
}
}
},
'fields' : {
'onValidate' : null,
'onValid' : function()
{
$(this).add( $(this).parent() ).removeClass( 'invalid' );
},
'onInvalid' : function()
{
$(this).add( $(this).parent() ).addClass( 'invalid' );
}
},
'form' : {
'onReset' : null,
'onValidate': null,
'onValid' : null,
'onInvalid' : function( fieldArr, language )
{
switch ( language )
{
case 'nl':
msg = 'Let op, niet alle velden zijn correct ingevuld.';
break;
case 'de':
msg = 'Achtung, nicht alle Felder sind korrekt ausgefuellt.';
break;
case 'es':
msg = 'Atención, no se han completado todos los campos correctamente.';
break;
case 'en':
default:
msg = 'Attention, not all fields have been filled out correctly.';
break;
}
alert( msg );
fieldArr.first().focus();
}
},
'keepClasses' : [ 'required' ],
'keepAttributes': [ 'pattern', 'placeholder' ]
};
$.fn[ _PLUGIN_ ].defaultValidations = {
'required': function( v )
{
var $f = $(this);
if ( $f.is( '[type="radio"]' ) || $f.is( '[type="checkbox"]' ) )
{
if ( $f.is( '[type="radio"]' ) )
{
var name = $f.attr( 'name' );
if ( name && name.length > 0 )
{
$f = $( 'input[name="' + name + '"]' );
}
}
if ( !$f.is( ':checked' ) )
{
return false;
}
}
else if ( $f.is( 'select' ) )
{
var vf = $f.data( _FIELD_ );
if ( vf && vf.placeholderValue !== false )
{
if ( $f.val() == vf.placeholderValue )
{
return false;
}
}
else
{
if ( v.length == 0 )
{
return false;
}
}
}
else
{
if ( v.length == 0 )
{
return false;
}
}
return true;
},
'Required': function( v )
{
return $.fn[ _PLUGIN_ ].defaultValidations.required.call( this, v );
},
'requiredgroup': function( v )
{
var $f = $(this),
vf = $f.data( _FIELD_ );
if ( vf && vf.requiredgroup !== false )
{
$f = $();
$(_INPUTS_).each(
function()
{
var tf = $(this).data( _FIELD_ );
if ( tf && tf.requiredgroup == vf.requiredgroup )
{
$f = $f.add( this );
}
}
);
}
var result = false;
$f.each(
function()
{
var f = this;
if ( $.fn[ _PLUGIN_ ].defaultValidations.required.call( f, trim( $(f).val() ) ) )
{
result = true;
}
}
);
return result;
},
'corresponding': function( v )
{
var org = '',
vf = $(this).data( _FIELD_ );
if ( vf && vf.corresponding !== false )
{
var $f = $(_INPUTS_).filter('[name="' + vf.corresponding + '"]'),
vf = $f.data( _FIELD_ );
if ( vf )
{
vf.clearPlaceholderValue();
org = trim( $f.val() );
vf.restorePlaceholderValue();
}
return ( v == org );
}
return false;
},
'number': function( v )
{
v = stripWhitespace( v );
if ( v.length == 0 )
{
return true;
}
if ( isNaN( v ) )
{
return false;
}
return true;
},
'email': function( v )
{
if ( v.length == 0 )
{
return true;
}
var r = /^([a-zA-Z0-9_\.\-+])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
return r.test( v );
},
'url': function( v )
{
if ( v.length == 0 )
{
return true;
}
if ( v.match(/^www\./) )
{
v = "http://" + v;
}
return v.match(/^(http\:\/\/|https\:\/\/)(.{4,})$/);
},
'pattern': function( v )
{
if ( v.length == 0 )
{
return true;
}
var $f = $(this),
vf = $f.data( 'validVal' );
if ( vf )
{
var p = vf.pattern;
if ( p.slice( 0, 1 ) == '/' )
{
p = p.slice( 1 );
}
if ( p.slice( 1 ) == '/' )
{
p = p.slice( 0, -1 );
}
return new RegExp( p ).test( v );
}
}
};
// test for borwser support
$.fn[ _PLUGIN_ ].support = {
touch: (function()
{
return 'ontouchstart' in document.documentElement;
})(),
placeholder: (function()
{
return 'placeholder' in document.createElement( 'input' );
})()
};
$.fn[ _PLUGIN_ ].debug = function( msg ) {};
$.fn[ _PLUGIN_ ].deprecated = function( func, alt )
{
if ( typeof console != 'undefined' )
{
if ( typeof console.error != 'undefined' )
{
console.error( func + ' is DEPRECATED, use ' + alt + ' instead.' );
}
}
};
// Create debugger is it doesn't already excists
if ( !$.fn.validValDebug )
{
$.fn.validValDebug = function( b )
{
$.fn[ _PLUGIN_ ].debug( 'validVal debugger not installed!' );
return this;
}
}
function isHtmlElement( field )
{
if ( typeof HTMLElement != 'undefined' )
{
return field instanceof HTMLElement;
}
return ( field.nodeType && field.nodeType == Node.ELEMENT_NODE );
}
function namespace( events )
{
if ( typeof events == 'string' )
{
events = events.split( ' ' );
}
return events.join( '.vv ' ) + '.vv';
}
function unique( arr )
{
return $.grep(
arr,
function( v, k )
{
return $.inArray( v, arr ) === k;
}
);
}
function trim( str )
{
if ( str === null )
{
return '';
}
if ( typeof str == 'object' )
{
var arr = [];
for ( var a in str )
{
arr[ a ] = trim( str[ a ] );
}
return arr;
}
if ( typeof str != 'string' )
{
return '';
}
if ( str.length == 0 )
{
return '';
}
return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
}
function stripWhitespace( str )
{
if ( str === null )
{
return '';
}
if ( typeof str == 'object' )
{
for ( var a in str )
{
str[ a ] = stripWhitespace( str[ a ] );
}
return str;
}
if ( typeof str != 'string' )
{
return '';
}
if ( str.length == 0 )
{
return '';
}
str = trim( str );
var r = [ ' ', '-', '+', '(', ')', '/', '\\' ];
for ( var i = 0, l = r.length; i < l; i++ )
{
str = str.split( r[ i ] ).join( '' );
}
return str;
}
function preventkeyup( kc )
{
switch( kc ) {
case 9: // tab
case 13: // enter
case 16: // shift
case 17: // control
case 18: // alt
case 37: // left
case 38: // up
case 39: // right
case 40: // down
case 224: // command
return true;
break;
default:
return false;
break;
}
}
})( jQuery );
\ No newline at end of file
/*jslint indent: 2, maxlen: 80, nomen: true, sloppy: true, todo: true */
/*global console, window, document */
(function (window, document) {
"use strict";
// NOTE: old browsers need all of these
var shim_dict = {};
/* ====================================================================== */
/* SHIMS */
/* ====================================================================== */
// NOTE: add more: https://github.com/kriskowal/es5-shim
// NOTE: to support IE8+/Windows Mobile where possible, test via modernizr
// =============================== indexOf ================================
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function(searchElement /*, fromIndex */) {
"use strict";
if (this === void 0 || this === null)
throw new TypeError();
var t = Object(this);
var len = t.length >>> 0;
if (len === 0)
return -1;
var n = 0;
if (arguments.length > 0)
{
n = Number(arguments[1]);
if (n !== n)
n = 0;
else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0))
n = (n > 0 || -1) * Math.floor(Math.abs(n));
}
if (n >= len)
return -1;
var k = n >= 0
? n
: Math.max(len - Math.abs(n), 0);
for (; k < len; k++)
{
if (k in t && t[k] === searchElement)
return k;
}
return -1;
};
}
// =============================== BASE64 encoding ========================
// https://gist.github.com/yahiko/229984
shim_dict.Base64 = {
characters: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" ,
/**
* Shim for atob (not available on IE8+9+Windows Mobile?)
* @method Base64.encode
* @param {string} string String to be hashed
* @return {string} encoded string
*/
encode: function( string ) {
var characters = shim_dict.Base64.characters;
var result = '';
var i = 0;
do {
var a = string.charCodeAt(i++);
var b = string.charCodeAt(i++);
var c = string.charCodeAt(i++);
a = a ? a : 0;
b = b ? b : 0;
c = c ? c : 0;
var b1 = ( a >> 2 ) & 0x3F;
var b2 = ( ( a & 0x3 ) << 4 ) | ( ( b >> 4 ) & 0xF );
var b3 = ( ( b & 0xF ) << 2 ) | ( ( c >> 6 ) & 0x3 );
var b4 = c & 0x3F;
if( ! b ) {
b3 = b4 = 64;
} else if( ! c ) {
b4 = 64;
}
result += characters.charAt( b1 ) + characters.charAt( b2 ) +
characters.charAt( b3 ) + characters.charAt( b4 );
} while ( i < string.length );
return result;
},
/**
* Shim for btoa (not available on IE8+9+Windows Mobile?)
* @method Base64.decode
* @param {string} string String to be hashed
* @return {string} encoded string
*/
decode: function( string ) {
var characters = shim_dict.Base64.characters;
var result = '';
var i = 0;
do {
var b1 = characters.indexOf( string.charAt(i++) );
var b2 = characters.indexOf( string.charAt(i++) );
var b3 = characters.indexOf( string.charAt(i++) );
var b4 = characters.indexOf( string.charAt(i++) );
var a = ( ( b1 & 0x3F ) << 2 ) | ( ( b2 >> 4 ) & 0x3 );
var b = ( ( b2 & 0xF ) << 4 ) | ( ( b3 >> 2 ) & 0xF );
var c = ( ( b3 & 0x3 ) << 6 ) | ( b4 & 0x3F );
result += String.fromCharCode(a) + (b?String.fromCharCode(b):'') + (c?String.fromCharCode(c):'');
} while( i < string.length );
return result;
}
};
window.shim = shim_dict;
}(window, document));
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment