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,9 +725,18 @@ hello.utils.extend( hello.utils, { ...@@ -713,9 +725,18 @@ 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.
// 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]; return args[0];
} }
}}
}
// else loop through and account for the missing ones. // else loop through and account for the missing ones.
for(x in o){if(o.hasOwnProperty(x)){ for(x in o){if(o.hasOwnProperty(x)){
...@@ -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){}
// Update store
utils.store(obj.network,obj);
// Call the globalEvent function on the parent // Call the globalEvent function on the parent
if(cb in win){
try{
win[cb](obj); win[cb](obj);
}
catch(e){
debug("Error thrown whilst executing parent callback", e);
return;
}
}
else{
debug("Error: Callback missing from parent window, snap!");
return;
}
// Update store
utils.store(obj.network,obj);
} }
// Close this current window
try{
window.close(); window.close();
hello.emit("notice",'Trying to close window'); }
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 if(p.path.match("me/files")&&p.method==='post'){ else{
return true; p.data = file;
}
}
}
if(p.method==='delete'){
// 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'){
// 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; return false;
} }
// Convert the POST into a javascript object
p.data = JSON.stringify(p.data);
p.headers = {
'content-type' : 'application/json'
};
}
return true; 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"
} }
}); });
})(hello); }
// 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;
}
}
});
})(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() {
...@@ -103,6 +103,7 @@ ...@@ -103,6 +103,7 @@
detectLngQS: 'setLng', detectLngQS: 'setLng',
ns: 'translation', ns: 'translation',
fallbackOnNull: true, fallbackOnNull: true,
fallbackOnEmpty: false,
fallbackToDefaultNS: false, fallbackToDefaultNS: false,
nsseparator: ':', nsseparator: ':',
keyseparator: '.', keyseparator: '.',
...@@ -141,6 +142,7 @@ ...@@ -141,6 +142,7 @@
cookieName: 'i18next', cookieName: 'i18next',
cookieDomain: undefined, cookieDomain: undefined,
objectTreeKeyHandler: undefined,
postProcess: undefined, postProcess: undefined,
parseMissingKey: undefined, parseMissingKey: undefined,
...@@ -545,7 +547,7 @@ ...@@ -545,7 +547,7 @@
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,
...@@ -692,6 +694,15 @@ ...@@ -692,6 +694,15 @@
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;
} }
...@@ -770,7 +781,10 @@ ...@@ -770,7 +781,10 @@
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);
} }
...@@ -801,10 +815,15 @@ ...@@ -801,10 +815,15 @@
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));
...@@ -922,7 +941,7 @@ ...@@ -922,7 +941,7 @@
} }
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) {
...@@ -939,6 +958,8 @@ ...@@ -939,6 +958,8 @@
} }
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 || '';
...@@ -980,18 +1001,22 @@ ...@@ -980,18 +1001,22 @@
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];
if (potentialKeys.length > 1) {
for (var i = 0; i < potentialKeys.length; i++) { for (var i = 0; i < potentialKeys.length; i++) {
key = potentialKeys[i]; key = potentialKeys[i];
if (exists(key)) { if (exists(key)) {
break; break;
} }
} }
}
var notFound = _getDefaultValue(key, options) var notFound = _getDefaultValue(key, options)
, found = _find(key, options) , found = _find(key, options)
...@@ -1044,7 +1069,7 @@ ...@@ -1044,7 +1069,7 @@
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
...@@ -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,17 +1161,25 @@ ...@@ -1135,17 +1161,25 @@
value = undefined; value = undefined;
} else if (value !== null) { } else if (value !== null) {
if (!o.returnObjectTrees && !options.returnObjectTrees) { if (!o.returnObjectTrees && !options.returnObjectTrees) {
if (o.objectTreeKeyHandler && typeof o.objectTreeKeyHandler == 'function') {
value = o.objectTreeKeyHandler(key, value, l, ns, options);
} else {
value = 'key \'' + ns + ':' + key + ' (' + l + ')\' ' + value = 'key \'' + ns + ':' + key + ' (' + l + ')\' ' +
'returned a object instead of string.'; 'returned an object instead of string.';
f.log(value); f.log(value);
} else if (typeof value !== 'number') { }
var copy = {}; // apply child translation on a copy } 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;
} }
} }
...@@ -1345,7 +1379,15 @@ ...@@ -1345,7 +1379,15 @@
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",
...@@ -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,17 +89,23 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -44,17 +89,23 @@ 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'),
// After page load injecting a fake body doesn't work so check if body exists
body = document.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'); fakeBody = body || document.createElement('body');
if ( parseInt(nodes, 10) ) { if ( parseInt(nodes, 10) ) {
// 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-- ) { 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);
...@@ -62,12 +113,21 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -62,12 +113,21 @@ window.Modernizr = (function( window, document, undefined ) {
} }
} }
// <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(''); style = ['&#173;','<style id="s', mod, '">', rule, '</style>'].join('');
div.id = mod; div.id = mod;
// 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; (body ? div : fakeBody).innerHTML += style;
fakeBody.appendChild(div); fakeBody.appendChild(div);
if ( !body ) { if ( !body ) {
//avoid crashing IE8, if background image is used
fakeBody.style.background = ''; 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'; fakeBody.style.overflow = 'hidden';
docOverflow = docElement.style.overflow; docOverflow = docElement.style.overflow;
docElement.style.overflow = 'hidden'; docElement.style.overflow = 'hidden';
...@@ -75,6 +135,7 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -75,6 +135,7 @@ window.Modernizr = (function( window, document, undefined ) {
} }
ret = callback(div, rule); ret = callback(div, rule);
// If this is done after page load we don't want to remove the body so check if body exists
if ( !body ) { if ( !body ) {
fakeBody.parentNode.removeChild(fakeBody); fakeBody.parentNode.removeChild(fakeBody);
docElement.style.overflow = docOverflow; docElement.style.overflow = docOverflow;
...@@ -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,9 +195,11 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -119,9 +195,11 @@ 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;
// When using `setAttribute`, IE skips "unload", WebKit skips "unload" and "resize", whereas `in` "catches" those
var isSupported = eventName in element; var isSupported = eventName in element;
if ( !isSupported ) { if ( !isSupported ) {
// If it has no `setAttribute` (i.e. doesn't implement Node interface), try generic element
if ( !element.setAttribute ) { if ( !element.setAttribute ) {
element = document.createElement('div'); element = document.createElement('div');
} }
...@@ -129,6 +207,7 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -129,6 +207,7 @@ window.Modernizr = (function( window, document, undefined ) {
element.setAttribute(eventName, ''); element.setAttribute(eventName, '');
isSupported = is(element[eventName], 'function'); isSupported = is(element[eventName], 'function');
// If property was created, "remove it" (by setting value to `undefined`)
if ( !is(element[eventName], 'undefined') ) { if ( !is(element[eventName], 'undefined') ) {
element[eventName] = undefined; element[eventName] = undefined;
} }
...@@ -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) {
// return the property name as a string
if (elem === false) return props[i]; if (elem === false) return props[i];
// let's bind a function
if (is(item, 'function')){ if (is(item, 'function')){
// default to autobind unless override
return item.bind(elem || obj); return item.bind(elem || obj);
} }
// return the unbound function or obj or value
return item; 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(' ');
// did they call .prefixed('boxSizing') or are we just testing a prop?
if(is(prefixed, "string") || is(prefixed, "undefined")) { if(is(prefixed, "string") || is(prefixed, "undefined")) {
return testProps(props, prefixed); return testProps(props, prefixed);
// otherwise, they called .prefixed('requestAnimationFrame', window[, elem])
} else { } 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() {
// 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)'); 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() {
// 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://)'); setCss('background:url(https://),url(https://),red url(https://)');
// If the UA supports multiple backgrounds, there should be three occurrences
// of the string "url(" in the return value for elemStyle.background
return (/(url\s*\(.*?){3}/).test(mStyle.background); return (/(url\s*\(.*?){3}/).test(mStyle.background);
}; tests['backgroundsize'] = function() { };
// 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() {
// 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.
setCssAll('opacity:.55'); 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); 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,12 +617,22 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -381,12 +617,22 @@ 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(
// legacy webkit syntax (FIXME: remove when syntax not in use anymore)
(str1 + '-webkit- '.split(' ').join(str2 + str1) + (str1 + '-webkit- '.split(' ').join(str2 + str1) +
// standard syntax // trailing 'background-image:'
prefixes.join(str3 + str1)).slice(0, -str1.length) prefixes.join(str3 + str1)).slice(0, -str1.length)
); );
...@@ -408,8 +654,14 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -408,8 +654,14 @@ window.Modernizr = (function( window, document, undefined ) {
var ret = !!testPropsAll('perspective'); var ret = !!testPropsAll('perspective');
// 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 ) { if ( ret && 'webkitPerspective' in docElement.style ) {
// 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 ) { 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,15 +707,34 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -447,15 +707,34 @@ 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;
// IE9 Running on Windows Server SKU can cause an exception to be thrown, bug #224
try { 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$/,'');
// 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.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,6 +755,9 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -476,6 +755,9 @@ 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$/,'');
// 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.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,35 +816,68 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -517,35 +816,68 @@ 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() {
/*>>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 ) { 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){
// safari false positive's on datalist: webk.it/74252
// see also github.com/Modernizr/Modernizr/issues/146
attrs.list = !!(document.createElement('datalist') && window.HTMLDataListElement); 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(' '));
/*>>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) { 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++ ) {
...@@ -553,6 +885,9 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -553,6 +885,9 @@ window.Modernizr = (function( window, document, undefined ) {
inputElem.setAttribute('type', inputElemType = props[i]); inputElem.setAttribute('type', inputElemType = props[i]);
bool = inputElem.type !== 'text'; bool = inputElem.type !== 'text';
// 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 ) { if ( bool ) {
inputElem.value = smile; inputElem.value = smile;
...@@ -563,17 +898,28 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -563,17 +898,28 @@ window.Modernizr = (function( window, document, undefined ) {
docElement.appendChild(inputElem); docElement.appendChild(inputElem);
defaultView = document.defaultView; defaultView = document.defaultView;
// Safari 2-4 allows the smiley as a value, despite making a slider
bool = defaultView.getComputedStyle && bool = defaultView.getComputedStyle &&
defaultView.getComputedStyle(inputElem, null).WebkitAppearance !== 'textfield' && defaultView.getComputedStyle(inputElem, null).WebkitAppearance !== 'textfield' &&
// Mobile android web browser has false positive, so must
// check the height to see if the widget is actually there.
(inputElem.offsetHeight !== 0); (inputElem.offsetHeight !== 0);
docElement.removeChild(inputElem); docElement.removeChild(inputElem);
} else if ( /^(search|tel)$/.test(inputElemType) ){ } else if ( /^(search|tel)$/.test(inputElemType) ){
// Spec doesn't define any special parsing or detectable UI
// 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) ) { } else if ( /^(url|email)$/.test(inputElemType) ) {
// Real url and email support comes with prebaked validation.
bool = inputElem.checkValidity && inputElem.checkValidity() === false; bool = inputElem.checkValidity && inputElem.checkValidity() === false;
} else { } else {
// If the upgraded input compontent rejects the :) text, we got a winner
bool = inputElem.value != smile; bool = inputElem.value != smile;
} }
} }
...@@ -582,9 +928,23 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -582,9 +928,23 @@ 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) ) {
// 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(); featureName = feature.toLowerCase();
Modernizr[featureName] = tests[feature](); Modernizr[featureName] = tests[feature]();
...@@ -592,9 +952,20 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -592,9 +952,20 @@ window.Modernizr = (function( window, document, undefined ) {
} }
} }
/*>>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,6 +978,11 @@ window.Modernizr = (function( window, document, undefined ) { ...@@ -607,6 +978,11 @@ window.Modernizr = (function( window, document, undefined ) {
feature = feature.toLowerCase(); feature = feature.toLowerCase();
if ( Modernizr[feature] !== undefined ) { if ( Modernizr[feature] !== undefined ) {
// 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; return Modernizr;
} }
...@@ -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 {
// Testing DOM property e.g. Modernizr.prefixed('requestAnimationFrame', window) // 'mozRequestAnimationFrame'
return testPropsAll(prop, obj, elem); 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