Days name in order in Jquery - javascript

I have a array like
var abc = ["mon","Thu","Fri","Tue","Wed","Sun","Sat"]
So next I want these days in order like
["mon","Tue","Wed","Thu","Fri","Sat","Sun"]
Is there any inbuilt functions or any logic for it?

I used a map object to be used along with a sort function .. pretty easy.
var arrayOfDays = ["Mon","Thu","Fri","Tue","Wed","Sun","Sat"]; //array to be sorted
//int this map, define the "correct" order of days
var map = {
"Sun" : 0,
"Mon" : 1,
"Tue" : 2,
"Wed" : 3,
"Thu" : 4,
"Fri" : 5,
"Sat" : 6,
}
function dateSort(array) //function to sort the array
{
for(i = 0 ; i < array.length ; i++)
for(j = i + 1 ; j < array.length ; j++)
{
if(map[array[i]] > map[array[j]])
{
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}
It can be invoked as follows:
datesort(arrayOfDays);
I tested it by printing the values of array of dates before calling the function and after:
Before: Mon Thu Fri Tue Wed Sun Sat
After: Sun Mon Tue Wed Thu Fri Sat
EDIT: As suggested by others, a version using Array.sort() - though avoid it if you are a beginner.
Define a custom compare function:
function dateCompare(d1, d2)
{
if(map[d1] == map[d2])
return 0;
return (map[d1] < map[d2]) ? -1 : 1;
}
Invoke as:
arrayOfDays.sort(dateCompare);

As indicated by others, you need to use square brackets for arrays, not curly ones:
var abc = ["mon","Thu","Fri","Tue","Wed","Sun","Sat"];
There is no predefined method that recognizes weekdays, but you can just compare given day names to a list that is in the correct order, like this:
function compareDayNames(day1, day2) {
var weekdays = ',monday,tuesday,wednesday,thursday,friday,saturday,sunday';
return weekdays.indexOf(',' + day1.toLowerCase())
- weekdays.indexOf(',' + day2.toLowerCase());
}
var abc = ["mon","Thu","Fri","Tue","Wed","Sun","Sat"];
abc.sort(compareDayNames);
console.log('Sorted: ' + abc.join(','));
Output in the console is:
Sorted: mon,Tue,Wed,Thu,Fri,Sat,Sun
This function also sorts longer and shorter names well:
var abc = ['Satur','Tu', 'W', 'M', 'Thurs'];
abc.sort(compareDayNames);
console.log('Sorted: ' + abc.join(','));
Output:
Sorted: M,Tu,W,Thurs,Satur
EDIT:
If you want to throw an error when an invalid day name is provided, or a name that is less than 2 characters (because T and S are ambiguous), then rewrite the compare function as follows:
function compareDayNames(day1, day2) {
var weekdays = ',monday,tuesday,wednesday,thursday,friday,saturday,sunday';
function dayNumber(day) {
var pos = weekdays.indexOf(',' + day.toLowerCase());
if (pos === -1 || day.length < 2) {
throw '"' + day + '" is not a valid day name';
}
return pos;
}
return dayNumber(day1) - dayNumber(day2);
}

$(document).ready(function () {
var ndays = "";
var days = "Sat,Fri,mon,Thu,Tue,Wed,Sun";
var dys = days.split(",");
debugger;
for (var i = 0; i < dys.length; i++) {
switch (dys[i]) {
case "mon":
ndays = ndays + ",1";
break;
case "Tue":
ndays = ndays + ",2";
break;
case "Wed":
ndays = ndays + ",3";
break;
case "Thu":
ndays = ndays + ",4";
break;
case "Fri":
ndays = ndays + ",5";
break;
case "Sat":
ndays = ndays + ",6";
break;
default:
ndays = ndays + ",7";
break;
}
alert(ndays);
}
var cDays = "";
var wdays = ndays.split(",").sort();
for (var s = 0; s < wdays.length; s++) {
//alert(wdays[s]);
debugger;
if (wdays[s] === "1") {
cDays = "Mon";
}
else if (wdays[s] === "2") {
cDays = cDays + ",Tue";
}
else if (wdays[s] === "3") {
cDays = cDays + ",Wed";
}
else if (wdays[s] === "4") {
cDays = cDays + ",Thu";
}
else if (wdays[s] === "5") {
cDays = cDays + ",Fri";
}
else if (wdays[s] === "6") {
cDays = cDays + ",Sat";
}
else {
cDays = cDays + ",Sun";
}
}
});

There is no inbuilt function for it, you need to write your own logic as suggested in other answers. You can create one common function to compare two array's as...
function compareDays(wrongSquence , rightSequence) {
var i=0;
for(i=0;i<wrongSquence.length;i++) {
if(wrongSquence [i] != rightSequence[i]) {
wrongSquence [i] = rightSequence[i];
}
}
return wrongSquence;
}
//e.g....
var abc = ["mon","Thu","Fri","Tue","Wed","Sun","Sat"],
outputFormat = ["mon","Tue","Wed","Thu","Fri","Sat","Sun"];
var correctArray = compareDays(abc, outputFormat);
console.log(correctArray); // check output...

Related

Dynamically create RegExps groups to match patterns lower than or greater than a desired value

I've made two JavaScript functions to dynamically create RegExps groups that match numbers lower than or greater than the number that is sent in the parameter. The purpose of these functions is to do something like this or this but dynamically, I need it for an App for building RegExps, this code made a group that match a particular group of numbers, later you could use the returned group to complete your final RegExp.
Here is the function to create a RegExp to find patterns greater than a desired value:
//Find greater than numbers
function getGreaterUintRegEx(n) {
var s = String(n);
var t = s.length,
a = [];
for (var i = 1; i < t + 1; i++) {
switch (s.charAt(t - i)) {
case "9":
a.push((Number(s.slice(0, t - i)) + 1) + "0" + (new Array(i)).join("\\d"));
break;
case "8":
a.push(s.slice(0, t - i) + "9" + (new Array(i)).join("\\d"));
break;
default:
a.push(s.slice(0, t - i) + "[" + (Number(s.charAt(t - i)) + 1) + "-9]" + (new Array(i)).join("\\d"));
}
}
a.push("\\d{" + (t + 1) + ",}");
a = a.filter(function(s, i) {
return a.indexOf(s) == i;
});
return "(" + a.join("|") + ")";
}
Example of use:
var regstr = getGreaterUintRegEx(124);
// (12[5-9]|1[3-9]\d|[2-9]\d\d|\d{4,})
var regstr = getGreaterUintRegEx(500);
// (50[1-9]|5[1-9]\d|[6-9]\d\d|\d{4,})
And here is the function to create a RegExp to find patterns lower than a desired value:
//Find lower than numbers
function getLowerUintRegEx(n) {
if (n == 0) return false;
if (n == 1) return "(0)";
if (n > 0 && n < 10) return "[0-" + (n - 1) + "]";
var s = String(n);
var t = s.length,
a = [];
for (var i = 1; i < t + 1; i++) {
switch (s.charAt(t - i)) {
case "0":
a.push(((s.slice(0, t - i) == "1") ? "" : (Number(s.slice(0, t - i)) - 1)) + "9" + (new Array(i)).join("\\d"));
break;
case "1":
a.push("[1-9]" + (new Array(i - 1)).join("\\d"));
break;
default:
a.push(s.slice(0, t - i) + "[0-" + (Number(s.charAt(t - i)) - 1) + "]" + (new Array(i)).join("\\d"));
}
}
if (t - 1 > 1) a.push("\\d{1," + (t - 1) + "}");
a.push("0");
a = a.filter(function(s, i) {
return a.indexOf(s) == i;
});
return "(" + a.join("|") + ")";
}
Example of use:
var regstr = getLowerUintRegEx(498);
// (49[0-7]|4[0-8]\d|[0-3]\d\d|\d{1,2}|0)
var regstr = getLowerUintRegEx(125);
// (12[0-4]|1[0-1]\d|[1-9]\d|\d{1,2}|0)
I want to make these functions more simply and less slow. It takes more than a second with big numbers. Is there any other easier method? Somebody knows a robust algorithm with less steps?
You'll get a big speedup just by removing unnecessary data structures (each call creates several Arrays and a Function, none are needed).
Here's a rewrite of just getGreaterUintRegEx:
function getGreaterUintRegEx(n) {
var nStr = String(n);
var len = nStr.length;
var result = '(';
var ds = '';
var i;
for (i = len - 1; i >= 0; i--) {
switch (nStr.charAt(i)) {
case '9': result += `${+nStr.slice(0, i) + 1}0${ds}|`; break;
case '8': result += `${nStr.slice(0, i)}9${ds}|`; break;
default: result += `${nStr.slice(0, i)}[${+nStr.charAt(i) + 1}-9]${ds}|`;
}
ds += '\\d';
}
return `${result}\\d{${len + 1},})`;
}
I've used ES6 template strings just for readability. They're currently supported across evergreen browsers, but you'll want to swap them for the old '' + '' if you want to support IE11.
This is not the way that I would solve the problem, but you are not looking for a better solution but actually want a review of your code, suggestions and optimisations that give the same functionality as your original (which is working code).
Anyway, below is a suggestion. The code is more readable, I have no intention of testing its performance.
var reduceRight = Function.prototype.call.bind(Array.prototype.reduceRight);
//Find greater than numbers
function getGreaterUintRegEx(n) {
var s = String(n);
var t = s.length - 1;
var a = reduceRight(s, function(acc, v, i) {
var x = s.slice(0, i);
if (v === '9') {
x = Number(x) + 1 + '0';
} else if (v === '8') {
x += '9';
} else {
x += '[' + (Number(v) + 1) + '-9]';
}
acc.push(x + '\\d'.repeat(t - i));
return acc;
}, []);
a.push('\\d{' + (t + 2) + ',}');
return '(' + a.join('|') + ')';
}
//Find greater than numbers: original
function getGreaterUintRegEx1(n) {
var s = String(n);
var t = s.length,
a = [];
for (var i = 1; i < t + 1; i++) {
switch (s.charAt(t - i)) {
case "9":
a.push((Number(s.slice(0, t - i)) + 1) + "0" + (new Array(i)).join("\\d"));
break;
case "8":
a.push(s.slice(0, t - i) + "9" + (new Array(i)).join("\\d"));
break;
default:
a.push(s.slice(0, t - i) + "[" + (Number(s.charAt(t - i)) + 1) + "-9]" + (new Array(i)).join("\\d"));
}
}
a.push("\\d{" + (t + 1) + ",}");
a = a.filter(function(s, i) {
return a.indexOf(s) == i;
});
return "(" + a.join("|") + ")";
}
var out = document.getElementById('out');
for (var i = 0; i < 100000; i += 1) {
var suggested = getGreaterUintRegEx(i);
var original = getGreaterUintRegEx1(i);
if (suggested !== original) {
var txt = suggested + '!==' + original;
out.textContent = txt;
throw new Error(txt);
}
}
out.textContent = suggested + '\n' + original + '\nSame results';
<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.4.1/es5-shim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/json3/3.3.2/json3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.34.1/es6-shim.js"></script>
<pre id="out"></pre>
way to complicated, your approach. And way to limited.
function getInts(str){
return String(str).match(/[+-]?\d+/g).map(Number);
}
function greaterThan(w){
return function(v){ return v > w }
}
function lowerThan(w){ //
return function(v){ return v < w }
}
getInts(someString).filter( greaterThan(473) )
or the more generic approach:
var is = (function(is){
for(var key in is) is[key] = Function("w", "return function(v){return v " + is[key] + " w}");
return is;
})({
eq: "===",
neq: "!==",
gt: ">",
lt: "<",
gteq: ">=",
lteq: "<="
});
is.typeof = function(w){ return function(v){ return typeof v === w }};
getInts(someString).filter( is.lt(92) );

returning equation with all possible parenthesis combinations and the result of each

On a recent interview, I was asked to return all possible combinations of order of operations on an input string, and the result. you should return all the ways/combinations in which you can "force" operations with parenthesis. I got the result (right hand side of the equation) but got stuck on the left side. how could I have done the left side and the right hand side together? Seems like two problems in one...
//input:
console.log(diffWaysToCompute("2 * 3 - 4 * 5"));
//output:
(2*(3-(4*5))) = -34
((2*3)-(4*5)) = -14
((2*(3-4))*5) = -10
(2*((3-4)*5)) = -10
(((2*3)-4)*5) = 10
'use strict'
function getNumbersAndOperators(str) {
var arr = str.split(" ");
var operators = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i] === "-" || arr[i] === "*" || arr[i] === "+") {
operators.push(arr[i]);
arr.splice(i, 1);
// console.log(operators);
}
}
return [arr, operators];
}
// console.log(getNumbersAndOperators("2 - 1 - 1"))
var diffWaysToCompute = function (input) {
// var numbers = input.split(" ");
// console.log(numbers);
// // console.log(number);
var results = compute(input);
results.sort(function (a, b) {
return a - b;
});
//put the numbers length into valid parenthesis:
var NumbersAndOperators = getNumbersAndOperators(input);
var numbers = NumbersAndOperators[0];
console.log(numbers);
var operators = NumbersAndOperators[1];
console.log(operators);
var parens = validParentheses(numbers.length);
// console.log(numbers);
console.log(operators);
// for (var i = 0; i < parens.length; i++) {
// for (var j = 0; j < parens[i].length; j++) {
// var val = parens[i][j];
// console.log(val);
// if (val === " ") {
// var num = numbers.shift();
// parens.splice(val, 0, num);
// //starting running into infinite loops and out of time.
// j--;
// }
// }
// i--;
// }
console.log(parens);
return results;
};
function validParentheses(n) {
if (n === 1) {
return ['( )'];
}
var prevParentheses = validParentheses(n - 1);
var list = {};
prevParentheses.forEach(function (item) {
list['( ' + item + ' )'] = null;
list['( )' + item] = null;
list[item + '( )'] = null;
});
console.log(Object.keys(list))
return Object.keys(list);
}
function compute(str) {
var res = [];
var i;
var j;
var k;
var left;
var right;
var string = [];
var placed = true;
if (!/[+*-]/.test(str)) { // + - *
return [parseInt(str)];
}
for (i = 0; i < str.length; i++) {
if (/\+|\-|\*/.test(str[i])) { // + - *
left = compute(str.substring(0, i));
right = compute(str.substring(i + 1, str.length));
for (j = 0; j < left.length; j++) {
for (k = 0; k < right.length; k++) {
if (str[i] === '+') {
res.push(parseInt(left[j] + right[k]));
} else if (str[i] === '-') {
// string.push("(" + str[i-2], str[i+2] + ")");
res.push(parseInt(left[j] - right[k]));
} else if (str[i] === '*') {
res.push(parseInt(left[j] * right[k]));
}
}
}
}
}
// console.log(string);
return res;
}
console.log(diffWaysToCompute("2 - 1 - 1"));
console.log(diffWaysToCompute("2 * 3 - 4 * 5"));
I never had to do such silly things, so let me try my teeth at it now.
(Caveat as always: it's highly simplified and without any checks&balances!)
The parser is the simplest thing here:
/*
Use of strings instead of ASCII codes for legibility.
I changed x - y to x + (-y) not only for convenience
but for algebraic correctness, too.
#param a array number nodes
#param o array operator nodes
*/
function parse(s,a,o){
var fnum = 0;
var uminus = false
for(var i=0;i<s.length;i++){
switch(s[i]){
case '-': uminus = true;
a.push(fnum);
o.push('+');
fnum = 0;
break;
case '+':
case '*':
case '/': if(uminus){
uminus = false;
fnum *= -1;
}
a.push(fnum);
o.push(s[i]);
fnum = 0;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': fnum = fnum * 10 + parseInt(s[i]);
break;
default: break;
}
}
//assuming symmetry
a.push(fnum);
}
The (-generation took me some time, too much time--I cheated here ;-)
/*
Found in an old notebook (ported from C)
Algo. is O(n^2) and can be done faster but I
couldn't be a...ehm, had no time, sorry.
#idx int index into individual result
#n int number of groups
#open int number of opening parentheses
#close int number of closing parentheses
#a array individual result
#all array space for all results
*/
function makeParens(idx,n,open,close,a,all){
if(close == n){
all.push(a.slice(0));
return;
} else {
if(open > close){
a[idx] = ')';
makeParens(idx+1,n,open,close+1,a,all);
}
if(open < n){
a[idx] = '(';
makeParens(idx+1,n,open+1,close,a,all);
}
}
}
And now? Yepp, that took me a while:
/*
The interesting part
Not very optimized but working
#s string the equation
#return array nicely formatted result
*/
function parenthesing(s){
var nums = [];
var ops = [];
var all = [];
var parens = [];
// parse input into numbers and operators
parse(input,nums,ops);
/*
Rules:
1) out-most parentheses must be open in direction to center
e.g.: (1+2+3), 1+(2+3), 1+(2+3)+4
but not: 1)+(2+3)+(4
so: first parenthesis on the left side must be open and
the last parenthesis on the right side must be close
2) parentheses in direct neighborhood to a number must be
open in direction to the number (multiplication is
not mutual)
e.g.: 1+(2+3)+4, but not: 1+2(+3+4)
3) parentheses in direct neighborhood to an operator must be
closed in direction to the operator (multiplication is
not mutual)
e.g.: 1+(2+3)+4, but not: 1+2(+3+)4
*/
// build combinations separately not in-line
// it's already a mess, no need to add more
makeParens(0,nums.length,0,0,[],parens);
// You may take a look at the raw material here
// console.log(parens.join("\n"));
for(var i= 0;i<parens.length;i++){
var term = [];
// work on copies to reduce pointer juggling
var _ops = ops.slice(0);
var _nums = nums.slice(0);
for(var j=0;j<parens[i].length;j++){
if(parens[i][j] === '('){
term.push("(");
// rule 3
if(parens[i][j+1] === ')'){
term.push(_nums.shift());
}
// rules 1,2
else {
term.push(_nums.shift());
term.push(_ops.shift());
}
}
if(parens[i][j] === ')'){
term.push(")");
// rules 2,3
if(parens[i][j+1] !== ')')
term.push(_ops.shift());
}
}
// some pretty printing
term = term.join("");
// eval() because I didn't want to write a parser
// but if you need one...
all.push(term + " = " + eval(term));
}
return all;
}
I'm not sure if I would get hired with that abomination. Ah, to be honest: I doubt it.
But I hope it is at least a little bit helpful.
Yikes. That was tricky. Good challenge. I'm sure this could be cut way down, but it works. I used lodash and broke the various functions down to make it more flexible. Here's a jsfiddle:
https://jsfiddle.net/mckinleymedia/3e8g22Lk/8/
Oops - had to add parseInt to the addition so it doesn't add as strings.
/*
//input:
diffWaysToCompute("2 * 3 - 4 * 5");
//output:
(2*(3-(4*5))) = -34 - 2,1,0
((2*3)-(4*5)) = -14 - 0,2,1 & 2,0,1
((2*(3-4))*5) = -10 - 1,0,2
(2*((3-4)*5)) = -10 - 1,2,0
(((2*3)-4)*5) = 10 - 0,1,2
*/
'use strict'
var diffWaysToCompute = function(str) {
var opsAvailable = ['+','-','/','*'],
numbers = [],
operators = [],
getNumbersAndOperators = function(str) {
var arr = str.split(" ");
for (var i in arr) {
if ( opsAvailable.indexOf( arr[i] ) > -1 ) {
operators.push( arr[i] );
} else {
numbers.push( arr[i] );
}
};
return;
},
permutator = function(range) {
var results = [];
function permute(arr, memo) {
var cur,
memo = memo || [];
for (var i in arr) {
cur = arr.splice(i, 1);
if (arr.length === 0) results.push(memo.concat(cur));
permute(arr.slice(), memo.concat(cur));
arr.splice(i, 0, cur[0]);
}
return results;
}
return permute(_.range(range));
},
equations = function( perms ) {
var results = [];
_.each(perms, function( perm, k ) {
results[k] = nest ( perm );
});
return results;
},
nest = function( perm ) {
var eqs = eqs || [],
ref = ref || _.range(perm.length).map(function () { return undefined }),
eq,
target = undefined;
for (var i in perm) {
var cur = perm[i],
next = perm[i] + 1,
n1 = numbers[ cur ],
n2 = numbers[ next ],
r1 = ref[ cur ],
r2 = ref[ next ];
if ( r1 !== undefined) n1 = eqs [ r1 ];
if ( r2 !== undefined) n2 = eqs [ r2 ];
var rNew;
rNew = eqs.length;
for (var x in ref ) {
if ( ( ref[ x ] !== undefined ) && ( ref[ x ] == r1 || ref[ x ] == r2 ) ) ref[ x ] = eqs.length;
};
ref[ cur ] = ref[ next ] = eqs.length;
eqs.push({
ops: operators[ cur ],
nums: [ n1, n2 ]
});
};
return eqs[ eqs.length - 1 ];
},
calculations = function ( eqs ) {
var results = []
_.each(eqs, function(equation) {
results.push(calculate( equation ));
});
return results;
},
calculate = function( eq ) {
var result = {
text: ""
};
// result.eq = eq;
result.text += "( ";
result.total = eq.nums[ 0 ];
if ( _.isObject(result.total) ) {
var result1 = calculate( result.total );
result.total = result1.total;
result.text += result1.text;
} else {
result.text += eq.nums[ 0 ];
}
_.each(eq.ops, function (op, k) {
var num = eq.nums[ k + 1 ];
result.text += " " + op + " ";
if ( _.isObject(num) ) {
var result2 = calculate( num );
num = result2.total;
result.text += result2.text;
} else {
result.text += num;
}
if ( op === '+') result.total = parseInt(result.total) + parseInt(num);
if ( op === '-') result.total = result.total - num;
if ( op === '/') result.total = result.total / num;
if ( op === '*') result.total = result.total * num;
});
result.text += " )";
return result;
},
display = function( as ) {
var target = document.getElementById('result');
target.innerHTML += '<h3 class="problem">String given: ' + str + '</h3>';
target.innerHTML += '<h4>Permutations</h4>';
_.each( as, function(a) {
target.innerHTML += '<div class="permutation">';
target.innerHTML += ' <span class="formula">' + a.text + '</span> = ';
target.innerHTML += ' <span class="total">' + a.total + '</span>';
target.innerHTML += '</div>';
});
},
perms,
eqs,
answers;
getNumbersAndOperators(str);
perms = permutator( operators.length );
eqs = equations( perms );
answers = calculations( eqs );
answers = _.uniq(answers, 'text');
display(answers);
return answers;
};
console.log(diffWaysToCompute("2 * 3 - 4 * 5"));

MobiScroll datePicker reset value 00-000-0000

Mobiscroll date picker
every year should ends with reset value 00-000-0000, the reset value is achieved when i click the reset button. how to achieve this.
+----+-----+------+
| 31 | dec | 2014 |
| 00 | 000 | 0000 | ----->reset value
| 01 | jan | 2015 |
| 02 | jan | 2015 |
+----+-----+------+
--------------------
|ok| |reset| |cancel|
--------------------
To add reset value in the date picker scroll view. modify few lines of code in
mobiscroll.util.datetime.js, mobiscroll.datetimebase.js
In mobiscroll.datetimebase.js,
under variable defaults={}, add partialDate : false,
add reset values in a wheel generating loop: for (k = 0; k < 3; k++) {},
if (s.partialDate)
{
keys.push(-1);
values.push('0000');
}
replace following codes in a mobiscroll.datetimebase.js
formatValue: function (d) {
//return datetime.formatDate(format, getDate(d), s);
//KKN
if (d[o["m"]] != "-1" && d[o["d"]] != "0" )
{
datetime.partialDate = false;
return datetime.formatDate(format, getDate(d), s);
}
else
{
datetime.partialDate = true;
var tempDate = [],
hideDate = false,
hideMonth = false;
$.each(['y', 'm', 'd', 'a', 'h', 'i', 's'], function (x, i) {
tempDate[o[i]] = d[o[i]];
});
if (d[o["m"]] == "-1")
{
//datetime.partialDateFormat = yy;
tempDate[o["m"]] = "0";
tempDate[o["d"]] = "1";
hideMonth = true;
hideDate = true;
}else if (d[o["d"]] == "0"){
//datetime.partialDateFormat = MM-yy;
tempDate[o["d"]] = "1";
hideDate = true;
}
//var dt = getDate (tempDate);
var newDt = datetime.formatDate(format, getDate(tempDate), s, hideDate, hideMonth);
datetime.partialDateFormat = s.partialDateFormat;
return newDt;
}
//return datetime.formatPartialDate(format, d, s ,o);
},
parseValue: function (val) {
if (!val) {
innerValues = {};
}
var datValue = getArray(val ? datetime.parseDate(format, val, s) : (s.defaultValue || new Date()), !!val && !!val.getTime);
var seperator = "-";
if (format.indexOf ('/') > 0){
seperator = "/";
}
var dateArray = val.split(seperator);
switch (dateArray.length)
{
case 1: //Year Only
datValue[o["d"]] = 0;
datValue[o["m"]] = -1;
break;
case 2: //Month & Year
datValue[o["d"]] = 0;
break;
}
return datValue;
},
validate: function (dw, i, time, dir) {
//KKN
var temp = [],
hideDate = false,
hideMonth = false;
var d = inst.getArrayVal(true);
if (s.partialDate)
{
hideDate = false,
hideMonth = false;
$.each(['y', 'm', 'd', 'a', 'h', 'i', 's'], function (x, i) {
temp[o[i]] = d[o[i]];
});
if (d[o["m"]] == "-1")
{
temp[o["m"]] = "0";
temp[o["d"]] = "1";
hideMonth = true;
hideDate = true;
}else if (d[o["d"]] == "0"){
//datetime.partialDateFormat = MM-yy;
temp[o["d"]] = "1";
hideDate = true;
}
//return true;
}
else
{
var validated = getClosestValidDate(getDate(inst.getArrayVal(true)), dir),
temp = getArray(validated);
}
y = get(temp, 'y'),
m = get(temp, 'm'),
minprop = true,
maxprop = true;
$.each(['y', 'm', 'd', 'a', 'h', 'i', 's'], function (x, i) {
if (o[i] !== undefined) {
var min = mins[i],
max = maxs[i],
maxdays = 31,
val = get(temp, i),
t = $('.dw-ul', dw).eq(o[i]);
if (i == 'd') {
maxdays = s.getMaxDayOfMonth(y, m);
max = maxdays;
if (regen) {
$('.dw-li', t).each(function () {
var that = $(this),
d = that.data('val'),
w = s.getDate(y, m, d).getDay(),
str = dord.replace(/[my]/gi, '').replace(/dd/, (d < 10 ? '0' + d : d) + (s.daySuffix || '')).replace(/d/, d + (s.daySuffix || ''));
$('.dw-i', that).html(str.match(/DD/) ? str.replace(/DD/, '<span class="dw-day">' + s.dayNames[w] + '</span>') : str.replace(/D/, '<span class="dw-day">' + s.dayNamesShort[w] + '</span>'));
});
}
}
if (minprop && mind) {
min = f[i](mind);
}
if (maxprop && maxd) {
max = f[i](maxd);
}
if (i != 'y') {
var i1 = getIndex(t, min),
i2 = getIndex(t, max);
$('.dw-li', t).removeClass('dw-v').slice(i1, i2 + 1).addClass('dw-v');
if (i == 'd') { // Hide days not in month
//$('.dw-li', t).removeClass('dw-h').slice(maxdays).addClass('dw-h');
if (s.partialDate){
if (hideMonth)
{
$('.dw-li', t).removeClass('dw-h').slice(1).addClass('dw-h');
}
else{
$('.dw-li', t).removeClass('dw-h').slice(maxdays + 1).addClass('dw-h');
}
}
else
{
$('.dw-li', t).removeClass('dw-h').slice(maxdays).addClass('dw-h');
}
}
}
if (val < min) {
val = min;
}
if (val > max) {
val = max;
}
if (minprop) {
minprop = val == min;
}
if (maxprop) {
maxprop = val == max;
}
// Disable some days
if (i == 'd') {
var first = s.getDate(y, m, 1).getDay(),
idx = {};
// Set invalid indexes
validateDates(invalid, y, m, first, maxdays, idx, 1);
// Delete indexes which are valid
validateDates(valid, y, m, first, maxdays, idx, 0);
$.each(idx, function (i, v) {
if (v) {
$('.dw-li', t).eq(i).removeClass('dw-v');
}
});
}
}
});
// Invalid times
if (hasTime) {
$.each(['a', 'h', 'i', 's'], function (i, v) {
var val = get(temp, v),
d = get(temp, 'd'),
t = $('.dw-ul', dw).eq(o[v]);
if (o[v] !== undefined) {
validateTimes(invalid, i, v, temp, y, m, d, t, 0);
validateTimes(valid, i, v, temp, y, m, d, t, 1);
// Get valid value
validValues[i] = +inst.getValidCell(val, t, dir).val;
}
});
}
if (hideMonth)
{
temp[o["m"]] = "-1";
temp[o["d"]] = "0";
}else if (hideDate){
//datetime.partialDateFormat = MM-yy;
temp[o["d"]] = "0";
}
inst._tempWheelArray = temp;
}
and in mobiscroll.util.datetime.js, replace following lines
(function ($, undefined) {
var ms = $.mobiscroll;
ms.datetime = {
defaults: {
shortYearCutoff: '+10',
monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
dayNamesMin: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
amText: 'am',
pmText: 'pm',
getYear: function (d) { return d.getFullYear(); },
getMonth: function (d) { return d.getMonth(); },
getDay: function (d) { return d.getDate(); },
getDate: function (y, m, d, h, i, s, u) { return new Date(y, m, d, h || 0, i || 0, s || 0, u || 0); },
getMaxDayOfMonth: function (y, m, o) { return 32 - new Date(y, m, 32).getDate(); },
getWeekNumber: function (d) {
// Copy date so don't modify original
d = new Date(d);
d.setHours(0, 0, 0);
// Set to nearest Thursday: current date + 4 - current day number
// Make Sunday's day number 7
d.setDate(d.getDate() + 4 - (d.getDay() || 7));
// Get first day of year
var yearStart = new Date(d.getFullYear(), 0, 1);
// Calculate full weeks to nearest Thursday
return Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
}
},
/**
* Format a date into a string value with a specified format.
* #param {String} format Output format.
* #param {Date} date Date to format.
* #param {Object} [settings={}] Settings.
* #return {String} Returns the formatted date string.
*/
formatDate: function (format, date, settings, hideDate, hideMonth) {
var newformat = format;
if (!date) {
return null;
}
var s = $.extend({}, ms.datetime.defaults, settings),
look = function (m) { // Check whether a format character is doubled
var n = 0;
while (i + 1 < format.length && format.charAt(i + 1) == m) {
n++;
i++;
}
return n;
},
f1 = function (m, val, len) { // Format a number, with leading zero if necessary
var n = '' + val;
if (look(m)) {
while (n.length < len) {
n = '0' + n;
}
}
return n;
},
f2 = function (m, val, s, l) { // Format a name, short or long as requested
return (look(m) ? l[val] : s[val]);
},
f3 = function (m){
look(m);
if (newformat.indexOf('/') > 0 ){
newformat = newformat.replace( m +'/', '');
}
else
{
newformat = newformat.replace( m +'-', '');
}
newformat = newformat.replace( m, '');
if (format.charAt(i+1) == "/" ||format.charAt(i+1) == "-"){//Skip Separator
i++;
}
return '';
},
i,
year,
output = '',
literal = false;
//return s.getPartialMonth(date, format);
for (i = 0; i < format.length; i++) {
if (literal) {
if (format.charAt(i) == "'" && !look("'")) {
literal = false;
} else {
output += format.charAt(i);
}
} else {
switch (format.charAt(i)) {
case 'd':
if (hideDate)
{
output += f3('d');
}
else
{
output += f1('d', s.getDay(date), 2);
}
break;
case 'D':
if (hideDate)
{
output += f3('D');
}
else
{
output += f2('D', date.getDay(), s.dayNamesShort, s.dayNames);
}
break;
case 'o':
output += f1('o', (date.getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000, 3);
break;
case 'm':
if (hideMonth)
{
output += f3('m');
}
else
{
output += f1('m', s.getMonth(date) + 1, 2);
}
break;
case 'M':
if (hideMonth)
{
output += f3('M');
}
else
{
output += f2('M', s.getMonth(date), s.monthNamesShort, s.monthNames);
}
break;
case 'y':
year = s.getYear(date);
output += (look('y') ? year : (year % 100 < 10 ? '0' : '') + year % 100);
//output += (look('y') ? date.getFullYear() : (date.getYear() % 100 < 10 ? '0' : '') + date.getYear() % 100);
break;
case 'h':
var h = date.getHours();
output += f1('h', (h > 12 ? (h - 12) : (h === 0 ? 12 : h)), 2);
break;
case 'H':
output += f1('H', date.getHours(), 2);
break;
case 'i':
output += f1('i', date.getMinutes(), 2);
break;
case 's':
output += f1('s', date.getSeconds(), 2);
break;
case 'a':
output += date.getHours() > 11 ? s.pmText : s.amText;
break;
case 'A':
output += date.getHours() > 11 ? s.pmText.toUpperCase() : s.amText.toUpperCase();
break;
case "'":
if (look("'")) {
output += "'";
} else {
literal = true;
}
break;
default:
output += format.charAt(i);
}
}
}
ms.partialDateFormat = newformat;
return output;
},
/**
* Format a date into a string value with a specified format.
* #param {String} format Output format.
* #param {Date} date Date to format.
* #param {Object} [settings={}] Settings.
* #return {String} Returns the formatted date string.
*/
formatPartialDate: function (format, date, settings, o) {
if (!date) {
return null;
}
var tempDate = [],
hideDate = false,
hideMonth = false;
$.each(['y', 'm', 'd', 'a', 'h', 'i', 's'], function (x, i) {
tempDate[o[i]] = date[o[i]];
});
if (date[o["m"]] == "-1")
{
tempDate[o["m"]] = "0";
tempDate[o["d"]] = "1";
hideMonth = true;
hideDate = true;
}else if (date[o["d"]] == "0"){
tempDate[o["d"]] = "1";
hideDate = true;
}
var t2 = ms.getDate(tempDate);
var t1 = new Date();
//var output = ms.formatDate(format, settings.getDate(tempDate), settings, hideDate, hideMonth);
var output = ms.formatDate(format, tempDate, settings, hideDate, hideMonth);
return output;
},
/**
* Extract a date from a string value with a specified format.
* #param {String} format Input format.
* #param {String} value String to parse.
* #param {Object} [settings={}] Settings.
* #return {Date} Returns the extracted date.
*/
parseDate: function (format, value, settings) {
var s = $.extend({}, ms.datetime.defaults, settings),
def = s.defaultValue || new Date();
if (!format || !value) {
return def;
}
// If already a date object
if (value.getTime) {
return value;
}
value = (typeof value == 'object' ? value.toString() : value + '');
var seperator = "-";
if (format.indexOf ('/') > 0){
seperator = "/";
}
var dateArray = value.split(seperator);
//else (value)
var shortYearCutoff = s.shortYearCutoff,
year = s.getYear(def),
month = s.getMonth(def) + 1,
day = s.getDay(def),
doy = -1,
hours = def.getHours(),
minutes = def.getMinutes(),
seconds = 0, //def.getSeconds(),
ampm = -1,
literal = false, // Check whether a format character is doubled
lookAhead = function (match) {
var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
if (matches) {
iFormat++;
}
return matches;
},
getNumber = function (match) { // Extract a number from the string value
lookAhead(match);
var size = (match == '#' ? 14 : (match == '!' ? 20 : (match == 'y' ? 4 : (match == 'o' ? 3 : 2)))),
digits = new RegExp('^\\d{1,' + size + '}'),
num = value.substr(iValue).match(digits);
if (!num) {
return 0;
}
iValue += num[0].length;
return parseInt(num[0], 10);
},
getName = function (match, s, l) { // Extract a name from the string value and convert to an index
var names = (lookAhead(match) ? l : s),
i;
for (i = 0; i < names.length; i++) {
if (value.substr(iValue, names[i].length).toLowerCase() == names[i].toLowerCase()) {
iValue += names[i].length;
return i + 1;
}
}
return 0;
},
checkLiteral = function () {
if (value.charAt(iValue) == '/' || value.charAt(iValue) == '-')
iValue++;
},
iValue = 0,
iFormat;
for (iFormat = 0; iFormat < format.length; iFormat++) {
if (literal) {
if (format.charAt(iFormat) == "'" && !lookAhead("'")) {
literal = false;
} else {
checkLiteral();
}
} else {
switch (format.charAt(iFormat)) {
case 'd':
if (dateArray.length <= 2){
day = 1;
//iValue++;
}
else{
day = getNumber('d');
}
break;
case 'D':
getName('D', s.dayNamesShort, s.dayNames);
break;
case 'o':
doy = getNumber('o');
break;
case 'm':
if (dateArray.length == 1){
month = 1;
//iValue++;
}
else
{
month = getNumber('m');
}
break;
case 'M':
if (dateArray.length == 1){
month = 1;
//iValue++;
}
else
{
month = getName('M', s.monthNamesShort, s.monthNames);
}
break;
case 'y':
year = getNumber('y');
break;
case 'H':
hours = getNumber('H');
break;
case 'h':
hours = getNumber('h');
break;
case 'i':
minutes = getNumber('i');
break;
case 's':
seconds = getNumber('s');
break;
case 'a':
ampm = getName('a', [s.amText, s.pmText], [s.amText, s.pmText]) - 1;
break;
case 'A':
ampm = getName('A', [s.amText, s.pmText], [s.amText, s.pmText]) - 1;
break;
case "'":
if (lookAhead("'")) {
checkLiteral();
} else {
literal = true;
}
break;
default:
checkLiteral();
}
}
}
if (year < 100) {
year += new Date().getFullYear() - new Date().getFullYear() % 100 +
(year <= (typeof shortYearCutoff != 'string' ? shortYearCutoff : new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10)) ? 0 : -100);
}
if (doy > -1) {
month = 1;
day = doy;
do {
var dim = 32 - new Date(year, month - 1, 32).getDate();
if (day <= dim) {
break;
}
month++;
day -= dim;
} while (true);
}
hours = (ampm == -1) ? hours : ((ampm && hours < 12) ? (hours + 12) : (!ampm && hours == 12 ? 0 : hours));
var date = s.getDate(year, month - 1, day, hours, minutes, seconds);
if (s.getYear(date) != year || s.getMonth(date) + 1 != month || s.getDay(date) != day) {
return def; // Invalid date
}
return date;
}
};
// #deprecated since 2.11.0, backward compatibility code
// ---
ms.formatDate = ms.datetime.formatDate;
ms.parseDate = ms.datetime.parseDate;
ms.formatPartialDate = ms.datetime.formatPartialDate;
ms.getDate = ms.datetime.getDate;
ms.partialDate = true;
ms.partialDateFormat = ms.datetime.partialDateFormat; })(jQuery);
to enable a partial date picker add partialDate: true, whenever you want to enable a partial date picker.

Which part is responsible on the animation in this script?

this code is from a jquery countdown counter , I am trying to eliminate the animation (flip) as you can see in the demo, I succeed to remove the main one, but if you notice at the end when a digit is at 0 you see a quick animation back to 9, and I could not figure which part is responsible for that, this is the code again here.
/*
* jquery-countdown plugin
*
* Copyright (c) 2009 Martin Conte Mac Donell <Reflejo#gmail.com>
* Dual licensed under the MIT and GPL licenses.
* http://docs.jquery.com/License
*
*/
// Draw digits in given container
var createDigits = function(where, options) {
var counter = 0;
// Iterate each startTime digit, if it is not a digit
// we'll asume that it's a separator
var mFirstPos, sFirstPos;
// reset digits and intervals array.
digits = [];
intervals = [];
for (var i = 0; i < options.startTime.length; i++) {
if (parseInt(options.startTime[i]) >= 0) {
elem = $('<div id="cnt_' + counter + '" class="cntDigit" />').css({
height: options.digitHeight,
float: 'left',
background: 'url(\'' + options.image + '\')',
width: options.digitWidth
});
elem.current = parseInt(options.startTime[i]);
digits.push(elem);
margin(counter, -elem.current * options.digitHeight * options.digitImages);
if (options.continuous === true) {
digits[counter]._max = function() { return 9; };
} else {
// Add max digits, for example, first digit of minutes (mm) has
// a max of 5. Conditional max is used when the left digit has reach
// the max. For example second "hours" digit has a conditional max of 4
switch (options.format[i]) {
case 'h':
digits[counter]._max = function(pos, isStart) {
if (pos % 2 == 0)
return 2;
else
return (isStart) ? 3: 9;
};
break;
case 'd':
digits[counter]._max = function() { return 9; };
break;
case 'm':
digits[counter]._max = function(pos) {
if(!mFirstPos) { mFirstPos = pos; }
return pos == mFirstPos ? 9 : 5;
};
break;
case 's':
digits[counter]._max = function(pos) {
if(!sFirstPos) { sFirstPos = pos; }
return pos == sFirstPos ? 9 : 5;
};
}
}
counter += 1;
} else {
elem = $('<div class="cntSeparator"/>').css({float: 'left'})
.text(options.startTime[i]);
}
where.append(elem)
}
};
var makeMovement = function(elem, steps, isForward, options) {
// Stop any other movement over the same digit.
if (intervals[elem])
window.clearInterval(intervals[elem]);
// Move to the initial position (We force that because in chrome
// there are some scenarios where digits lost sync)
var initialPos = -(options.digitHeight * options.digitImages *
digits[elem].current);
margin(elem, initialPos);
digits[elem].current = digits[elem].current + ((isForward) ? steps: -steps);
var x = 0;
intervals[elem] = setInterval(function() {
if (x++ === options.digitImages * steps) {
window.clearInterval(intervals[elem]);
delete intervals[elem];
return;
}
var diff = isForward ? -options.digitHeight: options.digitHeight;
margin(elem, initialPos + (x * diff));
}, options.stepTime / steps);
};
// Set or get element margin
var margin = function(elem, val) {
if (val !== undefined) {
digits[elem].margin = val;
return digits[elem].css({'backgroundPosition': '0 ' + val + 'px'});
}
return digits[elem].margin || 0;
};
// Makes the movement. This is done by "digitImages" steps.
var moveDigit = function(elem, options) {
if (digits[elem].current == 0) {
// Is there still time left?
if (elem > 0) {
var isStart = (digits[elem - 1].current == 0);
makeMovement(elem, digits[elem]._max(elem, isStart), true, options);
moveDigit(elem - 1, options);
} else { // That condition means that we reach the end! 00:00.
for (var i = 0; i < digits.length; i++) {
clearInterval(intervals[i]);
clearInterval(intervals.main);
margin(i, 0);
}
options.timerEnd();
}
return;
}
makeMovement(elem, 1, false, options);
};
// parses a date of the form hh:mm:ss, for example, where
// ... precision is the same as the format.
var parseRelativeDate = function(form, options) {
// give the date the values of now by default
var now = new Date();
var d = now.getDate();
var m = now.getMonth() + 1;
var y = now.getFullYear();
var h = now.getHours(), mm, s;
// read in components and render based on format
var format = options.format;
var parts = form.split(':');
if( format.indexOf('dd') == 0 ) {
d = parts[0];
parts = parts.slice(1);
format = format.substr(3);
}
if( format.indexOf('hh') == 0 ) {
h = parts[0];
parts = parts.slice(1);
format = format.substr(3);
}
if( format.indexOf('mm') == 0 ) {
mm = parts[0];
parts = parts.slice(1);
format = format.substr(3);
}
if( format.indexOf('ss') == 0 ) {
s = parts[0];
parts = parts.slice(1);
format = format.substr(3);
}
// return our constructed date object
return new Date([m, d, y].join('/') + ' ' + [h, mm, s].map(pad).join(':') + ' GMT-0900');
};
// convert a date object to the format specified
var formatCompute = function(d, options) {
var format = options.format;
var parse = {
d: d.getUTCDate() - 1,
h: d.getUTCHours(),
m: d.getUTCMinutes(),
s: d.getUTCSeconds()
};
return format.replace(/(dd|hh|mm|ss)/g, function($0, form) {
return pad(parse[form[0]]);
});
};
// add leading zeros
var pad = function(x){return (1e15+""+x).slice(-2)};
var digits = [];
var intervals = [];
jQuery.fn.countdown = function(userOptions) {
// Default options
var options = {
stepTime: 60,
// startTime and format MUST follow the same format.
// also you cannot specify a format unordered (e.g. hh:ss:mm is wrong)
format: "dd:hh:mm:ss",
startTime: "01:12:32:55",
digitImages: 6,
digitWidth: 67,
digitHeight: 90,
timerEnd: function(){},
image: "digits.png",
continuous: false
};
$.extend(options, userOptions);
// if an endTime is provided...
if( userOptions.endTime ) {
// calculate the difference between endTime and present time
var endDate = userOptions.endTime instanceof Date ? userOptions.endTime : parseRelativeDate(userOptions.endTime, options);
var diff = endDate.getTime() - (new Date()).getTime();
// and set that as the startTime
userOptions.startTime = formatCompute(new Date(diff), options);
delete userOptions.endTime;
}
$.extend(options, userOptions);
if (this.length) {
clearInterval(intervals.main);
createDigits(this, options);
intervals.main = setInterval(function(){ moveDigit(digits.length - 1, options); },
1000);
}
};
Is this:
setInterval(function(){ moveDigit(digits.length - 1, options); },
1000);
your animation has a duration of one second and this runs the function moveDigit()

Check SSL certificate Expiration Date

I need to check local computer's SSl certificate expiry DATE and compare it with current date and notify user that his/her certificate is going to expire in X days. All this I need to do in JavaScript.
Your certificate should look like this:
-----BEGIN CERTIFICATE-----
MIIGoDCCBIigAwIBAgIJAICRY3cWdgK1MA0GCSqGSIb3DQEBCwUAMIGeMQswCQYD
VQQGEwJCRzERMA8GA1UECAwIQnVsZ2FyaWExDjAMBgNVBAcMBVNvZmlhMQ8wDQYD
..
ud5Nja8+xycA/Jk7bSvB1jJjpc3oL0G9j0HOcxqQKd4e1IQXuss5V7FnQxSOVCq4
GVK0r3LkAxtl/EGmQC1DRlHAUWg=
-----END CERTIFICATE-----
You need to strip the -----BEGIN CERTIFICATE----- header and the -----END CERTIFICATE----- trailer from the certificate data, the rest is a Base64 encoded byte array.
You need to decode it to an array of bytes (in this example represented as array of number, where each number represents a byte - number between 0 and 255 inclusive).
That byte array is a DER encoded ASN.1 structure as defined in RFC-5280.
The below example will parse the content of the certificate after the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- traile has already been stripped.
Usage:
var pem =
"MIIGijCCBXKgAwIBAgIQEpI/gkvDS6idH2m2Zwn7ZzANBgkqhkiG9w0BAQsFADCB\n"
...
+"VQ+o34uWo7z19I8eXWSXN6P+Uj1OvHn8zNM1G/ddjQXBwMvzwwJEdVBhdK1uQw==\n";
var bytes = fromBase64(pem);
var validity = getValidity(bytes);
var notBefore = validity.notBefore;
var notAfter = validity.notAfter;
var now = new Date();
if ( notBefore.getTime() < now.getTime()
&& now.getTime() < notAfter.getTime())
{
// Certificate is withing its validity days
} else {
// Certificate is either not yet valid or has already expired.
}
Parsing:
var TYPE_INTEGER = 0x02;
var TYPE_SEQUENCE = 0x10;
var TYPE_UTC_TIME = 0x17;
var TYPE_GENERALIZED_TIME = 0x18;
function subArray(original, start, end) {
var subArr = [];
var index = 0;
for (var i = start; i < end; i++) {
subArr[index++] = original[i];
}
return subArr;
}
function getDigit(d) {
switch (d) {
default:
case 0x30: case '0': return 0;
case 0x31: case '1': return 1;
case 0x32: case '2': return 2;
case 0x33: case '3': return 3;
case 0x34: case '4': return 4;
case 0x35: case '5': return 5;
case 0x36: case '6': return 6;
case 0x37: case '7': return 7;
case 0x38: case '8': return 8;
case 0x39: case '9': return 9;
}
}
function enterTag(bytes, start, requiredTypes, name) {
if (start + 1 > bytes.length) {
throw new Error("Too short certificate input");
}
var typeByte = bytes[start ] & 0x0FF;
var lenByte = bytes[start +1] & 0x0FF;
var type = typeByte & 0x1F;
var len = lenByte;
var index = start + 2;
if (requiredTypes.length > 0 && requiredTypes.indexOf(type) == -1) {
throw new Error("Invalid type");
}
var lengthOfLength = 0;
if (len > 0x07F) {
lengthOfLength = len & 0x7F;
len = 0;
for (var i =0; i < lengthOfLength && index < bytes.length; i++) {
len = (len << 8 ) | (bytes[index] & 0x00FF);
index++;
}
}
if (index >= bytes.length) {
throw new Error("Too short certificate input");
}
return {index: index, type: type, length: len}
}
function processTag(bytes, start, requiredTypes, name) {
var result = enterTag(bytes, start, requiredTypes, name);
var index = result.index + result.length;
if (index >= bytes.length) {
throw new Error("Too short certificate input");
}
var valueStart = result.index;
var valueEnd = result.index + result.length;
var value = subArray(bytes, valueStart, valueEnd);
return { index: index, type: result.type, value: value};
}
function readDate(bytes, start, name) {
var date = new Date();
var result = processTag(bytes, start,
[TYPE_UTC_TIME, TYPE_GENERALIZED_TIME], name);
var index, year;
if (result.type == 0x17) { // UTCTime
if (result.value.length < 12) {
throw new Error("Invalid type");
}
var yearHigh = getDigit(result.value[0]);
var yearLow = getDigit(result.value[1]);
var year2Digits = (yearHigh * 10 ) + (yearLow)
if (year2Digits >= 50) {
year = 1900 + year2Digits;
} else {
year = 2000 + year2Digits;
}
index = 2;
} else if (result.type = 0x18) { // GeneralizedTime
if (result.value.length < 14) {
throw new Error("Invalid type");
}
var year1 = getDigit(result.value[0]);
var year2 = getDigit(result.value[1]);
var year3 = getDigit(result.value[2]);
var year4 = getDigit(result.value[3]);
year = (year1 * 1000) + (year2 * 100) + (year3*10) + year4;
index = 4;
}
var monthHigh = getDigit(result.value[index++]);
var monthLow = getDigit(result.value[index++]);
var dayHigh = getDigit(result.value[index++]);
var dayhLow = getDigit(result.value[index++]);
var hourHigh = getDigit(result.value[index++]);
var hourLow = getDigit(result.value[index++]);
var minuteHigh = getDigit(result.value[index++]);
var minuteLow = getDigit(result.value[index++]);
var secondHigh = getDigit(result.value[index++]);
var secondLow = getDigit(result.value[index]);
var month = (monthHigh * 10) + monthLow;
var day = (dayHigh * 10) + dayhLow;
var hour = (hourHigh * 10) + hourLow;
var minute = (minuteHigh * 10) + minuteLow;
var second = (secondHigh * 10) + secondLow;
if (month < 1 || month > 12) {
throw new Error("Invalid month");
}
if (day < 1 || day > 31) {
throw new Error("Invalid day");
}
if (hour < 0 || hour > 24) {
throw new Error("Invalid hour");
}
if (minute < 0 || minute > 59) {
throw new Error("Invalid minute");
}
if (second < 0 || second > 59) {
throw new Error("Invalid second ");
}
date.setUTCFullYear(year);
date.setUTCMonth(month-1);
date.setUTCDate(day);
date.setUTCHours(hour);
date.setUTCMinutes(minute);
date.setUTCSeconds(second);
return {
index: result.index,
type: result.type,
length: result.length,
value: result.value,
date: date
};
}
function getValidity(bytes) {
if (bytes == null || bytes.length <= 0) {
return null;
}
var index = 0;
index = enterTag(bytes, index, [TYPE_SEQUENCE], "Certificate").index;
index = enterTag(bytes, index, [TYPE_SEQUENCE], "TBSCertificate").index;
var result = processTag(bytes, index, [0x00, 0x02],
"Version or SerialNumber");
if (result.type == 0) {
index = result.index;
result = processTag(bytes, index, [TYPE_INTEGER], "SerialNumber")
}
index = result.index;
result = processTag(bytes, index, [TYPE_SEQUENCE],
"Signature AlgorithmIdentifier");
index = result.index;
result = processTag(bytes, index, [], "Issuer Name");
index = result.index;
index = enterTag(bytes, index, [TYPE_SEQUENCE], "Validity").index;
result = readDate(bytes, index, "Not Before");
var notBefore = result.date;
index = result.index;
result = readDate(bytes, index, "Not After");
var notAfter = result.date;
return {notBefore: notBefore, notAfter: notAfter};
}
function getNextBase64Chr(str, index, equalSignReceived, alpha) {
var chr = null;
var code = 0;
var padding = equalSignReceived;
while (index < str.length) {
chr = str.charAt(index);
if (chr == " " || chr == "\r" || chr == "\n" || chr == "\t") {
index++;
continue;
}
if (chr == "=") {
padding = true;
} else {
if (equalSignReceived) {
throw new Error("Invalid Base64 Endcoding.");
}
code = alpha.indexOf(chr);
if (code == -1) {
throw new Error("Invalid Base64 Encoding .");
}
}
break;
}
return { character: chr, code: code, padding: padding, nextIndex: ++index};
}
function fromBase64(str) {
var alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var value = [];
var index = 0;
var destIndex = 0;
var padding = false;
while (true) {
var first = getNextBase64Chr(str, index, padding, alpha);
var second = getNextBase64Chr(str, first .nextIndex, first .padding, alpha);
var third = getNextBase64Chr(str, second.nextIndex, second.padding, alpha);
var fourth = getNextBase64Chr(str, third .nextIndex, third .padding, alpha);
index = fourth.nextIndex;
padding = fourth.padding;
// ffffffss sssstttt ttffffff
var base64_first = first.code == null ? 0 : first.code;
var base64_second = second.code == null ? 0 : second.code;
var base64_third = third.code == null ? 0 : third.code;
var base64_fourth = fourth.code == null ? 0 : fourth.code;
var a = (( base64_first << 2 ) & 0xFC ) | ((base64_second >> 4) & 0x03);
var b = (( base64_second << 4 ) & 0xF0 ) | ((base64_third >> 2) & 0x0F);
var c = (( base64_third << 6 ) & 0xC0 ) | ((base64_fourth >> 0) & 0x3F);
value [destIndex++] = a;
if (!third.padding) {
value [destIndex++] = b;
} else {
break;
}
if (!fourth.padding) {
value [destIndex++] = c;
} else {
break;
}
if (index >= str.length) {
break;
}
}
return value;
}
Used resources:
A Layman's Guide to a Subset of ASN.1, BER, and DER
Encoding of ASN.1 UTC Time and GeneralizedTime
RFC-5280
The only available option so far is forge - https://github.com/digitalbazaar/forge/blob/master/README.md or some sort of custom extension for the client.
Client side does not know nothing about other than DOM elements thus it cannot inspect SSL layer.
More on this Within a web browser, is it possible for JavaScript to obtain information about the SSL Certificate being used for the current page?
This is not possible from the client, but you could do something like this with node / shell combo.
Assuming you have access to the server the cert is running on you could do something like this on the host:
var execSync = require('child_process').execSync
var cmd = "echo | openssl s_client -connect 127.0.0.1:443 2>/dev/null | openssl x509 -noout -dates"
var stdout = execSync(cmd).toString()
// "notBefore=Feb 16 15:33:00 2017 GMT\nnotAfter=May 17 15:33:00 2017 GMT"
From there you could parse the dates reported by stdout and write them to a public resource or json file so they are readable from the client, and from there do your date comparison.

Categories

Resources