I have the following JavaScript function which is used to keep users from inundating our server with Ajax requests:
var validateZip = function () {
var lastSuccessful = parseInt(jQuery('#mailingZip').val(), 10);
return {
validate: function(zip) {
var is5DigitNumber = /^\d{5}$/;
if (is5DigitNumber.test(zip) && lastSuccessful !== zip) {
lastSuccessful = zip;
return true;
} else {
return false;
}
}
}();
This page is reloaded if users input a bad zip code or other errors appear on the form. However since the dom hasn't loaded yet, I always pull NaN from that field.
Placing it in the on document ready for jQuery means that I can't call the function directly.
How can I modify my scope such that the lastSuccessful will remain "private" and get the value once the dom is ready?
function validateZip() { // use function declaration, not function expression
var lastSuccessful = parseInt(jQuery('#mailingZip').val(), 10);
return {
validate: function(zip) {
var is5DigitNumber = /^\d{5}$/;
if (is5DigitNumber.test(zip) && lastSuccessful !== zip) {
lastSuccessful = zip;
return true;
} else {
return false;
}
}
}; // removed premature invocation of function
jQuery(validateZip); // call it on document ready instead
Why can't you call it in ready event? Of course you can enclose it into another function:
$(functon(){
var validateZip = function () {
var lastSuccessful = parseInt(jQuery('#mailingZip').val(), 10);
return {
validate: function(zip) {
var is5DigitNumber = /^\d{5}$/;
if (is5DigitNumber.test(zip) && lastSuccessful !== zip) {
lastSuccessful = zip;
return true;
} else {
return false;
}
}
}();
});
The question is you you need to keep validateZip function as defined global? If you only run it at this point, just omit its declaration and just write:
$(functon(){
(function () {
var lastSuccessful = parseInt(jQuery('#mailingZip').val(), 10);
return {
validate: function(zip) {
var is5DigitNumber = /^\d{5}$/;
if (is5DigitNumber.test(zip) && lastSuccessful !== zip) {
lastSuccessful = zip;
return true;
} else {
return false;
}
}
})();
});
Don't know why I didn't think / do this before. I created a getter/setter on my return function, then on the document ready just called the setter with that value.
var validateZip = function() {
// Track the last sent in zip code -- treat this as private data
var lastSuccessful = "";
return {
validate : function(zip) {
var is5DigitNumber = /^\d{5}$/;
if (is5DigitNumber.test(zip) && lastSuccessful !== zip) {
lastSuccessful = zip;
return true;
} else {
return false;
}
},
setLastSuccessful : function(zip) {
lastSuccessful = zip;
},
getLastSuccessful : function() {
return lastSuccessful;
}
}
}();
jQuery(document).ready( function() {
validateZip.setLastSuccessful(jQuery('#mailingZip').val());
});
Related
I'm in way over my head here and need some help to understand what I'm looking at please! (Very new to Javascript!) Here is the situation as I understand it...
I have a script that is selecting a single line from a paragraph of text, and currently produces this alert, where '1' is the selected line:
alert(getLine("sourcePara", 1));
...Instead of triggering an alert I need this selected text to feed into this separate script which is sending data to another browser window. Presently it's taking a text field from a form with the id 'STOCK1', but that can be replaced:
function sendLog() {
var msg = document.getElementById('STOCK1').value;
t.send('STK1', msg);
}
I'm totally confused as to what form this text data is taking on the way out of the first script and have no idea how to call it in as the source for the second... HELP!
All the thanks!
EDIT:
Here is the source code for the Local Connection element;
function LocalConnection(options) {
this.name = 'localconnection';
this.id = new Date().getTime();
this.useLocalStorage = false;
this.debug = false;
this._actions= [];
this.init = function(options) {
try {
localStorage.setItem(this.id, this.id);
localStorage.removeItem(this.id);
this.useLocalStorage = true;
} catch(e) {
this.useLocalStorage = false;
}
for (var o in options) {
this[o] = options[o];
}
this.clear();
}
this.listen = function() {
if (this.useLocalStorage) {
if (window.addEventListener) {
window.addEventListener('storage', this.bind(this, this._check), false);
} else {
window.attachEvent('onstorage', this.bind(this, this._check));
}
} else {
setInterval(this.bind(this, this._check), 100);
}
}
this.send = function(event) {
var args = Array.prototype.slice.call(arguments, 1);
return this._write(event, args);
}
this.addCallback = function(event, func, scope) {
if (scope == undefined) {
scope = this;
}
if (this._actions[event] == undefined) {
this._actions[event] = [];
}
this._actions[event].push({f: func, s: scope});
}
this.removeCallback = function(event) {
for (var e in this._actions) {
if (e == event) {
delete this._actions[e];
break;
}
}
}
this._check = function() {
var data = this._read();
if (data.length > 0) {
for (var e in data) {
this._receive(data[e].event, data[e].args);
}
}
}
this._receive = function(event, args) {
if (this._actions[event] != undefined) {
for (var func in this._actions[event]) {
if (this._actions[event].hasOwnProperty(func)) {
this.log('Triggering callback "'+event+'"', this._actions[event]);
var callback = this._actions[event][func];
callback.f.apply(callback.s, args);
}
}
}
};
this._write = function(event, args) {
var events = this._getEvents();
var evt = {
id: this.id,
event: event,
args: args
};
events.push(evt);
this.log('Sending event', evt);
if (this.useLocalStorage) {
localStorage.setItem(this.name, JSON.stringify(events));
} else {
document.cookie = this.name + '=' + JSON.stringify(events) + "; path=/";
}
return true;
}
this._read = function() {
var events = this._getEvents();
if (events == '') {
return false;
}
var ret = [];
for (var e in events) {
if (events[e].id != this.id) {
ret.push({
event: events[e].event,
args: events[e].args
});
events.splice(e, 1);
}
}
if (this.useLocalStorage) {
localStorage.setItem(this.name, JSON.stringify(events));
} else {
document.cookie = this.name + '=' + JSON.stringify(events) + "; path=/";
}
return ret;
}
this._getEvents = function() {
return this.useLocalStorage ? this._getLocalStorage() : this._getCookie();
}
this._getLocalStorage = function() {
var events = localStorage.getItem(this.name);
if (events == null) {
return [];
}
return JSON.parse(events);
}
this._getCookie = function() {
var ca = document.cookie.split(';');
var data;
for (var i=0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1, c.length);
}
if (c.indexOf(this.name+'=') == 0) {
data = c.substring(this.name.length+1, c.length);
break;
}
}
data = data || '[]';
return JSON.parse(data);
}
this.clear = function() {
if (this.useLocalStorage) {
localStorage.removeItem(this.name);
} else {
document.cookie = this.name + "=; path=/";
}
}
this.bind = function(scope, fn) {
return function () {
fn.apply(scope, arguments);
};
}
this.log = function() {
if (!this.debug) {
return;
}
if (console) {
console.log(Array.prototype.slice.call(arguments));
}
}
this.init(options);
}
If I understand what you are asking for correctly, then I think its a matter of changing your log function to the following:
function sendLog() {
t.send('STK1', getLine("sourcePara", 1));
}
This assumes that getLine is globally accessible.
Alternatively Another approach would be to allow for the sendLog function to take the message as a parameter. In which case, you would change your first script to be:
sendLog(getLine("sourcePara", 1));
And the modified sendLog function would look like this:
function sendLog(msg) {
t.send('STK1', msg);
}
LocalConnection.js should handle transferring the data between windows/tabs. Looks like an an iteresting project:
https://github.com/jeremyharris/LocalConnection.js
I am trying to bind a variable to a scope before it moves to the view but my view shows before the variable is bounded. Here is my code.
$scope.getListing = function() {
var deferred = $q.defer();
$scope.$applyAsync(function() {
$rootScope.listingDetails =[];
referralCasesGroupByCaseStatus.getListing($rootScope.userDetails.rows.item(2).value).then(function(data){
$rootScope.listingDetails = data
deferred.resolve($rootScope.listingDetails)
if($rootScope.fromDashboard === false) {
$scope.showCaseStatus(1);
$state.go('app.case_status')
}
else {
$scope.showCaseStatus($rootScope.statusNumber)
$state.go('app.case_status')
$ionicLoading.hide();
}
});
})
return deferred.promise;
};
var changedNumber = 0;
$scope.showCaseStatus = function(number) {
var finishedPushingListings = false;
$rootScope.listingByCaseStatus = [];
$rootScope.caseStatusListings = [];
if(changedNumber !== 0 && changedNumber !== number) {
changedNumber = number;
}
else {
if(changedNumber > 0) {
$scope.$applyAsync($rootScope.detailsPresent = true);
}
}
$scope.$applyAsync(function() {
angular.forEach($rootScope.listingDetails, function(value, key) {
if(value.real_estate_agent_assignment_status_id == number) {
$rootScope.listingByCaseStatus.push(value);
}
});
})
$scope.$applyAsync(function() {
if($rootScope.listingByCaseStatus == 0 || $rootScope.listingByCaseStatus == undefined || $rootScope.listingByCaseStatus == null) {
$rootScope.detailsPresent = true;
$rootScope.changeNumber = true;
finishedPushingListings = true;
}
else {
$rootScope.detailsPresent = false;
$scope.noMoreItemsAvailable = false;
$rootScope.changeNumber = true;
finishedPushingListings = true;
}
})
};
The main problem here is that the function $scope.showCaseStatus($rootScope.statusNumber) doesnt finish executing before it executes the $state.go('app.case_status') and i would like for it to wait and finish executing before it jumps to the $state.go('app.case_status').
Any help is appreciated.
Since you are using $applyAsync(), the function effects are asynchronous. One way to achieve what you want is to make showCaseStatus() return a promise - and take into account that there are 2 asynchronous blocks:
$scope.showCaseStatus = function(number) {
var ..., d1, d2;
...
d1 = $q.defer();
$scope.$applyAsync(function() {
angular.forEach($rootScope.listingDetails, function(value, key) {
...
});
d1.resolve();
})
d2 = $q.defer();
$scope.$applyAsync(function() {
...
d2.resolve();
})
// both promises must be resolved to continue
return $q.all([d1.promise, d2.promise]);
};
Then the caller becomes:
$scope.showCaseStatus($rootScope.statusNumber).then(function() {
$state.go('app.case_status')
$ionicLoading.hide();
});
Some notes:
If you do not need the async blocks, you can remove them and simplify the code
If the second async block relies on the result of the first, they too should be synchronized
validation works fine if validation properties are placed after "HasError" property in VM.
In the case that the property placed before HasError I will get "parameters.hasError" as undefined. I think it's because the property "HasError" is not defined to that time.
Is there any solution without changing the order of the properties inside VM to make it work.
Thanks!
self._BusTypeDefault = function(param) {
var ret = param.BusType;
if(typeof(ret)==='undefined') {
ret = '';
}
else if(ko.isObservable(ret)) {
ret = ret.peek();
}
return ret;
};
self.BusType = ko.observable(self._BusTypeDefault(init)).extend({maxLength: {message: $Resources.PCIBUSError(), maxFieldLength: 255,hasError: self.HasError }});
self._HasErrorDefault = function(param) {
var ret = param.HasError;
if(typeof(ret)==='undefined') {
ret = false;
}
else if(ko.isObservable(ret)) {
ret = ret.peek();
}
return ret;
};
self.HasError = ko.observable(self._HasErrorDefault(init)).extend({errorAggregation: {}});
ko.extenders.maxLength = function (target, parameters) {
//add some sub-observables to our observable
target.hasMaxLengthError = ko.observable();
target.validationMessageMaxError = ko.observable();
//define a function to do validation
function validate(newValue) {
var preValue = target.hasMaxLengthError();
if (newValue.length >= parameters.maxFieldLength) {
target.hasMaxLengthError(true);
target.validationMessageMaxError(parameters.message || "This field is required");
}
else {
target.hasMaxLengthError(false);
target.validationMessageMaxError("");
}
if (parameters.hasError != null && target.hasMaxLengthError() !== preValue && typeof preValue !== 'undefined') {
parameters.hasError(target.hasMaxLengthError());
}
}
//initial validation
validate(target());
//validate whenever the value changes
target.subscribe(validate);
//return the original observable
return target;
};
You can use a function to delay the interpretation of hasError:
this.myObservable = ko.observable(1).extend({ myExtender : { hasError: function () { return self.hasError } } });
Then in the extender you'll need to call the function to actually get the observable behind:
ko.extenders.myExtender = function (target, params) {
function validate(newValue) {
alert("New Value: " + newValue + " ; Has Error: " + params.hasError()());
}
target.subscribe(validate);
}
See this example: http://jsfiddle.net/7ywLN/
How can I get the latest page data (HTML & Javascript varaibles) from PhantomJS
e.g page.refresh() or something?
I have an Interval, than checks a variable (on the page) every 200ms. However, this variable and the page content, isn't shown to have changed over time. (even though I know it has)
So I need an efficient way to check the value of a JS variable every 200ms or so,
then once I've discovered that variable has changed value, I want to request the latest page HTML.
How can I do this?
var Error = function (description) {
this.description = description;
return this;
};
var DTO = function (status, content, error) {
this.status = status;
this.content = content;
this.error = error;
return this;
};
function outputAndExit(dto) {
console.log(JSON.stringify(dto));
phantom.exit();
}
//For any uncaught exception, just log it out for .NET to capture
window.onerror = function (errorMsg, url, lineNumber) {
var description = 'window.onerror caught an error: ' +
'errorMsg: ' + errorMsg +
'url: ' + url +
'lineNumber: ' + lineNumber;
outputAndExit(new DTO(false, null, new Error(description)));
};
var GetDynamicPageResult__ = function () {
var obj = new GetDynamicPageResult();
obj.initialize();
return obj;
};
var GetDynamicPageResult = function () {
var self = this;
this.initialize = function () {
this.error = null;
this.isContentReadyForCrawler = false;
this.ticker = null;
this.tickerInterval = 150;
this.tickerElapsed = 0;
this.url = '';
this.loadDependencies();
this.processArgs();
this.openPage();
};
this.loadDependencies = function () {
this.system = require('system'),
this.page = require('webpage').create(),
this.page.injectJs('jquery-1.10.2.min');
this.fs = require('fs');
};
this.processArgs = function () {
if (this.system.args.length == 0) {
outputAndExit(new DTO(false, null, new Error('No arguments given')));
}
//system.args[0] Was the name of this script
this.url = this.system.args[1];
};
this.updateIsContentReadyForCrawler = function () {
var updateIsContentReadyForCrawler = self.page.evaluate(function () {
self.isContentReadyForCrawler = window.isContentReadyForCrawler;
});
};
this.openPage = function () {
self.page.open(this.url, function (status) { //NB: status = 'success' || 'fail'
if (status !== 'success') {
outputAndExit(new DTO(false, null, new Error('page.open received a non-success status')));
}
self.initTicker();
});
};
this.initTicker = function () {
this.ticker = setInterval(self.handleTick, self.tickerInterval);
};
this.handleTick = function () {
self.tickerElapsed += self.tickerInterval;
self.updateIsContentReadyForCrawler();
if (self.isContentReadyForCrawler) {
clearInterval(self.ticker);
var content = self.page.content;
self.finish(true, content, null);
} else {
var tooMuchTimeElapsed = self.tickerElapsed > 7000;
if (tooMuchTimeElapsed) {
clearInterval(self.ticker);
self.finish(false, null, new Error('Too much time elapsed'));
}
}
};
this.finish = function (status, content, error) {
content = content || '';
error = error || {};
outputAndExit(new DTO(status, content, error));
};
};
/**********************************************************************************/
/***************************** Helpers *****************************/
/**********************************************************************************/
var Utility__ = function () {
var obj = new Utility();
obj.initialize();
return obj;
};
var Utility = function () {
var self = this;
this.initialize = function () {
};
this.isEmpty = function (obj) {
var isEmpty = false;
(obj == undefined || obj == null) && (isEmpty = true);
return isEmpty;
};
this.isStringEmpty = function (str) {
var isEmpty = false;
isEmpty(str) && (isEmpty = true);
(isEmpty == false && $.trim(str) == '') && (isEmpty = true);
return isEmpty;
};
};
var getDynamicPageResult = new GetDynamicPageResult__();
I think you are almost there: you need to be using page.evaluate(), but currently only use it to get window.isContentReadyForCrawler. You need to use page.evaluate() to grab the latest HTML too.
I'm going to shamelessly paste in code from another answer (https://stackoverflow.com/a/12044474/841830):
var html = page.evaluate(function () {
var root = document.getElementsByTagName("html")[0];
var html = root ? root.outerHTML : document.body.innerHTML;
return html;
});
I've created a function to iterate through a UL/LI. This works perfectly, my problem is returning the value to another variable. Is this even possible? What's the best method for this? Thanks!
function getMachine(color, qty) {
$("#getMachine li").each(function() {
var thisArray = $(this).text().split("~");
if(thisArray[0] == color&& qty>= parseInt(thisArray[1]) && qty<= parseInt(thisArray[2])) {
return thisArray[3];
}
});
}
var retval = getMachine(color, qty);
I'm not entirely sure of the general purpose of the function, but you could always do this:
function getMachine(color, qty) {
var retval;
$("#getMachine li").each(function() {
var thisArray = $(this).text().split("~");
if(thisArray[0] == color&& qty>= parseInt(thisArray[1]) && qty<= parseInt(thisArray[2])) {
retval = thisArray[3];
return false;
}
});
return retval;
}
var retval = getMachine(color, qty);
The return statement you have is stuck in the inner function, so it won't return from the outer function. You just need a little more code:
function getMachine(color, qty) {
var returnValue = null;
$("#getMachine li").each(function() {
var thisArray = $(this).text().split("~");
if(thisArray[0] == color&& qty>= parseInt(thisArray[1]) && qty<= parseInt(thisArray[2])) {
returnValue = thisArray[3];
return false; // this breaks out of the each
}
});
return returnValue;
}
var retval = getMachine(color, qty);