Skip to content

Instantly share code, notes, and snippets.

@lastobelus
Last active December 31, 2015 05:49
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lastobelus/7943705 to your computer and use it in GitHub Desktop.
Save lastobelus/7943705 to your computer and use it in GitHub Desktop.
An asynchronous, debounced, optionally caching validator for ember-validations (requires my patch in pull request 64)
var UrlExists = Ember.Object.extend({
test: function(url){
var promise = Ember.Deferred.create();
if(Ember.isEmpty(url)){
promise.reject(false);
} else {
$.getJSON("http://query.yahooapis.com/v1/public/yql?"+
"q=select%20*%20from%20html%20where%20url%3D%22"+
encodeURIComponent(url)+"%22&format=json'&callback=?",
function(data){
if(data.results[0]){
promise.resolve(true);
} else {
promise.reject(false);
}
}
).fail(function() {
promise.reject(false);
});
}
return promise;
}
}).create();
Ember.Validations.validators.local.UrlExists = Ember.Validations.validators.Base.extend({
init: function() {
this._super();
/*jshint expr:true*/
if (this.options === true) {
this.options = {};
}
if (this.options.message === undefined) {
this.options.message = "must be a valid URL"
}
if (this.options.debounce === undefined){
this.options.debounce = 200;
}
if (this.options.caches === undefined) {
this.options.caches = true;
}
this.options.debounce = parseInt(this.options.debounce);
},
promise: null,
inflightValidations: {},
UrlExists: UrlExists,
pending: null,
call: function() {
var promise = this.get('promise');
var self = this;
if(Ember.isNone(promise)){
promise = Ember.Deferred.create();
this.set('promise', promise);
}
// we need a stop-and-go because two call->_call->resolve chains can overlap
this.set('pending', Ember.Deferred.create());
Ember.run.debounce(this, this._call, this.options.debounce);
return promise;
},
resolveValidation: function(value, result){
var inflightValidations = this.get('inflightValidations');
inflightValidations[value] = result;
var self = this;
this.get('pending').then(function() {
self.settle();
})
},
settle: function() {
var promise = this.get('promise');
var currentValue = this.model.get(this.property);
var inflightValidations = this.get('inflightValidations');
var finished = Ember.keys(inflightValidations).every(function(value){
return undefined === inflightValidations[value].then;
});
if(finished){
if(inflightValidations.hasOwnProperty(currentValue)){
if( inflightValidations[currentValue] === false ){
this.errors.pushObject(this.options.message);
}
promise.resolve();
this.set('promise', null);
} else {
// currentValue got ahead
this.call();
}
}
},
_call: function() {
this.get('pending').resolve();
var self = this;
var value = this.model.get(this.property);
var inflightValidations = this.get('inflightValidations');
if(this.options.caches && inflightValidations[value] && (undefined === inflightValidations[value].then)){
this.settle();
return;
}
inflightValidations[value] = this.UrlExists.test(value).then(
function() {
self.resolveValidation(value, true);
},
function() {
self.resolveValidation(value, false);
}
);
}
});
module.exports = UrlExists;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment