Get values from inside a nested function using this - javascript

I have a piece of code which has two nested functions inside a main one.
How do I retrieve the nested functions using this keyword? Is it possible?
I have tried times().present() and new times().present() , none of them seem to work and return undefined.
I have found similar examples on w3School but cant seem to implement it in this case.
Thanks in advance.
function times() {
var timingObj = function() {
this.present = currentTime;
this.past = pastTime;
};
var currentTime = function() {
var hourMin = new Date().getHours() + ":" + new Date().getMinutes();
return hourMin;
};
var pastTime = function() {
if (new Date().getDay() == 5) {
return "07:40"
} else {
return "16:30"
}
};
return timingObj;
}
console.log(times().present());
//console.log(new times().present());

function times() {
var currentTime = function() {
var hourMin = new Date().getHours() + ":" + new Date().getMinutes();
return hourMin;
};
var pastTime = function() {
if (new Date().getDay() == 5) {
return "07:40"
} else {
return "16:30"
}
};
return {
present: currentTime,
past: pastTime
};
}
console.log(times().present())

You can use Method call().
function times() {
var timingObj = function() {
this.present = currentTime;
this.past = pastTime;
};
var currentTime = function() {
var hourMin = new Date().getHours() + ":" + new Date().getMinutes();
return hourMin;
};
var pastTime = function() {
if (new Date().getDay() == 5) {
return "07:40"
} else {
return "16:30"
}
};
return timingObj;
}
times().call(null);
console.log(present(), past());
OR define them as prototype
function times() {
var timingObj = function() {
this.present = timingObj.prototype.currentTime;
this.past = timingObj.prototype.pastTime;
};
timingObj.prototype.currentTime = function() {
return new Date().getHours() + ":" + new Date().getMinutes();
};
timingObj.prototype.pastTime = function() {
return new Date().getDay() === 5 ? "07:40" : "16:30";
};
return timingObj;
}
console.log(times().prototype.currentTime(), times().prototype.pastTime());
//times().call(null);
//console.log(present(), past());

Related

Have I missed something in JS?

I have a js coding here, but it doesnt work. What is wrong with it?
Am i missing any {}? or what have I written wrong?
window.addEventListener("load", showPage);
function showPage()
console.log("showPage");
document.getElementById('horizontal1').style.animation = 'mymoveHor 1s';
document.getElementById('horizontal2').style.animation = 'mymoveHor 0.5s';
document.getElementById('vertical1').style.animation = 'mymoveVer 1.5s';
var dfade = document.getElementById("portfolio1");
function fadeIn(dfade, time) {
dfade.style.opacity = 0;
var last = +new Date();
var tick = function () {
dfade.style.opacity = +dfade.style.opacity + (new Date() - last) / time;
last = +new Date();
if (+dfade.style.opacity < 1) {
(window.requestAnimationFrame && requestAnimationFrame(tick)) || setTimeout(tick, 16);
}
};
tick();
}
fadeIn(dfade, 3000);
please help me out...
Just add "{" and "}" to your showPage function
function showPage() {
console.log("showPage");
document.getElementById('horizontal1').style.animation = 'mymoveHor 1s';
document.getElementById('horizontal2').style.animation = 'mymoveHor 0.5s';
document.getElementById('vertical1').style.animation = 'mymoveVer 1.5s';
}
function fadeIn(time) {
var dfade = document.getElementById("portfolio1");
dfade.style.opacity = 0;
var last = +new Date();
var tick = function () {
dfade.style.opacity = +dfade.style.opacity + (new Date() - last) / time;
last = +new Date();
if (+dfade.style.opacity < 1) {
(window.requestAnimationFrame && requestAnimationFrame(tick)) || setTimeout(tick, 16);
}
};
tick();
}
window.addEventListener("load", showPage);
fadeIn(dfade, 3000);

Putting getLocation() function and showPosition() illegal constructor

I am attempting to just put my getLocation() function and showPosition() function in a different file called location.js. But whenever I try to call the file from main.js it just says it's an illegal constructor
Stopwatch.js:
function Stopwatch(elem) {
var time = 0;
var offset;
var interval;
function update() {
if (this.isOn) {
time += delta();
}
elem.textContent = timeFormatter(time);
}
function delta() {
var now = Date.now();
var timePassed = now - offset;
offset = now;
return timePassed;
}
function timeFormatter(time) {
time = new Date(time);
var minutes = time.getMinutes().toString();
var seconds = time.getSeconds().toString();
var milliseconds = time.getMilliseconds().toString();
if (minutes.length < 2) {
minutes = '0' + minutes;
}
if (seconds.length < 2) {
seconds = '0' + seconds;
}
while (milliseconds.length < 3) {
milliseconds = '0' + milliseconds;
}
return minutes + ' : ' + seconds + ' . ' + milliseconds;
}
this.start = function() {
interval = setInterval(update.bind(this), 10);
offset = Date.now();
this.isOn = true;
};
this.stop = function() {
clearInterval(interval);
interval = null;
this.isOn = false;
};
this.reset = function() {
time = 0;
update();
};
this.getLocation = function() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition);
} else {
console.log("Not supported by this browser.");
}
};
function showPosition(position) {
console.log("Latitude: " + position.coords.latitude);
console.log("Longitude: " + position.coords.longitude);
};
this.isOn = false;
}
Main.js:
var timer = document.getElementById('timer');
var toggleBtn = document.getElementById('toggle');
var resetBtn = document.getElementById('reset');
var loc = new Location(); //ILLEGAL CONSTRUCTOR?!
var watch = new Stopwatch(timer);
function start() {
toggleBtn.textContent = 'Stop';
watch.getLocation();
watch.start();
}
function stop() {
toggleBtn.textContent = 'Start';
watch.stop();
}
toggleBtn.addEventListener('click', function() {
watch.isOn ? stop() : start();
});
resetBtn.addEventListener('click', function() {
watch.reset();
});
location.js: this is where I want the getLocation to go from stopwatch
function Location(){
this.getLocation = function() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition);
} else {
console.log("Not supported by this browser.");
}
};
function showPosition(position) {
console.log("Latitude: " + position.coords.latitude);
console.log("Longitude: " + position.coords.longitude);
};
}

Javascript not able to make common functions for a prototype

I am trying to make timer in javascript using a prototype. Each time a new timer is created, a object of prototype is created. There are methods to increase time and print each second. The whole code snippet is as follows:
function Timer(elem) {
this.interval = null;
this.currentTime = {
sec: 0,
min: 0,
hr: 0
};
this.elem = elem;
};
Timer.prototype.start = function() {
var self = this;
if (!self.interval) {
self.interval = setInterval(update, 1000);
}
function update() {
incrementTime();
render();
}
function render() {
self.elem.innerText = getPrintableTime();
}
function incrementTime() {
self.currentTime["min"] += Math.floor((++self.currentTime["sec"]) / 60);
self.currentTime["hr"] += Math.floor(self.currentTime["min"] / 60);
self.currentTime["sec"] = self.currentTime["sec"] % 60;
self.currentTime["min"] = self.currentTime["min"] % 60;
}
function getPrintableTime() {
var text = getTwoDigitNumber(self.currentTime["hr"]) + ":" + getTwoDigitNumber(self.currentTime["min"]) + ":" + getTwoDigitNumber(self.currentTime["sec"]);
return text;
}
function getTwoDigitNumber(number) {
if (number > 9) {
return "" + number;
} else {
return "0" + number;
}
}
};
module.exports = Timer;
I have all methods in start function. The problem is that for each new object of Timer, new space for each method will be used which is very inefficient. But when I try to put methods outside of start function, they lose access to self variable. You can see that there is setInterval function used which will be calling these methods per second. I cannot use this also as this will be instance of Window in subsequent calls.
How can I solve this situation by only keeping one instance of all the interior methods?
You don't need to have all methods in the start function. Yes, for each new Timer instance, new space for each function will be used, but that is necessary when you want to work with setInterval as you need a function which closes over the instance. However, you need only one such closure, the other methods can be standard prototype methods.
function getTwoDigitNumber(number) {
return (number > 9 ? "" : "0") + number;
}
function Timer(elem) {
this.interval = null;
this.currentTime = {
sec: 0,
min: 0,
hr: 0
};
this.elem = elem;
};
Timer.prototype.start = function() {
var self = this;
if (!this.interval) {
this.interval = setInterval(function update() {
self.incrementTime();
self.render();
}, 1000);
}
};
Timer.prototype.render() {
this.elem.innerText = this.getPrintableTime();
};
Timer.prototype.incrementTime = function() {
this.currentTime.sec += 1;
this.currentTime.min += Math.floor(this.currentTime.sec / 60);
this.currentTime.hr += Math.floor(this.currentTime.min / 60);
this.currentTime.sec = this.currentTime.sec % 60;
this.currentTime.min = this.currentTime.min % 60;
};
Timer.prototype.getPrintableTime = function() {
var text = getTwoDigitNumber(this.currentTime.hr) + ":"
+ getTwoDigitNumber(this.currentTime.min) + ":"
+ getTwoDigitNumber(self.currentTime.sec);
return text;
};
module.exports = Timer;
Btw, regarding your incrementTime pattern, you should have a look at How to create an accurate timer in javascript?.
You can use apply to use functions defined outside of prototype with correct this context.
function Timer(elem) {
this.interval = null;
this.currentTime = {
sec: 0,
min: 0,
hr: 0
};
this.elem = elem;
};
function update() {
incrementTime.apply(this);
render.apply(this);
}
function render() {
this.elem.innerText = getPrintableTime.apply(this);
}
function incrementTime() {
this.currentTime["min"] += Math.floor((++this.currentTime["sec"]) / 60);
this.currentTime["hr"] += Math.floor(this.currentTime["min"] / 60);
this.currentTime["sec"] = this.currentTime["sec"] % 60;
this.currentTime["min"] = this.currentTime["min"] % 60;
}
function getPrintableTime() {
var text = getTwoDigitNumber(this.currentTime["hr"]) + ":" + getTwoDigitNumber(this.currentTime["min"]) + ":" + getTwoDigitNumber(this.currentTime["sec"]);
return text;
}
function getTwoDigitNumber(number) {
if (number > 9) {
return "" + number;
} else {
return "0" + number;
}
}
Timer.prototype.start = function() {
var self = this;
if (!self.interval) {
self.interval = setInterval(function() {
update.apply(self);
}, 1000);
}
};
document.addEventListener('DOMContentLoaded', function() {
var timer = new Timer(document.getElementById('timer'));
timer.start();
}, false);
<div id="timer"></div>
If I understand correctly, you're wanting to only create one interval.
One possible solution would be to create a static method and variable to manage the setInterval. I would note that while this may be more performance friendly, the timers will always start and run on the same count...not from the moment each timer is created. (See example)
Of course, you could capture the current timestamp and calculate the elapsed time from there. But, that's another thread ;)
function Timer(elem) {
this.interval = null;
this.currentTime = {
sec: 0,
min: 0,
hr: 0
};
this.elem = elem;
};
Timer.subscribe = function(timer) {
Timer.subscribers = Timer.subscribers || [];
if (Timer.subscribers.indexOf(timer) === -1) {
Timer.subscribers.push(timer);
timer.update.call(timer);
}
Timer.checkInterval();
};
Timer.unsubscribe = function(timer) {
Timer.subscribers = Timer.subscribers || [];
if (Timer.subscribers.indexOf(timer) !== -1) {
Timer.subscribers.splice(Timer.subscribers.indexOf(timer), 1);
}
Timer.checkInterval();
};
Timer.checkInterval = function() {
if (!Timer.interval && Timer.subscribers.length > 0) {
Timer.interval = setInterval(function() {
Timer.subscribers.forEach(function(item) {
item.update.call(item);
});
}, 1000);
} else if (Timer.interval && Timer.subscribers.length === 0) {
clearInterval(Timer.interval);
Timer.interval = null;
}
};
Timer.prototype = {
start: function() {
Timer.subscribe(this);
},
stop: function() {
Timer.unsubscribe(this);
},
update: function() {
this.incrementTime();
this.render();
},
incrementTime: function() {
this.currentTime["min"] += Math.floor((++this.currentTime["sec"]) / 60);
this.currentTime["hr"] += Math.floor(this.currentTime["min"] / 60);
this.currentTime["sec"] = this.currentTime["sec"] % 60;
this.currentTime["min"] = this.currentTime["min"] % 60;
},
render: function() {
var self = this;
function getPrintableTime() {
var text = getTwoDigitNumber(self.currentTime["hr"]) + ":" + getTwoDigitNumber(self.currentTime["min"]) + ":" + getTwoDigitNumber(self.currentTime["sec"]);
return text;
}
function getTwoDigitNumber(number) {
if (number > 9) {
return "" + number;
} else {
return "0" + number;
}
}
this.elem.innerText = getPrintableTime();
}
};
/**
*
*/
var timers = document.getElementById('timers');
function addTimer() {
var el = document.createElement('div');
var tmr = document.createElement('span');
var btn = document.createElement('button');
var t = new Timer(tmr);
btn.innerText = 'Stop';
btn.onclick = function() {
t.stop();
};
el.appendChild(tmr);
el.appendChild(btn);
timers.appendChild(el);
t.start();
};
<div id="timers"></div>
<button onclick="addTimer()">Add Timer</button>

Console displaying information of 4 objects when there is only 1 object in my array

I have the following code in angularjs:
TimeSlotsModel.all()
.then(function (result) {
vm.data = result.data.data;
var events = [];
angular.forEach(vm.data, function(value,key) {
var eventName = value.name;
var startDate = new Date(value.startDate);
var endDate = new Date(value.endDate);
var selectedStartingTime =new Date(value.startTime * 1000 );
var selectedEndingTime = new Date(value.endTime * 1000);
//timing is not right, needs fixing
startTime = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate(),selectedStartingTime.getHours(), selectedStartingTime.getUTCMinutes());
endTime = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate(),selectedEndingTime.getUTCHours(), selectedEndingTime.getUTCMinutes());
// console.log(startTime);
events.push({
title: 'Event -' + eventName,
startTime: startTime,
endTime: endTime,
allDay: false
});
console.log(eventName);
console.log(events);
// console.log(value);
//value is the object!!
})
return events;
$scope.$broadcast('eventSourceChanged',$scope.eventSource);
})
}
Everytime the forEach loop runs through my array of objects , vm.data, the console prints this:
My questions are :
1) Why are the details of the 4 objects being printed? Does it mean that for every object in the array it contains 4 other objects?
2) Is each object pushed to the events[ ] properly?
3) If the answer to question 2 is no, what should I do to resolve it?
EDIT: Updated code to using promise to return events array:
//Calendar Controller
.controller('CalendarCtrl', function ($scope,TimeSlotsModel,$rootScope,$q) {
var vm = this;
function goToBackand() {
window.location = 'http://docs.backand.com';
}
function getAll() {
TimeSlotsModel.all()
.then(function (result) {
vm.data = result.data.data;
});
}
function clearData(){
vm.data = null;
}
function create(object) {
TimeSlotsModel.create(object)
.then(function (result) {
cancelCreate();
getAll();
});
}
function update(object) {
TimeSlotsModel.update(object.id, object)
.then(function (result) {
cancelEditing();
getAll();
});
}
function deleteObject(id) {
TimeSlotsModel.delete(id)
.then(function (result) {
cancelEditing();
getAll();
});
}
function initCreateForm() {
vm.newObject = {name: '', description: ''};
}
function setEdited(object) {
vm.edited = angular.copy(object);
vm.isEditing = true;
}
function isCurrent(id) {
return vm.edited !== null && vm.edited.id === id;
}
function cancelEditing() {
vm.edited = null;
vm.isEditing = false;
}
function cancelCreate() {
initCreateForm();
vm.isCreating = false;
}
// initialising the various methods
vm.objects = [];
vm.edited = null;
vm.isEditing = false;
vm.isCreating = false;
vm.getAll = getAll;
vm.create = create;
vm.update = update;
vm.delete = deleteObject;
vm.setEdited = setEdited;
vm.isCurrent = isCurrent;
vm.cancelEditing = cancelEditing;
vm.cancelCreate = cancelCreate;
vm.goToBackand = goToBackand;
vm.isAuthorized = false;
//rootScope refers to the universal scope, .$on is a receiver for the
//message 'authorized'
$rootScope.$on('authorized', function () {
vm.isAuthorized = true;
getAll();
});
$rootScope.$on('logout', function () {
clearData();
});
if(!vm.isAuthorized){
$rootScope.$broadcast('logout');
}
initCreateForm();
getAll();
$scope.calendar = {};
$scope.changeMode = function (mode) {
$scope.calendar.mode = mode;
};
$scope.loadEvents = function () {
$scope.calendar.eventSource = getEvents();
$scope.$broadcast('eventSourceChanged',$scope.eventSource);
};
$scope.onEventSelected = function (event) {
console.log('Event selected:' + event.startTime + '-' + event.endTime + ',' + event.title);
};
$scope.onViewTitleChanged = function (title) {
$scope.viewTitle = title;
};
$scope.today = function () {
$scope.calendar.currentDate = new Date();
};
$scope.isToday = function () {
var today = new Date(),
currentCalendarDate = new Date($scope.calendar.currentDate);
today.setHours(0, 0, 0, 0);
currentCalendarDate.setHours(0, 0, 0, 0);
return today.getTime() === currentCalendarDate.getTime();
};
$scope.onTimeSelected = function (selectedTime) {
console.log('Selected time: ' + selectedTime);
};
function getEvents(object){
var deferred = $q.defer();
TimeSlotsModel.all()
.then(function (result) {
vm.data = result.data.data;
var events = [];
angular.forEach(vm.data, function(value,key) {
var eventName = value.name;
var startDate = new Date(value.startDate);
var endDate = new Date(value.endDate);
var selectedStartingTime = new Date(value.startTime * 1000 );
var selectedEndingTime = new Date(value.endTime * 1000);
//timing is not right, needs fixing
startTime = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate(),selectedStartingTime.getHours(), selectedStartingTime.getUTCMinutes());
endTime = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate(),selectedEndingTime.getUTCHours(), selectedEndingTime.getUTCMinutes());
// console.log(startTime);
events.push({
title: 'Event -' + eventName,
startTime: startTime,
endTime: endTime,
allDay: false
});
// console.log(eventName);
// console.log(events);
// console.log(value);
// console.log(key);
// console.log(value);
//value is the object!!
})
deferred.resolve(events);
// return events;
})
return deferred.promise;
console.log(deferred.promise);
}
[!Removed old answer]
I believe James is right here the console might be the culprit, I just reproduced the same behaviour.

Custom function be called instead of getfullyear() in java script

I want my custom function be called instead of getfullyear() in java script how can I do this ? The point is to change a gregorian calendar to jallali in jomsocial.
for example when I write this
d=new Date(b.year,b.month,b.date+a);c.set("highlight",JalaliDate[d.getFullYear(),d.getMonth(),d.getDate()]
The result will be 2014/16/7, I want it to became 1393/6/25
This is the function that I want to be called
http://www.farsiweb.info/jalali/jalali.js
or
jQuery(function($){
$.datepicker.regional['fa'] = {
calendar: JalaliDate,
closeText: 'بستن',
prevText: 'قبل',
nextText: 'بعد',
currentText: 'امروز',
monthNames: ['فروردین','اردیبهشت','خرداد','تیر','مرداد','شهریور','مهر','آبان','آذر','دی','بهمن','اسفند'],
monthNamesShort: ['فروردین','اردیبهشت','خرداد','تیر','مرداد','شهریور','مهر','آبان','آذر','دی','بهمن','اسفند'],
dayNames: ['یکشنبه', 'دوشنبه', 'سه شنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'],
dayNamesShort: ['یک', 'دو', 'سه', 'چهار', 'پنج', 'جمعه', 'شنبه'],
dayNamesMin: ['ی','د','س','چ','پ','ج','ش'],
weekHeader: 'ه',
dateFormat: 'dd/mm/yy',
firstDay: 6,
isRTL: true,
showMonthAfterYear: false,
yearSuffix: '',
calculateWeek: function(date) {
var checkDate = new JalaliDate(date.getFullYear(), date.getMonth(), date.getDate() + (date.getDay() || 7) - 3);
return Math.floor(Math.round((checkDate.getTime() - new JalaliDate(checkDate.getFullYear(), 0, 1).getTime()) / 86400000) / 7) + 1;
}};
$.datepicker.setDefaults($.datepicker.regional['fa']); });
function JalaliDate(p0, p1, p2) {
var gregorianDate;
var jalaliDate;
if (!isNaN(parseInt(p0)) && !isNaN(parseInt(p1)) && !isNaN(parseInt(p2))) {
var g = jalali_to_gregorian([parseInt(p0, 10), parseInt(p1, 10), parseInt(p2, 10)]);
setFullDate(new Date(g[0], g[1], g[2]));
} else {
setFullDate(p0);
}
function jalali_to_gregorian(d) {
var adjustDay = 0;
if(d[1]<0){
adjustDay = leap_persian(d[0]-1)? 30: 29;
d[1]++;
}
var gregorian = jd_to_gregorian(persian_to_jd(d[0], d[1] + 1, d[2])-adjustDay);
gregorian[1]--;
return gregorian;
}
function gregorian_to_jalali(d) {
var jalali = jd_to_persian(gregorian_to_jd(d[0], d[1] + 1, d[2]));
jalali[1]--;
return jalali;
}
function setFullDate(date) {
if (date && date.getGregorianDate) date = date.getGregorianDate();
gregorianDate = new Date(date);
gregorianDate.setHours(gregorianDate.getHours() > 12 ? gregorianDate.getHours() + 2 : 0)
if (!gregorianDate || gregorianDate == 'Invalid Date' || isNaN(gregorianDate || !gregorianDate.getDate())) {
gregorianDate = new Date();
}
jalaliDate = gregorian_to_jalali([
gregorianDate.getFullYear(),
gregorianDate.getMonth(),
gregorianDate.getDate()]);
return this;
}
this.getGregorianDate = function() { return gregorianDate; }
this.setFullDate = setFullDate;
this.setMonth = function(e) {
jalaliDate[1] = e;
var g = jalali_to_gregorian(jalaliDate);
gregorianDate = new Date(g[0], g[1], g[2]);
jalaliDate = gregorian_to_jalali([g[0], g[1], g[2]]);
}
this.setDate = function(e) {
jalaliDate[2] = e;
var g = jalali_to_gregorian(jalaliDate);
gregorianDate = new Date(g[0], g[1], g[2]);
jalaliDate = gregorian_to_jalali([g[0], g[1], g[2]]);
};
this.getFullYear = function() { return jalaliDate[0]; };
this.getMonth = function() { return jalaliDate[1]; };
this.getDate = function() { return jalaliDate[2]; };
this.toString = function() { return jalaliDate.join(',').toString(); };
this.getDay = function() { return gregorianDate.getDay(); };
this.getHours = function() { return gregorianDate.getHours(); };
this.getMinutes = function() { return gregorianDate.getMinutes(); };
this.getSeconds = function() { return gregorianDate.getSeconds(); };
this.getTime = function() { return gregorianDate.getTime(); };
this.getTimeZoneOffset = function() { return gregorianDate.getTimeZoneOffset(); };
this.getYear = function() { return jalaliDate[0] % 100; };
this.setHours = function(e) { gregorianDate.setHours(e) };
this.setMinutes = function(e) { gregorianDate.setMinutes(e) };
this.setSeconds = function(e) { gregorianDate.setSeconds(e) };
this.setMilliseconds = function(e) { gregorianDate.setMilliseconds(e) }; }
Thanks
If you are using http://www.farsiweb.info/jalali/jalali.js, try
function printJalali(year, month, day) {
var jalali = gregorian_to_jalali([year, month+1, day]);
return jalali[0] + "/" + jalali[1] + "/" + jalali[2];
}
var today = new Date();
alert(printJalali(today.getFullYear(), today.getMonth(), today.getDate()));

Categories

Resources