diff --git a/app/app/components/auth-settings.js b/app/app/components/auth-settings.js
index 3afff469..070bac5b 100644
--- a/app/app/components/auth-settings.js
+++ b/app/app/components/auth-settings.js
@@ -23,10 +23,12 @@ export default Ember.Component.extend({
KeycloakUrlError: computed.empty('keycloakConfig.url'),
KeycloakRealmError: computed.empty('keycloakConfig.realm'),
KeycloakClientIdError: computed.empty('keycloakConfig.clientId'),
+ KeycloakPublicKeyError: computed.empty('keycloakConfig.publicKey'),
keycloakConfig: {
url: '',
realm: '',
- clientId: ''
+ clientId: '',
+ publicKey: '',
},
didReceiveAttrs() {
@@ -81,6 +83,20 @@ export default Ember.Component.extend({
this.$("#keycloak-clientId").focus();
return;
}
+ if (this.get('KeycloakPublicKeyError')) {
+ this.$("#keycloak-publicKey").focus();
+ return;
+ }
+
+ let pk = this.get('keycloakConfig.publicKey');
+ if (is.not.startWith(pk, '-----BEGIN PUBLIC KEY-----')) {
+ pk = '-----BEGIN PUBLIC KEY-----' + pk;
+ }
+ if (is.not.endWith(pk, '-----END PUBLIC KEY-----')) {
+ pk = pk + '-----END PUBLIC KEY-----' ;
+ }
+
+ this.set('keycloakConfig.publicKey', pk);
config = this.get('keycloakConfig');
break;
diff --git a/app/app/pods/auth/keycloak/route.js b/app/app/pods/auth/keycloak/route.js
index c87a6b19..ef72e79d 100644
--- a/app/app/pods/auth/keycloak/route.js
+++ b/app/app/pods/auth/keycloak/route.js
@@ -44,6 +44,8 @@ export default Ember.Route.extend({
this.get('kcAuth').fetchProfile(kc).then((profile) => {
let data = this.get('kcAuth').mapProfile(kc, profile);
+ console.log(kc);
+ console.log(profile);
console.log(data);
// this.get("session").authenticate('authenticator:keycloak', data)
diff --git a/app/app/pods/auth/keycloak/template.hbs b/app/app/pods/auth/keycloak/template.hbs
index 07400c1c..9f545853 100644
--- a/app/app/pods/auth/keycloak/template.hbs
+++ b/app/app/pods/auth/keycloak/template.hbs
@@ -1,4 +1,4 @@
-
Keycloak authentication...
+
Authenticating with Keycloak...
diff --git a/app/app/services/kc-auth.js b/app/app/services/kc-auth.js
index 962dbd8d..5f2a888a 100644
--- a/app/app/services/kc-auth.js
+++ b/app/app/services/kc-auth.js
@@ -69,6 +69,7 @@ export default Ember.Service.extend({
username: profile.username,
firstname: profile.firstName,
lastname: profile.lastName,
+ remoteId: profile.id
};
}
});
diff --git a/app/app/templates/components/auth-settings.hbs b/app/app/templates/components/auth-settings.hbs
index 3f65bb93..6c46eb4c 100644
--- a/app/app/templates/components/auth-settings.hbs
+++ b/app/app/templates/components/auth-settings.hbs
@@ -11,6 +11,10 @@
{{#if isKeycloakProvider}}
+
+
{{/if}}
save
diff --git a/app/vendor/is.js b/app/vendor/is.js
index 504517c5..6e106821 100644
--- a/app/vendor/is.js
+++ b/app/vendor/is.js
@@ -1,36 +1,35 @@
-// is.js 0.7.4
-// Author: Aras Atasaygin
+/*!
+ * is.js 0.9.0
+ * Author: Aras Atasaygin
+ */
// AMD with global, Node, or global
-;(function(root, factory) {
- if(typeof define === 'function' && define.amd) {
+;(function(root, factory) { // eslint-disable-line no-extra-semi
+ if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
- define(['is'], function(is) {
+ define(function() {
// Also create a global in case some scripts
// that are loaded still are looking for
// a global even when an AMD loader is in use.
- return (root.is = factory(is));
+ return (root.is = factory());
});
- } else if(typeof exports === 'object') {
+ } else if (typeof exports === 'object') {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like enviroments that support module.exports,
// like Node.
- module.exports = factory(require('is_js'));
+ module.exports = factory();
} else {
- // Browser globals (root is window)
- root.is = factory(root.is);
+ // Browser globals (root is self)
+ root.is = factory();
}
-} (this, function(is) {
+}(this, function() {
// Baseline
/* -------------------------------------------------------------------------- */
- var root = this || global;
- var previousIs = root.is;
-
// define 'is' object and current version
- is = {};
- is.VERSION = '0.7.4';
+ var is = {};
+ is.VERSION = '0.9.0';
// define interfaces
is.not = {};
@@ -39,27 +38,23 @@
// cache some methods to call later on
var toString = Object.prototype.toString;
- var arraySlice = Array.prototype.slice;
+ var slice = Array.prototype.slice;
var hasOwnProperty = Object.prototype.hasOwnProperty;
// helper function which reverses the sense of predicate result
function not(func) {
return function() {
- return !func.apply(null, arraySlice.call(arguments));
+ return !func.apply(null, slice.call(arguments));
};
}
// helper function which call predicate function per parameter and return true if all pass
function all(func) {
return function() {
- var parameters = arraySlice.call(arguments);
- var length = parameters.length;
- if(length === 1 && is.array(parameters[0])) { // support array
- parameters = parameters[0];
- length = parameters.length;
- }
+ var params = getParams(arguments);
+ var length = params.length;
for (var i = 0; i < length; i++) {
- if (!func.call(null, parameters[i])) {
+ if (!func.call(null, params[i])) {
return false;
}
}
@@ -70,14 +65,10 @@
// helper function which call predicate function per parameter and return true if any pass
function any(func) {
return function() {
- var parameters = arraySlice.call(arguments);
- var length = parameters.length;
- if(length === 1 && is.array(parameters[0])) { // support array
- parameters = parameters[0];
- length = parameters.length;
- }
+ var params = getParams(arguments);
+ var length = params.length;
for (var i = 0; i < length; i++) {
- if (func.call(null, parameters[i])) {
+ if (func.call(null, params[i])) {
return true;
}
}
@@ -85,12 +76,39 @@
};
}
+ // build a 'comparator' object for various comparison checks
+ var comparator = {
+ '<': function(a, b) { return a < b; },
+ '<=': function(a, b) { return a <= b; },
+ '>': function(a, b) { return a > b; },
+ '>=': function(a, b) { return a >= b; }
+ };
+
+ // helper function which compares a version to a range
+ function compareVersion(version, range) {
+ var string = (range + '');
+ var n = +(string.match(/\d+/) || NaN);
+ var op = string.match(/^[<>]=?|/)[0];
+ return comparator[op] ? comparator[op](version, n) : (version == n || n !== n);
+ }
+
+ // helper function which extracts params from arguments
+ function getParams(args) {
+ var params = slice.call(args);
+ var length = params.length;
+ if (length === 1 && is.array(params[0])) { // support array
+ params = params[0];
+ }
+ return params;
+ }
+
// Type checks
/* -------------------------------------------------------------------------- */
// is a given value Arguments?
is.arguments = function(value) { // fallback check is for IE
- return is.not.null(value) && (toString.call(value) === '[object Arguments]' || (typeof value === 'object' && 'callee' in value));
+ return toString.call(value) === '[object Arguments]' ||
+ (value != null && typeof value === 'object' && 'callee' in value);
};
// is a given value Array?
@@ -103,28 +121,43 @@
return value === true || value === false || toString.call(value) === '[object Boolean]';
};
+ // is a given value Char?
+ is.char = function(value) {
+ return is.string(value) && value.length === 1;
+ };
+
// is a given value Date Object?
is.date = function(value) {
return toString.call(value) === '[object Date]';
};
+ // is a given object a DOM node?
+ is.domNode = function(object) {
+ return is.object(object) && object.nodeType > 0;
+ };
+
// is a given value Error object?
is.error = function(value) {
return toString.call(value) === '[object Error]';
};
// is a given value function?
- is.function = function(value) { // fallback check is for IE
+ is['function'] = function(value) { // fallback check is for IE
return toString.call(value) === '[object Function]' || typeof value === 'function';
};
+ // is given value a pure JSON object?
+ is.json = function(value) {
+ return toString.call(value) === '[object Object]';
+ };
+
// is a given value NaN?
is.nan = function(value) { // NaN is number :) Also it is the only value which does not equal itself
return value !== value;
};
// is a given value null?
- is.null = function(value) {
+ is['null'] = function(value) {
return value === null;
};
@@ -135,13 +168,7 @@
// is a given value object?
is.object = function(value) {
- var type = typeof value;
- return type === 'function' || type === 'object' && !!value;
- };
-
- // is given value a pure JSON object?
- is.json = function(value) {
- return toString.call(value) === '[object Object]';
+ return Object(value) === value;
};
// is a given value RegExp?
@@ -151,11 +178,15 @@
// are given values same type?
// prevent NaN, Number same type check
- is.sameType = function(value1, value2) {
- if(is.nan(value1) || is.nan(value2)) {
- return is.nan(value1) === is.nan(value2);
+ is.sameType = function(value, other) {
+ var tag = toString.call(value);
+ if (tag !== toString.call(other)) {
+ return false;
}
- return toString.call(value1) === toString.call(value2);
+ if (tag === '[object Number]') {
+ return !is.any.nan(value, other) || is.all.nan(value, other);
+ }
+ return true;
};
// sameType method does not support 'all' and 'any' interfaces
is.sameType.api = ['not'];
@@ -165,72 +196,74 @@
return toString.call(value) === '[object String]';
};
- // is a given value Char?
- is.char = function(value) {
- return is.string(value) && value.length === 1;
- };
-
// is a given value undefined?
is.undefined = function(value) {
return value === void 0;
};
+ // is a given value window?
+ // setInterval method is only available for window object
+ is.windowObject = function(value) {
+ return value != null && typeof value === 'object' && 'setInterval' in value;
+ };
+
// Presence checks
/* -------------------------------------------------------------------------- */
//is a given value empty? Objects, arrays, strings
is.empty = function(value) {
- if(is.object(value)){
- var num = Object.getOwnPropertyNames(value).length;
- if(num === 0 || (num === 1 && is.array(value)) || (num === 2 && is.arguments(value))){
+ if (is.object(value)) {
+ var length = Object.getOwnPropertyNames(value).length;
+ if (length === 0 || (length === 1 && is.array(value)) ||
+ (length === 2 && is.arguments(value))) {
return true;
}
return false;
- } else {
- return value === '';
}
+ return value === '';
};
// is a given value existy?
is.existy = function(value) {
- return value !== null && value !== undefined;
- };
-
- // is a given value truthy?
- is.truthy = function(value) {
- return is.existy(value) && value !== false && is.not.nan(value) && value !== "" && value !== 0;
+ return value != null;
};
// is a given value falsy?
- is.falsy = not(is.truthy);
-
- // is a given value space?
- // horizantal tab: 9, line feed: 10, vertical tab: 11, form feed: 12, carriage return: 13, space: 32
- is.space = function(value) {
- if(is.char(value)) {
- var characterCode = value.charCodeAt(0);
- return (characterCode > 8 && characterCode < 14) || characterCode === 32;
- } else {
- return false;
- }
+ is.falsy = function(value) {
+ return !value;
};
+ // is a given value truthy?
+ is.truthy = not(is.falsy);
+
// Arithmetic checks
/* -------------------------------------------------------------------------- */
- // are given values equal? supports numbers, strings, regexps, booleans
+ // is a given number above minimum parameter?
+ is.above = function(n, min) {
+ return is.all.number(n, min) && n > min;
+ };
+ // above method does not support 'all' and 'any' interfaces
+ is.above.api = ['not'];
+
+ // is a given number decimal?
+ is.decimal = function(n) {
+ return is.number(n) && n % 1 !== 0;
+ };
+
+ // are given values equal? supports numbers, strings, regexes, booleans
// TODO: Add object and array support
- is.equal = function(value1, value2) {
+ is.equal = function(value, other) {
// check 0 and -0 equity with Infinity and -Infinity
- if(is.all.number(value1, value2)) {
- return value1 === value2 && 1 / value1 === 1 / value2;
+ if (is.all.number(value, other)) {
+ return value === other && 1 / value === 1 / other;
}
- // check regexps as strings too
- if(is.all.string(value1, value2) || is.all.regexp(value1, value2)) {
- return '' + value1 === '' + value2;
+ // check regexes as strings too
+ if (is.all.string(value, other) || is.all.regexp(value, other)) {
+ return '' + value === '' + other;
}
- if(is.all.boolean(value1, value2)) {
- return value1 === value2;
+ if (is.all.boolean(value, other)) {
+ return value === other;
}
return false;
};
@@ -238,157 +271,181 @@
is.equal.api = ['not'];
// is a given number even?
- is.even = function(numb) {
- return is.number(numb) && numb % 2 === 0;
+ is.even = function(n) {
+ return is.number(n) && n % 2 === 0;
};
- // is a given number odd?
- is.odd = function(numb) {
- return is.number(numb) && numb % 2 !== 0;
+ // is a given number finite?
+ is.finite = isFinite || function(n) {
+ return is.not.infinite(n) && is.not.nan(n);
};
- // is a given number positive?
- is.positive = function(numb) {
- return is.number(numb) && numb > 0;
+ // is a given number infinite?
+ is.infinite = function(n) {
+ return n === Infinity || n === -Infinity;
+ };
+
+ // is a given number integer?
+ is.integer = function(n) {
+ return is.number(n) && n % 1 === 0;
};
// is a given number negative?
- is.negative = function(numb) {
- return is.number(numb) && numb < 0;
+ is.negative = function(n) {
+ return is.number(n) && n < 0;
};
- // is a given number above minimum parameter?
- is.above = function(numb, min) {
- return is.all.number(numb, min) && numb > min;
+ // is a given number odd?
+ is.odd = function(n) {
+ return is.number(n) && n % 2 === 1;
+ };
+
+ // is a given number positive?
+ is.positive = function(n) {
+ return is.number(n) && n > 0;
};
- // above method does not support 'all' and 'any' interfaces
- is.above.api = ['not'];
// is a given number above maximum parameter?
- is.under = function(numb, max) {
- return is.all.number(numb, max) && numb < max;
+ is.under = function(n, max) {
+ return is.all.number(n, max) && n < max;
};
// least method does not support 'all' and 'any' interfaces
is.under.api = ['not'];
// is a given number within minimum and maximum parameters?
- is.within = function(numb, min, max) {
- return is.all.number(numb, min, max) && numb > min && numb < max;
+ is.within = function(n, min, max) {
+ return is.all.number(n, min, max) && n > min && n < max;
};
// within method does not support 'all' and 'any' interfaces
is.within.api = ['not'];
- // is a given number decimal?
- is.decimal = function(numb) {
- return is.number(numb) && numb % 1 !== 0;
- };
-
- // is a given number integer?
- is.integer = function(numb) {
- return is.number(numb) && numb % 1 === 0;
- };
-
- // is a given number finite?
- is.finite = isFinite || function(numb) {
- return numb !== Infinity && numb !== -Infinity && is.not.nan(numb);
- };
-
- // is a given number infinite?
- is.infinite = not(is.finite);
-
// Regexp checks
/* -------------------------------------------------------------------------- */
// Steven Levithan, Jan Goyvaerts: Regular Expressions Cookbook
// Scott Gonzalez: Email address validation
+ // dateString match m/d/yy and mm/dd/yyyy, allowing any combination of one or two digits for the day and month, and two or four digits for the year
// eppPhone match extensible provisioning protocol format
// nanpPhone match north american number plan format
- // dateString match m/d/yy and mm/dd/yyyy, allowing any combination of one or two digits for the day and month, and two or four digits for the year
// time match hours, minutes, and seconds, 24-hour clock
- var regexps = {
- url: /^(?:(?:https?|ftp):\/\/)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/\S*)?$/i,
- email: /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i,
- creditCard: /^(?:(4[0-9]{12}(?:[0-9]{3})?)|(5[1-5][0-9]{14})|(6(?:011|5[0-9]{2})[0-9]{12})|(3[47][0-9]{13})|(3(?:0[0-5]|[68][0-9])[0-9]{11})|((?:2131|1800|35[0-9]{3})[0-9]{11}))$/,
- alphaNumeric: /^[A-Za-z0-9]+$/,
- timeString: /^(2[0-3]|[01]?[0-9]):([0-5]?[0-9]):([0-5]?[0-9])$/,
- dateString: /^(1[0-2]|0?[1-9])\/(3[01]|[12][0-9]|0?[1-9])\/(?:[0-9]{2})?[0-9]{2}$/,
- usZipCode: /^[0-9]{5}(?:-[0-9]{4})?$/,
- caPostalCode: /^(?!.*[DFIOQU])[A-VXY][0-9][A-Z]\s?[0-9][A-Z][0-9]$/,
- ukPostCode: /^[A-Z]{1,2}[0-9RCHNQ][0-9A-Z]?\s?[0-9][ABD-HJLNP-UW-Z]{2}$|^[A-Z]{2}-?[0-9]{4}$/,
- nanpPhone: /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/,
- eppPhone: /^\+[0-9]{1,3}\.[0-9]{4,14}(?:x.+)?$/,
- socialSecurityNumber: /^(?!000|666)[0-8][0-9]{2}-(?!00)[0-9]{2}-(?!0000)[0-9]{4}$/,
+ var regexes = {
affirmative: /^(?:1|t(?:rue)?|y(?:es)?|ok(?:ay)?)$/,
- hexadecimal: /^[0-9a-fA-F]+$/,
+ alphaNumeric: /^[A-Za-z0-9]+$/,
+ caPostalCode: /^(?!.*[DFIOQU])[A-VXY][0-9][A-Z]\s?[0-9][A-Z][0-9]$/,
+ creditCard: /^(?:(4[0-9]{12}(?:[0-9]{3})?)|(5[1-5][0-9]{14})|(6(?:011|5[0-9]{2})[0-9]{12})|(3[47][0-9]{13})|(3(?:0[0-5]|[68][0-9])[0-9]{11})|((?:2131|1800|35[0-9]{3})[0-9]{11}))$/,
+ dateString: /^(1[0-2]|0?[1-9])([\/-])(3[01]|[12][0-9]|0?[1-9])(?:\2)(?:[0-9]{2})?[0-9]{2}$/,
+ email: /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i, // eslint-disable-line no-control-regex
+ eppPhone: /^\+[0-9]{1,3}\.[0-9]{4,14}(?:x.+)?$/,
+ hexadecimal: /^(?:0x)?[0-9a-fA-F]+$/,
hexColor: /^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/,
ipv4: /^(?:(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.){3}(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/,
- ipv6: /^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$|^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/,
- ip: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$|^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/
+ ipv6: /^((?=.*::)(?!.*::.+::)(::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\3)::|:\b|$))|(?!\2\3)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})$/i,
+ nanpPhone: /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/,
+ socialSecurityNumber: /^(?!000|666)[0-8][0-9]{2}-?(?!00)[0-9]{2}-?(?!0000)[0-9]{4}$/,
+ timeString: /^(2[0-3]|[01]?[0-9]):([0-5]?[0-9]):([0-5]?[0-9])$/,
+ ukPostCode: /^[A-Z]{1,2}[0-9RCHNQ][0-9A-Z]?\s?[0-9][ABD-HJLNP-UW-Z]{2}$|^[A-Z]{2}-?[0-9]{4}$/,
+ url: /^(?:(?:https?|ftp):\/\/)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/\S*)?$/i,
+ usZipCode: /^[0-9]{5}(?:-[0-9]{4})?$/
};
- // create regexp checks methods from 'regexp' object
- for(var regexp in regexps) {
- if(regexps.hasOwnProperty(regexp)) {
- regexpCheck(regexp, regexps);
+ function regexpCheck(regexp, regexes) {
+ is[regexp] = function(value) {
+ return is.existy(value) && regexes[regexp].test(value);
+ };
+ }
+
+ // create regexp checks methods from 'regexes' object
+ for (var regexp in regexes) {
+ if (regexes.hasOwnProperty(regexp)) {
+ regexpCheck(regexp, regexes);
}
}
- function regexpCheck(regexp, regexps) {
- is[regexp] = function(value) {
- return regexps[regexp].test(value);
- };
- }
+ // simplify IP checks by calling the regex helpers for IPv4 and IPv6
+ is.ip = function(value) {
+ return is.ipv4(value) || is.ipv6(value);
+ };
// String checks
/* -------------------------------------------------------------------------- */
- // is a given string include parameter substring?
- is.include = function(str, substr) {
- return str.indexOf(substr) > -1;
- };
- // include method does not support 'all' and 'any' interfaces
- is.include.api = ['not'];
-
- // is a given string all uppercase?
- is.upperCase = function(str) {
- return is.string(str) && str === str.toUpperCase();
+ // is a given string or sentence capitalized?
+ is.capitalized = function(string) {
+ if (is.not.string(string)) {
+ return false;
+ }
+ var words = string.split(' ');
+ for (var i = 0; i < words.length; i++) {
+ var word = words[i];
+ if (word.length) {
+ var chr = word.charAt(0);
+ if (chr !== chr.toUpperCase()) {
+ return false;
+ }
+ }
+ }
+ return true;
};
- // is a given string all lowercase?
- is.lowerCase = function(str) {
- return is.string(str) && str === str.toLowerCase();
- };
-
- // is string start with a given startWith parameter?
- is.startWith = function(str, startWith) {
- return is.string(str) && str.indexOf(startWith) === 0;
- };
- // startWith method does not support 'all' and 'any' interfaces
- is.startWith.api = ['not'];
-
- // is string end with a given endWith parameter?
- is.endWith = function(str, endWith) {
- return is.string(str) && str.indexOf(endWith) > -1 && str.indexOf(endWith) === str.length - endWith.length;
+ // is string end with a given target parameter?
+ is.endWith = function(string, target) {
+ if (is.not.string(string)) {
+ return false;
+ }
+ target += '';
+ var position = string.length - target.length;
+ return position >= 0 && string.indexOf(target, position) === position;
};
// endWith method does not support 'all' and 'any' interfaces
is.endWith.api = ['not'];
- // is a given string or sentence capitalized?
- is.capitalized = function(str) {
- if(is.not.string(str)) {
- return false;
- }
- var words = str.split(' ');
- var capitalized = [];
- for(var i = 0; i < words.length; i++) {
- capitalized.push(words[i][0] === words[i][0].toUpperCase());
- }
- return is.all.truthy.apply(null, capitalized);
+ // is a given string include parameter target?
+ is.include = function(string, target) {
+ return string.indexOf(target) > -1;
+ };
+ // include method does not support 'all' and 'any' interfaces
+ is.include.api = ['not'];
+
+ // is a given string all lowercase?
+ is.lowerCase = function(string) {
+ return is.string(string) && string === string.toLowerCase();
};
// is a given string palindrome?
- is.palindrome = function(str) {
- return is.string(str) && str == str.split('').reverse().join('');
+ is.palindrome = function(string) {
+ if (is.not.string(string)) {
+ return false;
+ }
+ string = string.replace(/[^a-zA-Z0-9]+/g, '').toLowerCase();
+ var length = string.length - 1;
+ for (var i = 0, half = Math.floor(length / 2); i <= half; i++) {
+ if (string.charAt(i) !== string.charAt(length - i)) {
+ return false;
+ }
+ }
+ return true;
+ };
+
+ // is a given value space?
+ // horizontal tab: 9, line feed: 10, vertical tab: 11, form feed: 12, carriage return: 13, space: 32
+ is.space = function(value) {
+ if (is.not.char(value)) {
+ return false;
+ }
+ var charCode = value.charCodeAt(0);
+ return (charCode > 8 && charCode < 14) || charCode === 32;
+ };
+
+ // is string start with a given target parameter?
+ is.startWith = function(string, target) {
+ return is.string(string) && string.indexOf(target) === 0;
+ };
+ // startWith method does not support 'all' and 'any' interfaces
+ is.startWith.api = ['not'];
+
+ // is a given string all uppercase?
+ is.upperCase = function(string) {
+ return is.string(string) && string === string.toUpperCase();
};
// Time checks
@@ -397,366 +454,388 @@
var days = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
var months = ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december'];
- // is a given date indicate today?
- is.today = function(obj) {
- var now = new Date();
- var todayString = now.toDateString();
- return is.date(obj) && obj.toDateString() === todayString;
- };
-
- // is a given date indicate yesterday?
- is.yesterday = function(obj) {
- var now = new Date();
- var yesterdayString = new Date(now.setDate(now.getDate() - 1)).toDateString();
- return is.date(obj) && obj.toDateString() === yesterdayString;
- };
-
- // is a given date indicate tomorrow?
- is.tomorrow = function(obj) {
- var now = new Date();
- var tomorrowString = new Date(now.setDate(now.getDate() + 1)).toDateString();
- return is.date(obj) && obj.toDateString() === tomorrowString;
- };
-
- // is a given date past?
- is.past = function(obj) {
- var now = new Date();
- return is.date(obj) && obj.getTime() < now.getTime();
- };
-
- // is a given date future?
- is.future = not(is.past);
-
- // is a given dates day equal given dayString parameter?
- is.day = function(obj, dayString) {
- return is.date(obj) && dayString.toLowerCase() === days[obj.getDay()];
+ // is a given dates day equal given day parameter?
+ is.day = function(date, day) {
+ return is.date(date) && day.toLowerCase() === days[date.getDay()];
};
// day method does not support 'all' and 'any' interfaces
is.day.api = ['not'];
- // is a given dates month equal given monthString parameter?
- is.month = function(obj, monthString) {
- return is.date(obj) && monthString.toLowerCase() === months[obj.getMonth()];
+ // is a given date in daylight saving time?
+ is.dayLightSavingTime = function(date) {
+ var january = new Date(date.getFullYear(), 0, 1);
+ var july = new Date(date.getFullYear(), 6, 1);
+ var stdTimezoneOffset = Math.max(january.getTimezoneOffset(), july.getTimezoneOffset());
+ return date.getTimezoneOffset() < stdTimezoneOffset;
};
- // month method does not support 'all' and 'any' interfaces
- is.month.api = ['not'];
- // is a given dates year equal given year parameter?
- is.year = function(obj, year) {
- return is.date(obj) && is.number(year) && year === obj.getFullYear();
+ // is a given date future?
+ is.future = function(date) {
+ var now = new Date();
+ return is.date(date) && date.getTime() > now.getTime();
+ };
+
+ // is date within given range?
+ is.inDateRange = function(date, start, end) {
+ if (is.not.date(date) || is.not.date(start) || is.not.date(end)) {
+ return false;
+ }
+ var stamp = date.getTime();
+ return stamp > start.getTime() && stamp < end.getTime();
+ };
+ // inDateRange method does not support 'all' and 'any' interfaces
+ is.inDateRange.api = ['not'];
+
+ // is a given date in last month range?
+ is.inLastMonth = function(date) {
+ return is.inDateRange(date, new Date(new Date().setMonth(new Date().getMonth() - 1)), new Date());
+ };
+
+ // is a given date in last week range?
+ is.inLastWeek = function(date) {
+ return is.inDateRange(date, new Date(new Date().setDate(new Date().getDate() - 7)), new Date());
+ };
+
+ // is a given date in last year range?
+ is.inLastYear = function(date) {
+ return is.inDateRange(date, new Date(new Date().setFullYear(new Date().getFullYear() - 1)), new Date());
+ };
+
+ // is a given date in next month range?
+ is.inNextMonth = function(date) {
+ return is.inDateRange(date, new Date(), new Date(new Date().setMonth(new Date().getMonth() + 1)));
+ };
+
+ // is a given date in next week range?
+ is.inNextWeek = function(date) {
+ return is.inDateRange(date, new Date(), new Date(new Date().setDate(new Date().getDate() + 7)));
+ };
+
+ // is a given date in next year range?
+ is.inNextYear = function(date) {
+ return is.inDateRange(date, new Date(), new Date(new Date().setFullYear(new Date().getFullYear() + 1)));
};
- // year method does not support 'all' and 'any' interfaces
- is.year.api = ['not'];
// is the given year a leap year?
is.leapYear = function(year) {
return is.number(year) && ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0);
};
+ // is a given dates month equal given month parameter?
+ is.month = function(date, month) {
+ return is.date(date) && month.toLowerCase() === months[date.getMonth()];
+ };
+ // month method does not support 'all' and 'any' interfaces
+ is.month.api = ['not'];
+
+ // is a given date past?
+ is.past = function(date) {
+ var now = new Date();
+ return is.date(date) && date.getTime() < now.getTime();
+ };
+
+ // is a given date in the parameter quarter?
+ is.quarterOfYear = function(date, quarter) {
+ return is.date(date) && is.number(quarter) && quarter === Math.floor((date.getMonth() + 3) / 3);
+ };
+ // quarterOfYear method does not support 'all' and 'any' interfaces
+ is.quarterOfYear.api = ['not'];
+
+ // is a given date indicate today?
+ is.today = function(date) {
+ var now = new Date();
+ var todayString = now.toDateString();
+ return is.date(date) && date.toDateString() === todayString;
+ };
+
+ // is a given date indicate tomorrow?
+ is.tomorrow = function(date) {
+ var now = new Date();
+ var tomorrowString = new Date(now.setDate(now.getDate() + 1)).toDateString();
+ return is.date(date) && date.toDateString() === tomorrowString;
+ };
+
// is a given date weekend?
// 6: Saturday, 0: Sunday
- is.weekend = function(obj) {
- return is.date(obj) && (obj.getDay() === 6 || obj.getDay() === 0);
+ is.weekend = function(date) {
+ return is.date(date) && (date.getDay() === 6 || date.getDay() === 0);
};
// is a given date weekday?
is.weekday = not(is.weekend);
- // is date within given range?
- is.inDateRange = function(obj, startObj, endObj) {
- if(is.not.date(obj) || is.not.date(startObj) || is.not.date(endObj)) {
- return false;
- }
- var givenDate = obj.getTime();
- var start = startObj.getTime();
- var end = endObj.getTime();
- return givenDate > start && givenDate < end;
+ // is a given dates year equal given year parameter?
+ is.year = function(date, year) {
+ return is.date(date) && is.number(year) && year === date.getFullYear();
};
- // inDateRange method does not support 'all' and 'any' interfaces
- is.inDateRange.api = ['not'];
+ // year method does not support 'all' and 'any' interfaces
+ is.year.api = ['not'];
- // is a given date in last week range?
- is.inLastWeek = function(obj) {
- return is.inDateRange(obj, new Date(new Date().setDate(new Date().getDate() - 7)), new Date());
- };
-
- // is a given date in last month range?
- is.inLastMonth = function(obj) {
- return is.inDateRange(obj, new Date(new Date().setMonth(new Date().getMonth() - 1)), new Date());
- };
-
- // is a given date in last year range?
- is.inLastYear = function(obj) {
- return is.inDateRange(obj, new Date(new Date().setFullYear(new Date().getFullYear() - 1)), new Date());
- };
-
- // is a given date in next week range?
- is.inNextWeek = function(obj) {
- return is.inDateRange(obj, new Date(), new Date(new Date().setDate(new Date().getDate() + 7)));
- };
-
- // is a given date in next month range?
- is.inNextMonth = function(obj) {
- return is.inDateRange(obj, new Date(), new Date(new Date().setMonth(new Date().getMonth() + 1)));
- };
-
- // is a given date in next year range?
- is.inNextYear = function(obj) {
- return is.inDateRange(obj, new Date(), new Date(new Date().setFullYear(new Date().getFullYear() + 1)));
- };
-
- // is a given date in the parameter quarter?
- is.quarterOfYear = function(obj, quarterNumber) {
- return is.date(obj) && is.number(quarterNumber) && quarterNumber === Math.floor((obj.getMonth() + 3) / 3);
- };
- // quarterOfYear method does not support 'all' and 'any' interfaces
- is.quarterOfYear.api = ['not'];
-
- // is a given date in daylight saving time?
- is.dayLightSavingTime = function(obj) {
- var january = new Date(obj.getFullYear(), 0, 1);
- var july = new Date(obj.getFullYear(), 6, 1);
- var stdTimezoneOffset = Math.max(january.getTimezoneOffset(), july.getTimezoneOffset());
- return obj.getTimezoneOffset() < stdTimezoneOffset;
+ // is a given date indicate yesterday?
+ is.yesterday = function(date) {
+ var now = new Date();
+ var yesterdayString = new Date(now.setDate(now.getDate() - 1)).toDateString();
+ return is.date(date) && date.toDateString() === yesterdayString;
};
// Environment checks
/* -------------------------------------------------------------------------- */
- // check if library is used as a Node.js module
- if(typeof window !== 'undefined') {
+ var freeGlobal = is.windowObject(typeof global == 'object' && global) && global;
+ var freeSelf = is.windowObject(typeof self == 'object' && self) && self;
+ var thisGlobal = is.windowObject(typeof this == 'object' && this) && this;
+ var root = freeGlobal || freeSelf || thisGlobal || Function('return this')();
- // store navigator properties to use later
- var userAgent = 'navigator' in window && 'userAgent' in navigator && navigator.userAgent.toLowerCase() || '';
- var vendor = 'navigator' in window && 'vendor' in navigator && navigator.vendor.toLowerCase() || '';
- var appVersion = 'navigator' in window && 'appVersion' in navigator && navigator.appVersion.toLowerCase() || '';
+ var document = freeSelf && freeSelf.document;
+ var previousIs = root.is;
- // is current browser chrome?
- is.chrome = function() {
- return /chrome|chromium/i.test(userAgent) && /google inc/.test(vendor);
- };
- // chrome method does not support 'all' and 'any' interfaces
- is.chrome.api = ['not'];
+ // store navigator properties to use later
+ var navigator = freeSelf && freeSelf.navigator;
+ var appVersion = (navigator && navigator.appVersion || '').toLowerCase();
+ var userAgent = (navigator && navigator.userAgent || '').toLowerCase();
+ var vendor = (navigator && navigator.vendor || '').toLowerCase();
- // is current browser firefox?
- is.firefox = function() {
- return /firefox/i.test(userAgent);
- };
- // firefox method does not support 'all' and 'any' interfaces
- is.firefox.api = ['not'];
+ // is current device android?
+ is.android = function() {
+ return /android/.test(userAgent);
+ };
+ // android method does not support 'all' and 'any' interfaces
+ is.android.api = ['not'];
- // is current browser internet explorer?
- // parameter is optional
- is.ie = function(version) {
- if(!version) {
- return /msie/i.test(userAgent) || "ActiveXObject" in window;
- }
- if(version >= 11) {
- return "ActiveXObject" in window;
- }
- return new RegExp('msie ' + version).test(userAgent);
- };
- // ie method does not support 'all' and 'any' interfaces
- is.ie.api = ['not'];
+ // is current device android phone?
+ is.androidPhone = function() {
+ return /android/.test(userAgent) && /mobile/.test(userAgent);
+ };
+ // androidPhone method does not support 'all' and 'any' interfaces
+ is.androidPhone.api = ['not'];
- // is current browser opera?
- is.opera = function() {
- return /^Opera\//.test(userAgent) || // Opera 12 and older versions
- /\x20OPR\//.test(userAgent); // Opera 15+
- };
- // opera method does not support 'all' and 'any' interfaces
- is.opera.api = ['not'];
+ // is current device android tablet?
+ is.androidTablet = function() {
+ return /android/.test(userAgent) && !/mobile/.test(userAgent);
+ };
+ // androidTablet method does not support 'all' and 'any' interfaces
+ is.androidTablet.api = ['not'];
- // is current browser safari?
- is.safari = function() {
- return /safari/i.test(userAgent) && /apple computer/i.test(vendor);
- };
- // safari method does not support 'all' and 'any' interfaces
- is.safari.api = ['not'];
+ // is current device blackberry?
+ is.blackberry = function() {
+ return /blackberry/.test(userAgent) || /bb10/.test(userAgent);
+ };
+ // blackberry method does not support 'all' and 'any' interfaces
+ is.blackberry.api = ['not'];
- // is current device ios?
- is.ios = function() {
- return is.iphone() || is.ipad() || is.ipod();
- };
- // ios method does not support 'all' and 'any' interfaces
- is.ios.api = ['not'];
+ // is current browser chrome?
+ // parameter is optional
+ is.chrome = function(range) {
+ var match = /google inc/.test(vendor) ? userAgent.match(/(?:chrome|crios)\/(\d+)/) : null;
+ return match !== null && is.not.opera() && compareVersion(match[1], range);
+ };
+ // chrome method does not support 'all' and 'any' interfaces
+ is.chrome.api = ['not'];
- // is current device iphone?
- is.iphone = function() {
- return /iphone/i.test(userAgent);
- };
- // iphone method does not support 'all' and 'any' interfaces
- is.iphone.api = ['not'];
+ // is current device desktop?
+ is.desktop = function() {
+ return is.not.mobile() && is.not.tablet();
+ };
+ // desktop method does not support 'all' and 'any' interfaces
+ is.desktop.api = ['not'];
- // is current device ipad?
- is.ipad = function() {
- return /ipad/i.test(userAgent);
- };
- // ipad method does not support 'all' and 'any' interfaces
- is.ipad.api = ['not'];
+ // is current browser edge?
+ // parameter is optional
+ is.edge = function(range) {
+ var match = userAgent.match(/edge\/(\d+)/);
+ return match !== null && compareVersion(match[1], range);
+ };
+ // edge method does not support 'all' and 'any' interfaces
+ is.edge.api = ['not'];
- // is current device ipod?
- is.ipod = function() {
- return /ipod/i.test(userAgent);
- };
- // ipod method does not support 'all' and 'any' interfaces
- is.ipod.api = ['not'];
+ // is current browser firefox?
+ // parameter is optional
+ is.firefox = function(range) {
+ var match = userAgent.match(/(?:firefox|fxios)\/(\d+)/);
+ return match !== null && compareVersion(match[1], range);
+ };
+ // firefox method does not support 'all' and 'any' interfaces
+ is.firefox.api = ['not'];
- // is current device android?
- is.android = function() {
- return /android/i.test(userAgent);
- };
- // android method does not support 'all' and 'any' interfaces
- is.android.api = ['not'];
+ // is current browser internet explorer?
+ // parameter is optional
+ is.ie = function(range) {
+ var match = userAgent.match(/(?:msie |trident.+?; rv:)(\d+)/);
+ return match !== null && compareVersion(match[1], range);
+ };
+ // ie method does not support 'all' and 'any' interfaces
+ is.ie.api = ['not'];
- // is current device android phone?
- is.androidPhone = function() {
- return /android/i.test(userAgent) && /mobile/i.test(userAgent);
- };
- // androidPhone method does not support 'all' and 'any' interfaces
- is.androidPhone.api = ['not'];
+ // is current device ios?
+ is.ios = function() {
+ return is.iphone() || is.ipad() || is.ipod();
+ };
+ // ios method does not support 'all' and 'any' interfaces
+ is.ios.api = ['not'];
- // is current device android tablet?
- is.androidTablet = function() {
- return /android/i.test(userAgent) && !/mobile/i.test(userAgent);
- };
- // androidTablet method does not support 'all' and 'any' interfaces
- is.androidTablet.api = ['not'];
+ // is current device ipad?
+ // parameter is optional
+ is.ipad = function(range) {
+ var match = userAgent.match(/ipad.+?os (\d+)/);
+ return match !== null && compareVersion(match[1], range);
+ };
+ // ipad method does not support 'all' and 'any' interfaces
+ is.ipad.api = ['not'];
- // is current device blackberry?
- is.blackberry = function() {
- return /blackberry/i.test(userAgent) || /BB10/i.test(userAgent);
- };
- // blackberry method does not support 'all' and 'any' interfaces
- is.blackberry.api = ['not'];
+ // is current device iphone?
+ // parameter is optional
+ is.iphone = function(range) {
+ // avoid false positive for Facebook in-app browser on ipad;
+ // original iphone doesn't have the OS portion of the UA
+ var match = is.ipad() ? null : userAgent.match(/iphone(?:.+?os (\d+))?/);
+ return match !== null && compareVersion(match[1] || 1, range);
+ };
+ // iphone method does not support 'all' and 'any' interfaces
+ is.iphone.api = ['not'];
- // is current device desktop?
- is.desktop = function() {
- return is.not.mobile() && is.not.tablet();
- };
- // desktop method does not support 'all' and 'any' interfaces
- is.desktop.api = ['not'];
+ // is current device ipod?
+ // parameter is optional
+ is.ipod = function(range) {
+ var match = userAgent.match(/ipod.+?os (\d+)/);
+ return match !== null && compareVersion(match[1], range);
+ };
+ // ipod method does not support 'all' and 'any' interfaces
+ is.ipod.api = ['not'];
- // is current operating system linux?
- is.linux = function() {
- return /linux/i.test(appVersion);
- };
- // linux method does not support 'all' and 'any' interfaces
- is.linux.api = ['not'];
+ // is current operating system linux?
+ is.linux = function() {
+ return /linux/.test(appVersion);
+ };
+ // linux method does not support 'all' and 'any' interfaces
+ is.linux.api = ['not'];
- // is current operating system mac?
- is.mac = function() {
- return /mac/i.test(appVersion);
- };
- // mac method does not support 'all' and 'any' interfaces
- is.mac.api = ['not'];
+ // is current operating system mac?
+ is.mac = function() {
+ return /mac/.test(appVersion);
+ };
+ // mac method does not support 'all' and 'any' interfaces
+ is.mac.api = ['not'];
- // is current operating system windows?
- is.windows = function() {
- return /win/i.test(appVersion);
- };
- // windows method does not support 'all' and 'any' interfaces
- is.windows.api = ['not'];
+ // is current device mobile?
+ is.mobile = function() {
+ return is.iphone() || is.ipod() || is.androidPhone() || is.blackberry() || is.windowsPhone();
+ };
+ // mobile method does not support 'all' and 'any' interfaces
+ is.mobile.api = ['not'];
- // is current device windows phone?
- is.windowsPhone = function() {
- return is.windows() && /phone/i.test(userAgent);
- };
- // windowsPhone method does not support 'all' and 'any' interfaces
- is.windowsPhone.api = ['not'];
+ // is current state offline?
+ is.offline = not(is.online);
+ // offline method does not support 'all' and 'any' interfaces
+ is.offline.api = ['not'];
- // is current device windows tablet?
- is.windowsTablet = function() {
- return is.windows() && is.not.windowsPhone() && /touch/i.test(userAgent);
- };
- // windowsTablet method does not support 'all' and 'any' interfaces
- is.windowsTablet.api = ['not'];
+ // is current state online?
+ is.online = function() {
+ return !navigator || navigator.onLine === true;
+ };
+ // online method does not support 'all' and 'any' interfaces
+ is.online.api = ['not'];
- // is current device mobile?
- is.mobile = function() {
- return is.iphone() || is.ipod() || is.androidPhone() || is.blackberry() || is.windowsPhone();
- };
- // mobile method does not support 'all' and 'any' interfaces
- is.mobile.api = ['not'];
+ // is current browser opera?
+ // parameter is optional
+ is.opera = function(range) {
+ var match = userAgent.match(/(?:^opera.+?version|opr)\/(\d+)/);
+ return match !== null && compareVersion(match[1], range);
+ };
+ // opera method does not support 'all' and 'any' interfaces
+ is.opera.api = ['not'];
- // is current device tablet?
- is.tablet = function() {
- return is.ipad() || is.androidTablet() || is.windowsTablet();
- };
- // tablet method does not support 'all' and 'any' interfaces
- is.tablet.api = ['not'];
+ // is current browser phantomjs?
+ // parameter is optional
+ is.phantom = function(range) {
+ var match = userAgent.match(/phantomjs\/(\d+)/);
+ return match !== null && compareVersion(match[1], range);
+ };
+ // phantom method does not support 'all' and 'any' interfaces
+ is.phantom.api = ['not'];
- // is current state online?
- is.online = function() {
- return navigator.onLine;
- };
- // online method does not support 'all' and 'any' interfaces
- is.online.api = ['not'];
+ // is current browser safari?
+ // parameter is optional
+ is.safari = function(range) {
+ var match = userAgent.match(/version\/(\d+).+?safari/);
+ return match !== null && compareVersion(match[1], range);
+ };
+ // safari method does not support 'all' and 'any' interfaces
+ is.safari.api = ['not'];
- // is current state offline?
- is.offline = not(is.online);
- // offline method does not support 'all' and 'any' interfaces
- is.offline.api = ['not'];
+ // is current device tablet?
+ is.tablet = function() {
+ return is.ipad() || is.androidTablet() || is.windowsTablet();
+ };
+ // tablet method does not support 'all' and 'any' interfaces
+ is.tablet.api = ['not'];
- // is current device supports touch?
- is.touchDevice = function() {
- return 'ontouchstart' in window ||'DocumentTouch' in window && document instanceof DocumentTouch;
- };
- // touchDevice method does not support 'all' and 'any' interfaces
- is.touchDevice.api = ['not'];
- }
+ // is current device supports touch?
+ is.touchDevice = function() {
+ return !!document && ('ontouchstart' in freeSelf ||
+ ('DocumentTouch' in freeSelf && document instanceof DocumentTouch));
+ };
+ // touchDevice method does not support 'all' and 'any' interfaces
+ is.touchDevice.api = ['not'];
+
+ // is current operating system windows?
+ is.windows = function() {
+ return /win/.test(appVersion);
+ };
+ // windows method does not support 'all' and 'any' interfaces
+ is.windows.api = ['not'];
+
+ // is current device windows phone?
+ is.windowsPhone = function() {
+ return is.windows() && /phone/.test(userAgent);
+ };
+ // windowsPhone method does not support 'all' and 'any' interfaces
+ is.windowsPhone.api = ['not'];
+
+ // is current device windows tablet?
+ is.windowsTablet = function() {
+ return is.windows() && is.not.windowsPhone() && /touch/.test(userAgent);
+ };
+ // windowsTablet method does not support 'all' and 'any' interfaces
+ is.windowsTablet.api = ['not'];
// Object checks
/* -------------------------------------------------------------------------- */
// has a given object got parameterized count property?
- is.propertyCount = function(obj, count) {
- if(!is.object(obj) || !is.number(count)) {
+ is.propertyCount = function(object, count) {
+ if (is.not.object(object) || is.not.number(count)) {
return false;
}
- if(Object.keys) {
- return Object.keys(obj).length === count;
- }
- var properties = [],
- property;
- for(property in obj) {
- if (hasOwnProperty.call(obj, property)) {
- properties.push(property);
+ var n = 0;
+ for (var property in object) {
+ if (hasOwnProperty.call(object, property) && ++n > count) {
+ return false;
}
}
- return properties.length === count;
+ return n === count;
};
// propertyCount method does not support 'all' and 'any' interfaces
is.propertyCount.api = ['not'];
// is given object has parameterized property?
- is.propertyDefined = function(obj, property) {
- return is.object(obj) && is.string(property) && property in obj;
+ is.propertyDefined = function(object, property) {
+ return is.object(object) && is.string(property) && property in object;
};
// propertyDefined method does not support 'all' and 'any' interfaces
is.propertyDefined.api = ['not'];
- // is a given object window?
- // setInterval method is only available for window object
- is.windowObject = function(obj) {
- return typeof obj === 'object' && 'setInterval' in obj;
- };
-
- // is a given object a DOM node?
- is.domNode = function(obj) {
- return is.object(obj) && obj.nodeType > 0;
- };
-
// Array checks
/* -------------------------------------------------------------------------- */
// is a given item in an array?
- is.inArray = function(val, arr){
- if(is.not.array(arr)) {
+ is.inArray = function(value, array) {
+ if (is.not.array(array)) {
return false;
}
- for(var i = 0; i < arr.length; i++) {
- if (arr[i] === val) return true;
+ for (var i = 0; i < array.length; i++) {
+ if (array[i] === value) {
+ return true;
+ }
}
return false;
};
@@ -764,12 +843,15 @@
is.inArray.api = ['not'];
// is a given array sorted?
- is.sorted = function(arr) {
- if(is.not.array(arr)) {
+ is.sorted = function(array, sign) {
+ if (is.not.array(array)) {
return false;
}
- for(var i = 0; i < arr.length; i++) {
- if(arr[i] > arr[i + 1]) return false;
+ var predicate = comparator[sign] || comparator['>='];
+ for (var i = 1; i < array.length; i++) {
+ if (!predicate(array[i], array[i - 1])) {
+ return false;
+ }
}
return true;
};
@@ -780,17 +862,17 @@
function setInterfaces() {
var options = is;
- for(var option in options) {
- if(hasOwnProperty.call(options, option) && is.function(options[option])) {
+ for (var option in options) {
+ if (hasOwnProperty.call(options, option) && is['function'](options[option])) {
var interfaces = options[option].api || ['not', 'all', 'any'];
for (var i = 0; i < interfaces.length; i++) {
- if(interfaces[i] === 'not') {
+ if (interfaces[i] === 'not') {
is.not[option] = not(is[option]);
}
- if(interfaces[i] === 'all') {
+ if (interfaces[i] === 'all') {
is.all[option] = all(is[option]);
}
- if(interfaces[i] === 'any') {
+ if (interfaces[i] === 'any') {
is.any[option] = any(is[option]);
}
}
@@ -803,15 +885,6 @@
// Intentionally added after setInterfaces function
/* -------------------------------------------------------------------------- */
- // set optional regexps to methods if you think they suck
- is.setRegexp = function(regexp, regexpName) {
- for(var r in regexps) {
- if(hasOwnProperty.call(regexps, r) && (regexpName === r)) {
- regexps[r] = regexp;
- }
- }
- };
-
// change namespace of library to prevent name collisions
// var preferredName = is.setNamespace();
// preferredName.odd(3);
@@ -821,5 +894,14 @@
return this;
};
+ // set optional regexes to methods
+ is.setRegexp = function(regexp, name) {
+ for (var r in regexes) {
+ if (hasOwnProperty.call(regexes, r) && (name === r)) {
+ regexes[r] = regexp;
+ }
+ }
+ };
+
return is;
-}));
+}));
\ No newline at end of file
diff --git a/core/api/endpoint/authentication_endpoint.go b/core/api/endpoint/authentication_endpoint.go
index 994a4545..f11773f0 100644
--- a/core/api/endpoint/authentication_endpoint.go
+++ b/core/api/endpoint/authentication_endpoint.go
@@ -12,22 +12,17 @@
package endpoint
import (
- "crypto/rand"
"database/sql"
"encoding/json"
"errors"
"fmt"
"net/http"
"strings"
- "time"
-
- jwt "github.com/dgrijalva/jwt-go"
"github.com/documize/community/core/api/endpoint/models"
"github.com/documize/community/core/api/entity"
"github.com/documize/community/core/api/request"
"github.com/documize/community/core/api/util"
- "github.com/documize/community/core/environment"
"github.com/documize/community/core/log"
"github.com/documize/community/core/section/provider"
"github.com/documize/community/core/utility"
@@ -308,130 +303,3 @@ func preAuthorizeStaticAssets(r *http.Request) bool {
return false
}
-
-var jwtKey string
-
-func init() {
- environment.GetString(&jwtKey, "salt", false, "the salt string used to encode JWT tokens, if not set a random value will be generated",
- func(t *string, n string) bool {
- if jwtKey == "" {
- b := make([]byte, 17)
- _, err := rand.Read(b)
- if err != nil {
- jwtKey = err.Error()
- log.Error("problem using crypto/rand", err)
- return false
- }
- for k, v := range b {
- if (v >= 'a' && v <= 'z') || (v >= 'A' && v <= 'Z') || (v >= '0' && v <= '0') {
- b[k] = v
- } else {
- s := fmt.Sprintf("%x", v)
- b[k] = s[0]
- }
- }
- jwtKey = string(b)
- log.Info("Please set DOCUMIZESALT or use -salt with this value: " + jwtKey)
- }
- return true
- })
-}
-
-// Generates JSON Web Token (http://jwt.io)
-func generateJWT(user, org, domain string) string {
- token := jwt.New(jwt.SigningMethodHS256)
-
- // issuer
- token.Claims["iss"] = "Documize"
- // subject
- token.Claims["sub"] = "webapp"
- // expiry
- token.Claims["exp"] = time.Now().Add(time.Hour * 168).Unix()
- // data
- token.Claims["user"] = user
- token.Claims["org"] = org
- token.Claims["domain"] = domain
-
- tokenString, _ := token.SignedString([]byte(jwtKey))
-
- return tokenString
-}
-
-// Check for authorization token.
-// We look for 'Authorization' request header OR query string "?token=XXX".
-func findJWT(r *http.Request) (token string) {
- header := r.Header.Get("Authorization")
-
- if header != "" {
- header = strings.Replace(header, "Bearer ", "", 1)
- }
-
- if len(header) > 1 {
- token = header
- } else {
- query := r.URL.Query()
- token = query.Get("token")
- }
-
- if token == "null" {
- token = ""
- }
-
- return
-}
-
-// We take in raw token string and decode it.
-func decodeJWT(tokenString string) (c request.Context, claims map[string]interface{}, err error) {
- method := "decodeJWT"
-
- // sensible defaults
- c.UserID = ""
- c.OrgID = ""
- c.Authenticated = false
- c.Guest = false
-
- token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
- return []byte(jwtKey), nil
- })
-
- if err != nil {
- err = fmt.Errorf("bad authorization token")
- return
- }
-
- if !token.Valid {
- if ve, ok := err.(*jwt.ValidationError); ok {
- if ve.Errors&jwt.ValidationErrorMalformed != 0 {
- log.Error("invalid token", err)
- err = fmt.Errorf("bad token")
- return
- } else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
- log.Error("expired token", err)
- err = fmt.Errorf("expired token")
- return
- } else {
- log.Error("invalid token", err)
- err = fmt.Errorf("bad token")
- return
- }
- } else {
- log.Error("invalid token", err)
- err = fmt.Errorf("bad token")
- return
- }
- }
-
- c = request.NewContext()
- c.UserID = token.Claims["user"].(string)
- c.OrgID = token.Claims["org"].(string)
-
- if len(c.UserID) == 0 || len(c.OrgID) == 0 {
- err = fmt.Errorf("%s : unable parse token data", method)
- return
- }
-
- c.Authenticated = true
- c.Guest = false
-
- return c, token.Claims, nil
-}
diff --git a/core/api/endpoint/jwt.go b/core/api/endpoint/jwt.go
new file mode 100644
index 00000000..066970aa
--- /dev/null
+++ b/core/api/endpoint/jwt.go
@@ -0,0 +1,153 @@
+// Copyright 2016 Documize Inc. . All rights reserved.
+//
+// This software (Documize Community Edition) is licensed under
+// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
+//
+// You can operate outside the AGPL restrictions by purchasing
+// Documize Enterprise Edition and obtaining a commercial license
+// by contacting .
+//
+// https://documize.com
+
+package endpoint
+
+import (
+ "crypto/rand"
+ "fmt"
+ "net/http"
+ "strings"
+ "time"
+
+ jwt "github.com/dgrijalva/jwt-go"
+
+ "github.com/documize/community/core/api/request"
+ "github.com/documize/community/core/environment"
+ "github.com/documize/community/core/log"
+)
+
+var jwtKey string
+
+func init() {
+ environment.GetString(&jwtKey, "salt", false, "the salt string used to encode JWT tokens, if not set a random value will be generated",
+ func(t *string, n string) bool {
+ if jwtKey == "" {
+ b := make([]byte, 17)
+ _, err := rand.Read(b)
+ if err != nil {
+ jwtKey = err.Error()
+ log.Error("problem using crypto/rand", err)
+ return false
+ }
+ for k, v := range b {
+ if (v >= 'a' && v <= 'z') || (v >= 'A' && v <= 'Z') || (v >= '0' && v <= '0') {
+ b[k] = v
+ } else {
+ s := fmt.Sprintf("%x", v)
+ b[k] = s[0]
+ }
+ }
+ jwtKey = string(b)
+ log.Info("Please set DOCUMIZESALT or use -salt with this value: " + jwtKey)
+ }
+ return true
+ })
+}
+
+// Generates JSON Web Token (http://jwt.io)
+func generateJWT(user, org, domain string) string {
+ token := jwt.New(jwt.SigningMethodHS256)
+
+ // issuer
+ token.Claims["iss"] = "Documize"
+ // subject
+ token.Claims["sub"] = "webapp"
+ // expiry
+ token.Claims["exp"] = time.Now().Add(time.Hour * 168).Unix()
+ // data
+ token.Claims["user"] = user
+ token.Claims["org"] = org
+ token.Claims["domain"] = domain
+
+ tokenString, _ := token.SignedString([]byte(jwtKey))
+
+ return tokenString
+}
+
+// Check for authorization token.
+// We look for 'Authorization' request header OR query string "?token=XXX".
+func findJWT(r *http.Request) (token string) {
+ header := r.Header.Get("Authorization")
+
+ if header != "" {
+ header = strings.Replace(header, "Bearer ", "", 1)
+ }
+
+ if len(header) > 1 {
+ token = header
+ } else {
+ query := r.URL.Query()
+ token = query.Get("token")
+ }
+
+ if token == "null" {
+ token = ""
+ }
+
+ return
+}
+
+// We take in raw token string and decode it.
+func decodeJWT(tokenString string) (c request.Context, claims map[string]interface{}, err error) {
+ method := "decodeJWT"
+
+ // sensible defaults
+ c.UserID = ""
+ c.OrgID = ""
+ c.Authenticated = false
+ c.Guest = false
+
+ token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
+ return []byte(jwtKey), nil
+ })
+
+ if err != nil {
+ err = fmt.Errorf("bad authorization token")
+ return
+ }
+
+ if !token.Valid {
+ if ve, ok := err.(*jwt.ValidationError); ok {
+ if ve.Errors&jwt.ValidationErrorMalformed != 0 {
+ log.Error("invalid token", err)
+ err = fmt.Errorf("bad token")
+ return
+ } else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
+ log.Error("expired token", err)
+ err = fmt.Errorf("expired token")
+ return
+ } else {
+ log.Error("invalid token", err)
+ err = fmt.Errorf("bad token")
+ return
+ }
+ } else {
+ log.Error("invalid token", err)
+ err = fmt.Errorf("bad token")
+ return
+ }
+ }
+
+ c = request.NewContext()
+ c.UserID = token.Claims["user"].(string)
+ c.OrgID = token.Claims["org"].(string)
+
+ if len(c.UserID) == 0 || len(c.OrgID) == 0 {
+ err = fmt.Errorf("%s : unable parse token data", method)
+ return
+ }
+
+ c.Authenticated = true
+ c.Guest = false
+
+ return c, token.Claims, nil
+}