Statistics
| Branch: | Tag: | Revision:

root / ui / static / snf / js / sync.js @ 47276ec2

History | View | Annotate | Download (14.9 kB)

1 8d08f18a Kostas Papadimitriou
;(function(root){
2 8d08f18a Kostas Papadimitriou
    
3 8d08f18a Kostas Papadimitriou
    // root
4 8d08f18a Kostas Papadimitriou
    var root = root;
5 8d08f18a Kostas Papadimitriou
    
6 8d08f18a Kostas Papadimitriou
    // setup namepsaces
7 8d08f18a Kostas Papadimitriou
    var snf = root.synnefo = root.synnefo || {};
8 8d08f18a Kostas Papadimitriou
    var sync = snf.sync = snf.sync || {};
9 8d08f18a Kostas Papadimitriou
    var api = snf.api = snf.api || {};
10 9ce969a7 Kostas Papadimitriou
    var storage = snf.storage = snf.storage || {};
11 8d08f18a Kostas Papadimitriou
12 8d08f18a Kostas Papadimitriou
    // shortcuts
13 8d08f18a Kostas Papadimitriou
    var bb = Backbone;
14 8d08f18a Kostas Papadimitriou
15 8d08f18a Kostas Papadimitriou
    // logging
16 8d08f18a Kostas Papadimitriou
    var logger = new snf.logging.logger("SNF-API");
17 8d08f18a Kostas Papadimitriou
    var debug = _.bind(logger.debug, logger)
18 8d08f18a Kostas Papadimitriou
    
19 8d08f18a Kostas Papadimitriou
    // method map
20 8d08f18a Kostas Papadimitriou
    var methodMap = {
21 8d08f18a Kostas Papadimitriou
        'create': 'POST',
22 8d08f18a Kostas Papadimitriou
        'update': 'PUT',
23 8d08f18a Kostas Papadimitriou
        'delete': 'DELETE',
24 8d08f18a Kostas Papadimitriou
        'read'  : 'GET'
25 8d08f18a Kostas Papadimitriou
    };
26 8d08f18a Kostas Papadimitriou
27 8d08f18a Kostas Papadimitriou
    // custom getUrl function
28 8d08f18a Kostas Papadimitriou
    // handles url retrieval based on the object passed
29 8d08f18a Kostas Papadimitriou
    // on most occasions in the synnefo api this will call
30 8d08f18a Kostas Papadimitriou
    // the model/collection url method
31 8d08f18a Kostas Papadimitriou
    var getUrl = function(object, options) {
32 8d08f18a Kostas Papadimitriou
        if (!(object && object.url)) return null;
33 8d08f18a Kostas Papadimitriou
        return _.isFunction(object.url) ? object.url(options) : object.url;
34 8d08f18a Kostas Papadimitriou
    };
35 8d08f18a Kostas Papadimitriou
    
36 8d08f18a Kostas Papadimitriou
    // Call history (set of api paths with the dates the path last called)
37 8d08f18a Kostas Papadimitriou
    var api_history = api.requests = api.requests || {};
38 f06ec46e Kostas Papadimitriou
    var addApiCallDate = function(url, d, method) {
39 8d08f18a Kostas Papadimitriou
        if (d === undefined) { d = Date() };
40 8d08f18a Kostas Papadimitriou
        var path = snf.util.parseUri(url).path;
41 9ce969a7 Kostas Papadimitriou
        var key = path + "_" + method;
42 9ce969a7 Kostas Papadimitriou
43 9ce969a7 Kostas Papadimitriou
        // TODO: check if d is very old date
44 9ce969a7 Kostas Papadimitriou
        api_history[key] = d;
45 8d08f18a Kostas Papadimitriou
        return api_history[path]
46 8d08f18a Kostas Papadimitriou
    }
47 9ce969a7 Kostas Papadimitriou
48 9ce969a7 Kostas Papadimitriou
    var clearApiCallDate = function(url, method) {
49 9ce969a7 Kostas Papadimitriou
        var path = snf.util.parseUri(url).path;
50 9ce969a7 Kostas Papadimitriou
        var key = path + "_" + method;
51 9ce969a7 Kostas Papadimitriou
        api_history[key] = false;
52 9ce969a7 Kostas Papadimitriou
        return api_history[path]
53 9ce969a7 Kostas Papadimitriou
    }
54 9ce969a7 Kostas Papadimitriou
55 8d08f18a Kostas Papadimitriou
    var api_errors = api.errors = api.errors || [];
56 8d08f18a Kostas Papadimitriou
    var add_api_error = function(settings, data) {
57 8d08f18a Kostas Papadimitriou
        api_errors.push({url:settings.url, date:new Date, settings:settings, data:data})
58 8d08f18a Kostas Papadimitriou
    }
59 8d08f18a Kostas Papadimitriou
60 f06ec46e Kostas Papadimitriou
    var setChangesSince = function(url, method) {
61 8d08f18a Kostas Papadimitriou
        var path = snf.util.parseUri(url).path;
62 f06ec46e Kostas Papadimitriou
        var d = api_history[path + "_" + method];
63 8d08f18a Kostas Papadimitriou
        if (d) {
64 8d08f18a Kostas Papadimitriou
            url = url + "?changes-since=" + snf.util.ISODateString(d)
65 8d08f18a Kostas Papadimitriou
        }
66 8d08f18a Kostas Papadimitriou
        return url;
67 8d08f18a Kostas Papadimitriou
    }
68 8d08f18a Kostas Papadimitriou
    
69 8d08f18a Kostas Papadimitriou
    // custom sync method
70 8d08f18a Kostas Papadimitriou
    // appends global ajax handlers
71 8d08f18a Kostas Papadimitriou
    // handles changed-since url parameter based on api path
72 8d08f18a Kostas Papadimitriou
    api.sync = function(method, model, options) {
73 dbe026f9 Kostas Papadimitriou
        
74 8d08f18a Kostas Papadimitriou
        var type = methodMap[method];
75 8d08f18a Kostas Papadimitriou
        
76 8d08f18a Kostas Papadimitriou
        if (model && (model.skipMethods || []).indexOf(method) >= 0) {
77 8d08f18a Kostas Papadimitriou
            throw "Model does not support " + method + " calls";
78 8d08f18a Kostas Papadimitriou
        }
79 dbe026f9 Kostas Papadimitriou
80 8d08f18a Kostas Papadimitriou
        if (!options.url) {
81 dbe026f9 Kostas Papadimitriou
            var urlobject = model;
82 dbe026f9 Kostas Papadimitriou
83 dbe026f9 Kostas Papadimitriou
            // fallback to collection url for item creation
84 dbe026f9 Kostas Papadimitriou
            if (method == "create" && model.isNew && model.isNew()) {
85 dbe026f9 Kostas Papadimitriou
                urlobject = model.collection;
86 dbe026f9 Kostas Papadimitriou
            }
87 dbe026f9 Kostas Papadimitriou
88 8d08f18a Kostas Papadimitriou
            options.url = getUrl(model, options) || urlError();
89 16392650 Kostas Papadimitriou
            if (model && model.supportIncUpdates) {
90 16392650 Kostas Papadimitriou
                options.url = options.refresh ? options.url : setChangesSince(options.url, type);
91 16392650 Kostas Papadimitriou
            }
92 8d08f18a Kostas Papadimitriou
            if (!options.refresh && options.cache === undefined) {
93 8d08f18a Kostas Papadimitriou
                options.cache = true;
94 8d08f18a Kostas Papadimitriou
            }
95 8d08f18a Kostas Papadimitriou
        }
96 8d08f18a Kostas Papadimitriou
97 9ce969a7 Kostas Papadimitriou
        // default error options
98 9ce969a7 Kostas Papadimitriou
        options.critical = options.critical === undefined ? true : options.critical;
99 9ce969a7 Kostas Papadimitriou
        options.display = options.display === undefined ? true : options.display;
100 9ce969a7 Kostas Papadimitriou
101 9ce969a7 Kostas Papadimitriou
        if (api.stop_calls && !options.no_skip) {
102 8d08f18a Kostas Papadimitriou
            return;
103 8d08f18a Kostas Papadimitriou
        }
104 8d08f18a Kostas Papadimitriou
105 8d08f18a Kostas Papadimitriou
        var success = options.success || function(){};
106 8d08f18a Kostas Papadimitriou
        var error = options.error || function(){};
107 8d08f18a Kostas Papadimitriou
        var complete = options.complete || function(){};
108 8d08f18a Kostas Papadimitriou
        var before_send = options.beforeSend || function(){};
109 8d08f18a Kostas Papadimitriou
110 8d08f18a Kostas Papadimitriou
        // custom json data.
111 8d08f18a Kostas Papadimitriou
        if (options.data && model && (method == 'create' || method == 'update')) {
112 8d08f18a Kostas Papadimitriou
            options.contentType = 'application/json';
113 8d08f18a Kostas Papadimitriou
            options.data = JSON.stringify(options.data);
114 8d08f18a Kostas Papadimitriou
        }
115 8d08f18a Kostas Papadimitriou
        
116 8d08f18a Kostas Papadimitriou
        var api_params = {};
117 8d08f18a Kostas Papadimitriou
        var api_options = _.extend(api_params, options, {
118 8d08f18a Kostas Papadimitriou
            success: api.handlerWrapper(api.successHandler, success, "success"),
119 8d08f18a Kostas Papadimitriou
            error: api.handlerWrapper(api.errorHandler, error, "error"),
120 8d08f18a Kostas Papadimitriou
            complete: api.handlerWrapper(api.completeHandler, complete, "complete"),
121 8d08f18a Kostas Papadimitriou
            beforeSend: api.handlerWrapper(api.beforeSendHandler, before_send, "beforeSend"),
122 8d08f18a Kostas Papadimitriou
            cache: options.cache || false,
123 66be390b Kostas Papadimitriou
            timeout: options.timeout || snf.config.ajax_timeout || window.TIMEOUT || 5000
124 8d08f18a Kostas Papadimitriou
        });
125 8d08f18a Kostas Papadimitriou
        return bb.sync(method, model, api_options);
126 8d08f18a Kostas Papadimitriou
    }
127 8d08f18a Kostas Papadimitriou
    
128 9ce969a7 Kostas Papadimitriou
    api.timeouts_occured = 0;
129 9ce969a7 Kostas Papadimitriou
130 8d08f18a Kostas Papadimitriou
    api.handlerWrapper = function(wrap, method, type) {
131 9ce969a7 Kostas Papadimitriou
        
132 8d08f18a Kostas Papadimitriou
        var cb_type = type;
133 9ce969a7 Kostas Papadimitriou
134 8d08f18a Kostas Papadimitriou
        return function() {
135 8d08f18a Kostas Papadimitriou
            
136 9ce969a7 Kostas Papadimitriou
            var xhr = undefined;
137 9ce969a7 Kostas Papadimitriou
            var handler_type = type;
138 9ce969a7 Kostas Papadimitriou
            var args = arguments;
139 9ce969a7 Kostas Papadimitriou
            var ajax_options = this;
140 9ce969a7 Kostas Papadimitriou
141 9ce969a7 Kostas Papadimitriou
            // save the request date to use it as a changes-since value
142 9ce969a7 Kostas Papadimitriou
            // for opera because we are not able to determine
143 9ce969a7 Kostas Papadimitriou
            // response date header for 304 requests
144 9ce969a7 Kostas Papadimitriou
            if (handler_type == "beforeSend" && $.browser.opera) {
145 9ce969a7 Kostas Papadimitriou
                this.date_send = new Date;
146 9ce969a7 Kostas Papadimitriou
            }
147 9ce969a7 Kostas Papadimitriou
148 9ce969a7 Kostas Papadimitriou
            // error with status code 0 in opera
149 9ce969a7 Kostas Papadimitriou
            // act as 304 response
150 9ce969a7 Kostas Papadimitriou
            if (handler_type == "error" && $.browser.opera) {
151 9ce969a7 Kostas Papadimitriou
                if (arguments[0].status === 0 && arguments[1] === "error") {
152 9ce969a7 Kostas Papadimitriou
                    arguments[0].status = 304;
153 9ce969a7 Kostas Papadimitriou
                    arguments[1] = "notmodified";
154 9ce969a7 Kostas Papadimitriou
                    response_type = "success";
155 9ce969a7 Kostas Papadimitriou
                    xhr = arguments[0];
156 9ce969a7 Kostas Papadimitriou
                }
157 9ce969a7 Kostas Papadimitriou
            }
158 9ce969a7 Kostas Papadimitriou
            
159 9ce969a7 Kostas Papadimitriou
            // add error in api errors registry
160 9ce969a7 Kostas Papadimitriou
            // api errors registry will be sent
161 9ce969a7 Kostas Papadimitriou
            // if user reports an error using feedback form
162 9ce969a7 Kostas Papadimitriou
            if (handler_type == "error") {
163 ec511098 Kostas Papadimitriou
                // skip logging requested ?
164 ec511098 Kostas Papadimitriou
                // if not log this error
165 ec511098 Kostas Papadimitriou
                if (this.log_error !== false) {
166 ec511098 Kostas Papadimitriou
                    add_api_error(this, arguments);
167 ec511098 Kostas Papadimitriou
                }
168 8d08f18a Kostas Papadimitriou
            }
169 6a3a5bf7 Kostas Papadimitriou
            
170 9ce969a7 Kostas Papadimitriou
            // identify response status
171 6a3a5bf7 Kostas Papadimitriou
            var status = 304;
172 6a3a5bf7 Kostas Papadimitriou
            if (arguments[0]) {
173 9ce969a7 Kostas Papadimitriou
                status = arguments[0].status;
174 6a3a5bf7 Kostas Papadimitriou
            }
175 8d08f18a Kostas Papadimitriou
            
176 9ce969a7 Kostas Papadimitriou
            // identify aborted request
177 8d08f18a Kostas Papadimitriou
            try {
178 8d08f18a Kostas Papadimitriou
                if (args[1] === "abort") {
179 8d08f18a Kostas Papadimitriou
                    api.trigger("abort");
180 8d08f18a Kostas Papadimitriou
                    return;
181 8d08f18a Kostas Papadimitriou
                }
182 8d08f18a Kostas Papadimitriou
            } catch(error) {
183 8d08f18a Kostas Papadimitriou
                console.error("error aborting", error);
184 8d08f18a Kostas Papadimitriou
            }
185 9ce969a7 Kostas Papadimitriou
            
186 9ce969a7 Kostas Papadimitriou
            // try to set the last request date
187 9ce969a7 Kostas Papadimitriou
            // only for notmodified or succeed responses
188 8d08f18a Kostas Papadimitriou
            try {
189 8d08f18a Kostas Papadimitriou
                // identify xhr object
190 9ce969a7 Kostas Papadimitriou
                xhr = xhr || args[2];
191 9ce969a7 Kostas Papadimitriou
                
192 9ce969a7 Kostas Papadimitriou
                // not modified response
193 9ce969a7 Kostas Papadimitriou
                if (args[1] === "notmodified") {
194 9ce969a7 Kostas Papadimitriou
                    if (xhr) {
195 9ce969a7 Kostas Papadimitriou
                        // use date_send if exists (opera browser)
196 9ce969a7 Kostas Papadimitriou
                        var d = this.date_send || xhr.getResponseHeader('Date');
197 9ce969a7 Kostas Papadimitriou
                        if (d) { addApiCallDate(this.url, new Date(d), ajax_options.type); };
198 8d08f18a Kostas Papadimitriou
                    }
199 8d08f18a Kostas Papadimitriou
                    return;
200 8d08f18a Kostas Papadimitriou
                }
201 8d08f18a Kostas Papadimitriou
                
202 9ce969a7 Kostas Papadimitriou
                // success response
203 9ce969a7 Kostas Papadimitriou
                if (args[1] == "success" && handler_type == "success") {
204 9ce969a7 Kostas Papadimitriou
                    try {
205 9ce969a7 Kostas Papadimitriou
                        // use date_send if exists (opera browser)
206 9ce969a7 Kostas Papadimitriou
                        var d = this.date_send || args[2].getResponseHeader('Date');
207 9ce969a7 Kostas Papadimitriou
                        if (d) { addApiCallDate(this.url, new Date(d), ajax_options.type); };
208 9ce969a7 Kostas Papadimitriou
                    } catch (err) {
209 9ce969a7 Kostas Papadimitriou
                        console.error(err)
210 9ce969a7 Kostas Papadimitriou
                    }
211 9ce969a7 Kostas Papadimitriou
                }
212 8d08f18a Kostas Papadimitriou
            } catch (err) {
213 8d08f18a Kostas Papadimitriou
                console.error(err);
214 8d08f18a Kostas Papadimitriou
            }
215 9ce969a7 Kostas Papadimitriou
            
216 9ce969a7 Kostas Papadimitriou
            // dont call error callback for non modified responses
217 9ce969a7 Kostas Papadimitriou
            if (arguments[1] === "notmodified") {
218 9ce969a7 Kostas Papadimitriou
                return;
219 9ce969a7 Kostas Papadimitriou
            }
220 9ce969a7 Kostas Papadimitriou
            
221 9ce969a7 Kostas Papadimitriou
            // prepare arguments for error callbacks
222 9ce969a7 Kostas Papadimitriou
            var cb_args = _.toArray(arguments);
223 9ce969a7 Kostas Papadimitriou
            if (handler_type === "error") {
224 9ce969a7 Kostas Papadimitriou
                cb_args.push(_.clone(this));
225 9ce969a7 Kostas Papadimitriou
            }
226 9ce969a7 Kostas Papadimitriou
            
227 9ce969a7 Kostas Papadimitriou
            // determine if we need to call our callback wrapper
228 9ce969a7 Kostas Papadimitriou
            var call_api_handler = true;
229 9ce969a7 Kostas Papadimitriou
230 9ce969a7 Kostas Papadimitriou
            // request handles errors by itself, s
231 9ce969a7 Kostas Papadimitriou
            if (handler_type == "error" && this.skip_api_error) {
232 9ce969a7 Kostas Papadimitriou
                call_api_handler = false
233 9ce969a7 Kostas Papadimitriou
            }
234 9ce969a7 Kostas Papadimitriou
235 9ce969a7 Kostas Papadimitriou
            // aborted request, don't call error handler
236 9ce969a7 Kostas Papadimitriou
            if (handler_type === "error" && args[1] === "abort") {
237 9ce969a7 Kostas Papadimitriou
                call_api_handler = false;
238 9ce969a7 Kostas Papadimitriou
            }
239 9ce969a7 Kostas Papadimitriou
            
240 9ce969a7 Kostas Papadimitriou
            // reset api call date, next call will be sent without changes-since
241 9ce969a7 Kostas Papadimitriou
            // parameter set
242 9ce969a7 Kostas Papadimitriou
            if (handler_type === "error") {
243 9ce969a7 Kostas Papadimitriou
                if (args[1] === "error") {
244 9ce969a7 Kostas Papadimitriou
                    clearApiCallDate(this.url, this.type);
245 9ce969a7 Kostas Papadimitriou
                }
246 9ce969a7 Kostas Papadimitriou
            }
247 9ce969a7 Kostas Papadimitriou
            
248 9ce969a7 Kostas Papadimitriou
            // call api call back and retrieve params to
249 9ce969a7 Kostas Papadimitriou
            // be passed to the callback method set for
250 9ce969a7 Kostas Papadimitriou
            // this type of response
251 9ce969a7 Kostas Papadimitriou
            if (call_api_handler) {
252 9ce969a7 Kostas Papadimitriou
                cb_args = wrap.apply(this, cb_args);
253 9ce969a7 Kostas Papadimitriou
            }
254 9ce969a7 Kostas Papadimitriou
            
255 9ce969a7 Kostas Papadimitriou
            // call requested callback
256 9ce969a7 Kostas Papadimitriou
            method.apply(this, _.toArray(cb_args));
257 8d08f18a Kostas Papadimitriou
        }
258 8d08f18a Kostas Papadimitriou
    }
259 8d08f18a Kostas Papadimitriou
260 8d08f18a Kostas Papadimitriou
    api.successHandler = function(data, status, xhr) {
261 8d08f18a Kostas Papadimitriou
        //debug("ajax success", arguments)
262 8d08f18a Kostas Papadimitriou
        // on success, update the last date we called the api url
263 8d08f18a Kostas Papadimitriou
        return [data, status, xhr];
264 8d08f18a Kostas Papadimitriou
    }
265 8d08f18a Kostas Papadimitriou
266 8d08f18a Kostas Papadimitriou
    api.errorHandler = function(event, xhr, settings, error) {
267 f06ec46e Kostas Papadimitriou
        
268 9ce969a7 Kostas Papadimitriou
        // dont trigger api error untill timeouts occured
269 9ce969a7 Kostas Papadimitriou
        // exceed the skips_timeouts limit
270 9ce969a7 Kostas Papadimitriou
        //
271 9ce969a7 Kostas Papadimitriou
        // check only requests with skips_timeouts option set
272 9ce969a7 Kostas Papadimitriou
        if (xhr === "timeout" && _.last(arguments).skips_timeouts) {
273 9ce969a7 Kostas Papadimitriou
            var skip_timeouts = snf.config.skip_timeouts || 1;
274 9ce969a7 Kostas Papadimitriou
            if (snf.api.timeouts_occured < skip_timeouts) {
275 9ce969a7 Kostas Papadimitriou
                snf.api.timeouts_occured++;
276 9ce969a7 Kostas Papadimitriou
                return;
277 9ce969a7 Kostas Papadimitriou
            } else {
278 9ce969a7 Kostas Papadimitriou
                // reset and continue to error trigger
279 9ce969a7 Kostas Papadimitriou
                snf.api.timeouts_occured = 0;
280 9ce969a7 Kostas Papadimitriou
            }
281 8d08f18a Kostas Papadimitriou
        }
282 09f90e6e Kostas Papadimitriou
283 09f90e6e Kostas Papadimitriou
        // if error occured and changes-since is set for the request
284 09f90e6e Kostas Papadimitriou
        // skip triggering the error and try again without the changes-since
285 09f90e6e Kostas Papadimitriou
        // parameter set
286 09f90e6e Kostas Papadimitriou
        var url = snf.util.parseUri(this.url);
287 09f90e6e Kostas Papadimitriou
        if (url.query.indexOf("changes-since") > -1) {
288 09f90e6e Kostas Papadimitriou
            clearApiCallDate(this.url, this.type);
289 09f90e6e Kostas Papadimitriou
            return _.toArray(arguments);
290 09f90e6e Kostas Papadimitriou
        }
291 9ce969a7 Kostas Papadimitriou
    
292 9ce969a7 Kostas Papadimitriou
        // skip aborts, notmodified (opera)
293 9ce969a7 Kostas Papadimitriou
        if (xhr === "error" || xhr === "timeout") {
294 9ce969a7 Kostas Papadimitriou
            var args = _.toArray(arguments);
295 9ce969a7 Kostas Papadimitriou
            api.trigger("error", args);
296 9ce969a7 Kostas Papadimitriou
        }
297 9ce969a7 Kostas Papadimitriou
298 9ce969a7 Kostas Papadimitriou
        return _.toArray(arguments);
299 8d08f18a Kostas Papadimitriou
    }
300 8d08f18a Kostas Papadimitriou
301 8d08f18a Kostas Papadimitriou
    api.completeHandler = function(xhr, status) {
302 8d08f18a Kostas Papadimitriou
        //debug("ajax complete", arguments)
303 8d08f18a Kostas Papadimitriou
        return arguments;
304 8d08f18a Kostas Papadimitriou
    }
305 8d08f18a Kostas Papadimitriou
306 8d08f18a Kostas Papadimitriou
    api.beforeSendHandler = function(xhr, settings) {
307 8d08f18a Kostas Papadimitriou
        //debug("ajax beforeSend", arguments)
308 8d08f18a Kostas Papadimitriou
        // ajax settings
309 8d08f18a Kostas Papadimitriou
        var ajax_settings = this;
310 8d08f18a Kostas Papadimitriou
        return arguments;
311 8d08f18a Kostas Papadimitriou
    }
312 8d08f18a Kostas Papadimitriou
313 9ce969a7 Kostas Papadimitriou
    // api call helper
314 9ce969a7 Kostas Papadimitriou
    api.call = function(url, method, data, complete, error, success, options) {
315 8d08f18a Kostas Papadimitriou
            var self = this;
316 8d08f18a Kostas Papadimitriou
            error = error || function(){};
317 8d08f18a Kostas Papadimitriou
            success = success || function(){};
318 8d08f18a Kostas Papadimitriou
            complete = complete || function(){};
319 adab5d39 Kostas Papadimitriou
            var extra = data ? data._options || {} : {};
320 bedfe884 Kostas Papadimitriou
321 9ce969a7 Kostas Papadimitriou
            // really ugly way to pass sync request options.
322 9ce969a7 Kostas Papadimitriou
            // it works though....
323 9ce969a7 Kostas Papadimitriou
            if (data && data._options) { delete data['_options'] };
324 9ce969a7 Kostas Papadimitriou
            
325 9ce969a7 Kostas Papadimitriou
            // prepare the params
326 8d08f18a Kostas Papadimitriou
            var params = {
327 8d08f18a Kostas Papadimitriou
                url: snf.config.api_url + "/" + url,
328 8d08f18a Kostas Papadimitriou
                data: data,
329 8d08f18a Kostas Papadimitriou
                success: success,
330 8d08f18a Kostas Papadimitriou
                complete: function() { api.trigger("call"); complete(this) },
331 8d08f18a Kostas Papadimitriou
                error: error
332 8d08f18a Kostas Papadimitriou
            }
333 f06ec46e Kostas Papadimitriou
334 9ce969a7 Kostas Papadimitriou
            params = _.extend(params, extra, options);
335 8d08f18a Kostas Papadimitriou
            this.sync(method, this, params);
336 8d08f18a Kostas Papadimitriou
        },
337 8d08f18a Kostas Papadimitriou
338 8d08f18a Kostas Papadimitriou
    _.extend(api, bb.Events);
339 8d08f18a Kostas Papadimitriou
    
340 9ce969a7 Kostas Papadimitriou
    // helper for callbacks that need to get called
341 9ce969a7 Kostas Papadimitriou
    // in fixed intervals
342 8d08f18a Kostas Papadimitriou
    api.updateHandler = function(options) {
343 8d08f18a Kostas Papadimitriou
        this.cb = options.callback;
344 8d08f18a Kostas Papadimitriou
        this.limit = options.limit;
345 8d08f18a Kostas Papadimitriou
        this.timeout = options.timeout;
346 8d08f18a Kostas Papadimitriou
347 8d08f18a Kostas Papadimitriou
        this.normal_timeout = options.timeout;
348 8d08f18a Kostas Papadimitriou
        this.fast_timeout = options.fast;
349 8d08f18a Kostas Papadimitriou
350 8d08f18a Kostas Papadimitriou
        this._called = 0;
351 8d08f18a Kostas Papadimitriou
        this.interval = undefined;
352 8d08f18a Kostas Papadimitriou
        this.call_on_start = options.call_on_start || true;
353 edd1d565 Kostas Papadimitriou
354 edd1d565 Kostas Papadimitriou
        this.running = false;
355 b1e6a2de Kostas Papadimitriou
        this.last_call = false;
356 8d08f18a Kostas Papadimitriou
        
357 8d08f18a Kostas Papadimitriou
        // wrapper
358 8d08f18a Kostas Papadimitriou
        function _cb() {
359 8d08f18a Kostas Papadimitriou
            if (this.fast_timeout == this.timeout){
360 8d08f18a Kostas Papadimitriou
                this._called++;
361 8d08f18a Kostas Papadimitriou
            }
362 8d08f18a Kostas Papadimitriou
363 8d08f18a Kostas Papadimitriou
            if (this._called >= this.limit && this.fast_timeout == this.timeout) {
364 8d08f18a Kostas Papadimitriou
                this.timeout = this.normal_timeout;
365 8d08f18a Kostas Papadimitriou
                this.setInterval()
366 8d08f18a Kostas Papadimitriou
            }
367 8d08f18a Kostas Papadimitriou
            this.cb();
368 b1e6a2de Kostas Papadimitriou
            this.last_call = new Date;
369 8d08f18a Kostas Papadimitriou
        };
370 edd1d565 Kostas Papadimitriou
371 8d08f18a Kostas Papadimitriou
        _cb = _.bind(_cb, this);
372 8d08f18a Kostas Papadimitriou
373 35855ff5 Kostas Papadimitriou
        this.faster = function(do_call) {
374 8d08f18a Kostas Papadimitriou
            this.timeout = this.fast_timeout;
375 8d08f18a Kostas Papadimitriou
            this._called = 0;
376 35855ff5 Kostas Papadimitriou
            this.setInterval(do_call);
377 8d08f18a Kostas Papadimitriou
        }
378 8d08f18a Kostas Papadimitriou
379 35855ff5 Kostas Papadimitriou
        this.setInterval = function(do_call) {
380 8d08f18a Kostas Papadimitriou
            this.trigger("clear");
381 8d08f18a Kostas Papadimitriou
            window.clearInterval(this.interval);
382 b1e6a2de Kostas Papadimitriou
            
383 edd1d565 Kostas Papadimitriou
            this.interval = window.setInterval(_cb, this.timeout);
384 edd1d565 Kostas Papadimitriou
            this.running = true;
385 b1e6a2de Kostas Papadimitriou
            
386 35855ff5 Kostas Papadimitriou
            var call = do_call || this.call_on_start;
387 b1e6a2de Kostas Papadimitriou
            
388 b1e6a2de Kostas Papadimitriou
            if (this.last_call) {
389 b1e6a2de Kostas Papadimitriou
                var next_call = (this.timeout - ((new Date) - this.last_call));
390 b1e6a2de Kostas Papadimitriou
                if (next_call < this.timeout/2) {
391 b1e6a2de Kostas Papadimitriou
                    call = true;
392 b1e6a2de Kostas Papadimitriou
                } else {
393 b1e6a2de Kostas Papadimitriou
                    call = false;
394 b1e6a2de Kostas Papadimitriou
                }
395 b1e6a2de Kostas Papadimitriou
            }
396 b1e6a2de Kostas Papadimitriou
397 b1e6a2de Kostas Papadimitriou
            if (call) {
398 8d08f18a Kostas Papadimitriou
                _cb();
399 8d08f18a Kostas Papadimitriou
            }
400 b1e6a2de Kostas Papadimitriou
            return this;
401 8d08f18a Kostas Papadimitriou
        }
402 8d08f18a Kostas Papadimitriou
403 2c9bfad1 Kostas Papadimitriou
        this.start = function (call_on_start) {
404 b1e6a2de Kostas Papadimitriou
            if (this.running) { this.stop() };
405 2c9bfad1 Kostas Papadimitriou
            this.call_on_start = call_on_start == undefined ? this.call_on_start : call_on_start;
406 8d08f18a Kostas Papadimitriou
            this.setInterval();
407 b1e6a2de Kostas Papadimitriou
            return this;
408 8d08f18a Kostas Papadimitriou
        }
409 8d08f18a Kostas Papadimitriou
410 8d08f18a Kostas Papadimitriou
        this.stop = function() {
411 8d08f18a Kostas Papadimitriou
            this.trigger("clear");
412 8d08f18a Kostas Papadimitriou
            window.clearInterval(this.interval);
413 edd1d565 Kostas Papadimitriou
            this.running = false;
414 b1e6a2de Kostas Papadimitriou
            return this;
415 8d08f18a Kostas Papadimitriou
        }
416 8d08f18a Kostas Papadimitriou
    }
417 8d08f18a Kostas Papadimitriou
    
418 9ce969a7 Kostas Papadimitriou
    // api error state
419 8d08f18a Kostas Papadimitriou
    api.stop_calls = false;
420 9ce969a7 Kostas Papadimitriou
    api.STATES = { NORMAL:1, WARN:0, ERROR:-1 };
421 9ce969a7 Kostas Papadimitriou
    api.error_state = api.STATES.NORMAL;
422 9ce969a7 Kostas Papadimitriou
423 9ce969a7 Kostas Papadimitriou
    // on api error update the api error_state
424 9ce969a7 Kostas Papadimitriou
    api.bind("error", function() {
425 9ffd10ce Kostas Papadimitriou
        if (snf.api.error_state == snf.api.STATES.ERROR) { return };
426 9ffd10ce Kostas Papadimitriou
427 9ce969a7 Kostas Papadimitriou
        var args = _.toArray(_.toArray(arguments)[0]);
428 9ce969a7 Kostas Papadimitriou
        var params = _.last(args);
429 9ce969a7 Kostas Papadimitriou
        
430 9ce969a7 Kostas Papadimitriou
        if (params.critical) {
431 9ce969a7 Kostas Papadimitriou
            snf.api.error_state = api.STATES.ERROR;
432 9ce969a7 Kostas Papadimitriou
            snf.api.stop_calls = true;
433 9ce969a7 Kostas Papadimitriou
        } else {
434 9ffd10ce Kostas Papadimitriou
            snf.api.error_state = api.STATES.ERROR;
435 9ce969a7 Kostas Papadimitriou
        }
436 9ce969a7 Kostas Papadimitriou
        snf.api.trigger("change:error_state", snf.api.error_state);
437 9ce969a7 Kostas Papadimitriou
    });
438 9ce969a7 Kostas Papadimitriou
    
439 9ce969a7 Kostas Papadimitriou
    // reset api error state
440 9ce969a7 Kostas Papadimitriou
    api.bind("reset", function() {
441 9ce969a7 Kostas Papadimitriou
        snf.api.error_state = api.STATES.NORMAL;
442 9ce969a7 Kostas Papadimitriou
        snf.api.stop_calls = false;
443 9ce969a7 Kostas Papadimitriou
        snf.api.trigger("change:error_state", snf.api.error_state);
444 9ce969a7 Kostas Papadimitriou
    })
445 8d08f18a Kostas Papadimitriou
446 8d08f18a Kostas Papadimitriou
    // make it eventable
447 8d08f18a Kostas Papadimitriou
    _.extend(api.updateHandler.prototype, bb.Events);
448 8d08f18a Kostas Papadimitriou
    
449 8d08f18a Kostas Papadimitriou
})(this);