Include and Promise for Prototype
6
Here's a quick lil' addon for Prototype that I use often.
Prototype.Promise(condition, action, interval)
condition is a string that you want met before an action is run.
action is a function that does the action.
interval is the polling rate for condition in seconds, and defaults to 1
So, for example, you may want function foo to run, but only once bar has been set:
function foo(a,b) {
this.retVal=a+b;
}
var thingy = {
retVal:0
};
Prototype.Promise(
'thingy.retVal=5',
foo.bind(thingy,5,10),
5
);
Then, in some point in the mysterious future, thingy.retVal gets set to 5, at which point, the Promise goes into effect, and thingy.retVal becomes 10.
Where I find this particularly useful is in making sure that a document is loaded before doing something (condition="$$('body').length>=1), as you can see it used for the include functions.
Speaking of which, the following include functions are great for getting scripts and stylesheets into your page. I won't bother with examples, as they're pretty straightforward.
Meanwhile, Prototype.scriptPath will point to wherever in your server's heirarchy Prototype was loaded from. The regex, you'll note allows for names like prototype.compressed.js, prototype.modified.js, 2007-09-28.prototype.js, etc - just in case you want to keep track of your various hacks of Prototype, as I do.
Prototype.Promise(condition, action, interval)
condition is a string that you want met before an action is run.
action is a function that does the action.
interval is the polling rate for condition in seconds, and defaults to 1
So, for example, you may want function foo to run, but only once bar has been set:
function foo(a,b) {
this.retVal=a+b;
}
var thingy = {
retVal:0
};
Prototype.Promise(
'thingy.retVal=5',
foo.bind(thingy,5,10),
5
);
Then, in some point in the mysterious future, thingy.retVal gets set to 5, at which point, the Promise goes into effect, and thingy.retVal becomes 10.
Where I find this particularly useful is in making sure that a document is loaded before doing something (condition="$$('body').length>=1), as you can see it used for the include functions.
Speaking of which, the following include functions are great for getting scripts and stylesheets into your page. I won't bother with examples, as they're pretty straightforward.
Meanwhile, Prototype.scriptPath will point to wherever in your server's heirarchy Prototype was loaded from. The regex, you'll note allows for names like prototype.compressed.js, prototype.modified.js, 2007-09-28.prototype.js, etc - just in case you want to keep track of your various hacks of Prototype, as I do.
Object.extend(Prototype,
{
scriptPath:
$$('script').find(
function (s) {
return (s.src && /prototype.*js$/i.test(s.src))
}
).src.replace(/prototype.*js$/i,''),
includeJS: function (file) {
var elScript = Element.create('script',{
type:'text/javascript',
src:file
});
var myPromise = (function () {$$('head')[0].appendChild(this);}).bind(elScript);
Prototype.Promise(
'$$("head").length>=1',
(function () {
$$('head')[0].appendChild(this);
}).bind(elScript)
);
},
includeCSS: function (file) {
var elLink = Element.create('link',{
type:'text/css',
rel:'stylesheet',
href:file
});
Prototype.Promise(
'$$("head").length>=1',
(function () {
$$('head')[0].appendChild(this);
}).bind(elLink)
);
},
Promise: function (condition,action,interval) {
if (typeof interval=='undefined') interval=1;
//Promise to do this now, or whenever (condition) is met
if (eval(condition))
action();
else
new PeriodicalExecuter(
function (pe) {
Prototype.Promise.bind(pe,condition,action,interval)();
},
2
);
if (this.stop) this.stop();
}
}
);






Promise: function (condition,action,interval) {
if (typeof interval=='undefined') interval=1;
if (condition())
action();
else
new PeriodicalExecuter(
function (pe) {
Prototype.Promise.bind(pe,condition,action,interval)();
},
2
);
if (this.stop) this.stop();
}
So, here it is.
var __method=this,
oArgs=arguments,
args=$A(arguments),
__cond=args.shift(),
__intv=args.shift();
if (__cond.apply(__cond,args))
this.apply(this,args);
else new PeriodicalExecuter(function (pe) {
__method.promise.apply(__method,oArgs);
pe.stop();
},__intv);
}
/* Example of use */
function f() {
alert("I've kept my promise.");
}
function c() {
return f.value==4;
}
f.value=5;
f.promise(c,1);
/*Not necessarily how c()'s results will be changed in a real world
example, but a fine way to test it in this situation */
setTimeout("f.value--;", 10);
{
//Do something
}
Event.observe(window, 'load', init);
A page load is an event, and only occurs once. Attaching an event handler to window.load later in the game means your function will never run. That's why it's used that way in Include, as such behavior allows an include to be requested before or after the page loads, and it'll work without fail.
Heh. Ever do something nifty and forget *why* you did it in the first place?
One thing I've used it for that's a good example is an auto-image uploader. I'll spare you the gories of the implementation, but the simple explanation is that the image upload button is a properly skinned iframe with an opacity=0 input[type=file]. I'd like to have it set up so that once an image is selected, it's uploaded and called back to the parent window with the image's location. Unfortunately, there's no event for this.
The solution: Promise to submit the input's form when its value is nonempty.
There are other uses. The point is that while observe and Promise do have some functionality overlap (as event handling and polling are certain to have at some point or another), Promise does fill a gap in the case of awkward programming situations in an elegant way (yeah, I know it's brute force polling, but when that's the only solution, it's not a bad thing for the frontal code to look elegant).