history.js: Add clear-history and clear-form-history commands.
[conkeror.git] / modules / hook.js
blob657e4d479d93815e8fb71d05fe03e1e21b62d559
1 /**
2  * (C) Copyright 2007-2008 Jeremy Maitin-Shepard
3  * (C) Copyright 2010 John J. Foerch
4  *
5  * Use, modification, and distribution are subject to the terms specified in the
6  * COPYING file.
7 **/
9 require("coroutine.js");
11 /* Adds the specified function to the specified hook.  To add a local
12  * hook, invoke this function as:  add_hook.call(context, hook_name, ...).
13  * Note: hook_name must be a string */
14 function add_hook (hook_name, func, prepend) {
15     if (!(hook_name in this))
16         this[hook_name] = [];
17     var hook = this[hook_name];
19     if (hook.indexOf(func) != -1)
20         return func;
22     if (prepend)
23         hook.unshift(func);
24     else
25         hook.push(func);
26     return func;
29 /**
30  * Call every function in the array `hook' with the remaining
31  * arguments of this function.  Note: This should only be used by
32  * define_hook and friends to define hook.run(...) functions.  Hooks
33  * should always be run by calling hook.run(...).
34  */
35 function run_hooks (hook, args) {
36     if (hook == null)
37         return;
38     for (let i = 0, hlen = hook.length; i < hlen; ++i)
39         hook[i].apply(null, Array.prototype.slice.call(args));
42 function run_hooks_until_success (hook, args) {
43     if (hook == null)
44         return false;
45     var result;
46     for (let i = 0, hlen = hook.length; i < hlen; ++i)
47         if ((result = hook[i].apply(null, Array.prototype.slice.call(args))))
48             return result;
49     return false;
52 function run_hooks_until_failure (hook, args) {
53     if (hook == null)
54         return true;
55     for (let i = 0, hlen = hook.length; i < hlen; ++i)
56         if (!hook[i].apply(null, Array.prototype.slice.call(args)))
57             return false;
58     return true;
61 function run_coroutine_hooks (hook, args) {
62     if (hook == null)
63         yield co_return();
64     for (let i = 0, hlen = hook.length; i < hlen; ++i)
65         yield hook[i].apply(null, Array.prototype.slice.call(args));
68 function run_coroutine_hooks_until_success (hook, args) {
69     if (hook == null)
70         yield co_return(false);
71     var result;
72     for (let i = 0, hlen = hook.length; i < hlen; ++i)
73         if ((result = yield hook[i].apply(null, Array.prototype.slice.call(args))))
74             yield co_return(result);
75     yield co_return(false);
78 function run_coroutine_hooks_until_failure (hook, args) {
79     if (hook == null)
80         yield co_return(true);
81     for (let i = 0, hlen = hook.length; i < hlen; ++i)
82         if (!(yield hook[i].apply(null, Array.prototype.slice.call(args))))
83             yield co_return(false);
84     yield co_return(true);
88 const RUN_HOOK = 'RUN_HOOK';
89 const RUN_HOOK_UNTIL_SUCCESS = 'RUN_HOOK_UNTIL_SUCCESS';
90 const RUN_HOOK_UNTIL_FAILURE = 'RUN_HOOK_UNTIL_FAILURE';
93 /* This should only be used by define_hook functions */
94 function initialize_hook (run, hook_name, hook_type, doc_string, extra_doc_string) {
95     var docstrings = {
96         RUN_HOOK: "Each hook function is run in sequence.",
97         RUN_HOOK_UNTIL_SUCCESS:
98         "Each hook function is run in sequence until one returns a "+
99             "logically true value.  That value is returned.",
100         RUN_HOOK_UNTIL_FAILURE:
101         "Each hook function is run in sequence until one returns a "+
102             "logically false value.  If no function returns such a "+
103             "value, then the result of the hook will be `true'."
104     };
105     var h = this[hook_name];
106     if (h == null)
107         h = this[hook_name] = [];
108     if (hook_type == null)
109         hook_type = RUN_HOOK;
110     h.run = run;
111     h.hook_type = hook_type;
112     h.hook_name = hook_name;
113     h.doc_string =
114         (doc_string? doc_string + "\n" : "") +
115         docstrings[hook_type] +
116         (extra_doc_string? "\n" + extra_doc_string : "");
117     h.source_code_reference = get_caller_source_code_reference(1);
118     return h;
121 function define_hook (hook_name, hook_type, doc_string) {
122     const prototype = {
123         RUN_HOOK: function () {
124             run_hooks(this, arguments);
125         },
126         RUN_HOOK_UNTIL_SUCCESS: function () {
127             return run_hooks_until_success(this, arguments);
128         },
129         RUN_HOOK_UNTIL_FAILURE: function () {
130             return run_hooks_until_failure(this, arguments);
131         }
132     };
133     initialize_hook(prototype[hook_type || RUN_HOOK],
134                     hook_name, hook_type, doc_string);
137 function define_coroutine_hook (hook_name, hook_type, doc_string) {
138     const prototype = {
139         RUN_HOOK: function () {
140             yield run_coroutine_hooks(this, arguments);
141         },
142         RUN_HOOK_UNTIL_SUCCESS: function () {
143             var result = yield run_coroutine_hooks_until_success(this, arguments);
144             yield co_return(result);
145         },
146         RUN_HOOK_UNTIL_FAILURE: function () {
147             var result = yield run_coroutine_hooks_until_failure(this, arguments);
148             yield co_return(result);
149         }
150     };
151     initialize_hook(prototype[hook_type || RUN_HOOK],
152                     hook_name, hook_type, doc_string);
155 function simple_local_hook_definer (extra_doc_string) {
156     const prototype = {
157         RUN_HOOK: function (x) {
158             var hook_name = this.hook_name;
159             if (hook_name in x)
160                 run_hooks(x[hook_name], arguments);
161             run_hooks(this, arguments);
162         },
163         RUN_HOOK_UNTIL_SUCCESS: function (x) {
164             var hook_name = this.hook_name;
165             var result;
166             if ((hook_name in x) && (result = run_hooks_until_success(x[hook_name], arguments)))
167                 return result;
168             return run_hooks_until_success(conkeror[hook_name], arguments);
169         },
170         RUN_HOOK_UNTIL_FAILURE: function (x) {
171             var hook_name = this.hook_name;
172             if ((hook_name in x) && !run_hooks_until_success(x[hook_name], arguments))
173                 return false;
174             return run_hooks_until_failure(conkeror[hook_name], arguments);
175         }
176     };
177     return function (hook_name, hook_type, doc_string) {
178         initialize_hook(prototype[hook_type || RUN_HOOK],
179                         hook_name, hook_type, doc_string,
180                         extra_doc_string);
181     };
184 function simple_local_coroutine_hook_definer (extra_doc_string) {
185     const prototype = {
186         RUN_HOOK: function (x) {
187             var hook_name = this.hook_name;
188             if (hook_name in x)
189                 yield run_coroutine_hooks(x[hook_name], arguments);
190             yield run_coroutine_hooks(this, arguments);
191         },
192         RUN_HOOK_UNTIL_SUCCESS: function (x) {
193             var hook_name = this.hook_name;
194             var result;
195             if ((hook_name in x) &&
196                 (result = yield run_coroutine_hooks_until_success(x[hook_name], arguments)))
197             {
198                 yield co_return(result);
199             }
200             result = yield run_coroutine_hooks_until_success(conkeror[hook_name], arguments);
201             yield co_return(result);
202         },
203         RUN_HOOK_UNTIL_FAILURE: function (x) {
204             var hook_name = this.hook_name;
205             if ((hook_name in x) &&
206                 !(yield run_coroutine_hooks_until_success(x[hook_name], arguments)))
207             {
208                 yield co_return(false);
209             }
210             var result = yield run_coroutine_hooks_until_failure(conkeror[hook_name], arguments);
211             yield co_return(result);
212         }
213     };
214     return function (hook_name, hook_type, doc_string) {
215         initialize_hook(prototype[hook_type || RUN_HOOK],
216                         hook_name, hook_type, doc_string,
217                         extra_doc_string);
218     };
221 function local_hook_definer (prop_name, extra_doc_string) {
222     const prototype = {
223         RUN_HOOK: function (x) {
224             var hook_name = this.hook_name;
225             if (hook_name in x)
226                 run_hooks(x[hook_name], arguments);
227             if (hook_name in x[prop_name])
228                 run_hooks(x[prop_name][hook_name], arguments);
229             run_hooks(this, arguments);
230         },
231         RUN_HOOK_UNTIL_SUCCESS: function (x) {
232             var hook_name = this.hook_name;
233             var result;
234             if ((hook_name in x) && (result = run_hooks_until_success(x[hook_name], arguments)))
235                 return result;
236             if ((hook_name in x[prop_name]) && (result = run_hooks_until_success(x[prop_name][hook_name], arguments)))
237                 return result;
238             return run_hooks_until_success(conkeror[hook_name], arguments);
239         },
240         RUN_HOOK_UNTIL_FAILURE: function (x) {
241             var hook_name = this.hook_name;
242             if ((hook_name in x) && !run_hooks_until_success(x[hook_name], arguments))
243                 return false;
244             if ((hook_name in x[prop_name]) && !run_hooks_until_success(x[prop_name][hook_name], arguments))
245                 return false;
246             return run_hooks_until_failure(conkeror[hook_name], arguments);
247         }
248     };
249     return function (hook_name, hook_type, doc_string) {
250         initialize_hook(prototype[hook_type || RUN_HOOK],
251                         hook_name, hook_type, doc_string,
252                         extra_doc_string);
253     };
256 function local_coroutine_hook_definer (prop_name, extra_doc_string) {
257     const prototype = {
258         RUN_HOOK: function (x) {
259             var hook_name = this.hook_name;
260             if (hook_name in x)
261                 yield run_coroutine_hooks(x[hook_name], arguments);
262             if (hook_name in x[prop_name])
263                 yield run_coroutine_hooks(x[prop_name][hook_name], arguments);
264             yield run_coroutine_hooks(this, arguments);
265         },
266         RUN_HOOK_UNTIL_SUCCESS: function (x) {
267             var hook_name = this.hook_name;
268             var result;
269             if ((hook_name in x) &&
270                 (result = yield run_coroutine_hooks_until_success(x[hook_name], arguments)))
271             {
272                 yield co_return(result);
273             }
274             if ((hook_name in x[prop_name]) &&
275                 (result = yield run_coroutine_hooks_until_success(x[prop_name][hook_name], arguments)))
276             {
277                 yield co_return(result);
278             }
279             result = yield run_coroutine_hooks_until_success(conkeror[hook_name], arguments);
280             yield co_return(result);
281         },
282         RUN_HOOK_UNTIL_FAILURE: function (x) {
283             var hook_name = this.hook_name;
284             if ((hook_name in x) &&
285                 !(yield run_coroutine_hooks_until_success(x[hook_name], arguments)))
286             {
287                 yield co_return(false);
288             }
289             if ((hook_name in x[prop_name]) &&
290                 !(yield run_coroutine_hooks_until_success(x[prop_name][hook_name], arguments)))
291             {
292                 yield co_return(false);
293             }
294             var result = yield run_coroutine_hooks_until_failure(conkeror[hook_name], arguments);
295             yield co_return(result);
296         }
297     };
298     return function (hook_name, hook_type, doc_string) {
299         initialize_hook(prototype[hook_type || RUN_HOOK],
300                         hook_name, hook_type, doc_string,
301                         extra_doc_string);
302     };
305 function remove_hook (hook_name, func) {
306     var hook = this[hook_name];
307     var index;
308     if (hook && (index = hook.indexOf(func)) != -1)
309         hook.splice(index, 1);
312 provide("hook");