Statistics
| Branch: | Tag: | Revision:

root / ui / static / snf / js / sync.js @ 44dd6b92

History | View | Annotate | Download (8.6 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 8d08f18a Kostas Papadimitriou
11 8d08f18a Kostas Papadimitriou
    // shortcuts
12 8d08f18a Kostas Papadimitriou
    var bb = Backbone;
13 8d08f18a Kostas Papadimitriou
14 8d08f18a Kostas Papadimitriou
    // logging
15 8d08f18a Kostas Papadimitriou
    var logger = new snf.logging.logger("SNF-API");
16 8d08f18a Kostas Papadimitriou
    var debug = _.bind(logger.debug, logger)
17 8d08f18a Kostas Papadimitriou
    
18 8d08f18a Kostas Papadimitriou
    // method map
19 8d08f18a Kostas Papadimitriou
    var methodMap = {
20 8d08f18a Kostas Papadimitriou
        'create': 'POST',
21 8d08f18a Kostas Papadimitriou
        'update': 'PUT',
22 8d08f18a Kostas Papadimitriou
        'delete': 'DELETE',
23 8d08f18a Kostas Papadimitriou
        'read'  : 'GET'
24 8d08f18a Kostas Papadimitriou
    };
25 8d08f18a Kostas Papadimitriou
26 8d08f18a Kostas Papadimitriou
    // custom getUrl function
27 8d08f18a Kostas Papadimitriou
    // handles url retrieval based on the object passed
28 8d08f18a Kostas Papadimitriou
    // on most occasions in the synnefo api this will call
29 8d08f18a Kostas Papadimitriou
    // the model/collection url method
30 8d08f18a Kostas Papadimitriou
    var getUrl = function(object, options) {
31 8d08f18a Kostas Papadimitriou
        if (!(object && object.url)) return null;
32 8d08f18a Kostas Papadimitriou
        return _.isFunction(object.url) ? object.url(options) : object.url;
33 8d08f18a Kostas Papadimitriou
    };
34 8d08f18a Kostas Papadimitriou
    
35 8d08f18a Kostas Papadimitriou
    // Call history (set of api paths with the dates the path last called)
36 8d08f18a Kostas Papadimitriou
    var api_history = api.requests = api.requests || {};
37 f06ec46e Kostas Papadimitriou
    var addApiCallDate = function(url, d, method) {
38 8d08f18a Kostas Papadimitriou
        if (d === undefined) { d = Date() };
39 8d08f18a Kostas Papadimitriou
        var path = snf.util.parseUri(url).path;
40 f06ec46e Kostas Papadimitriou
        api_history[path + "_" + method] = d;
41 8d08f18a Kostas Papadimitriou
        return api_history[path]
42 8d08f18a Kostas Papadimitriou
    }
43 8d08f18a Kostas Papadimitriou
    
44 8d08f18a Kostas Papadimitriou
    var api_errors = api.errors = api.errors || [];
45 8d08f18a Kostas Papadimitriou
    var add_api_error = function(settings, data) {
46 8d08f18a Kostas Papadimitriou
        api_errors.push({url:settings.url, date:new Date, settings:settings, data:data})
47 8d08f18a Kostas Papadimitriou
    }
48 8d08f18a Kostas Papadimitriou
49 f06ec46e Kostas Papadimitriou
    var setChangesSince = function(url, method) {
50 8d08f18a Kostas Papadimitriou
        var path = snf.util.parseUri(url).path;
51 f06ec46e Kostas Papadimitriou
        var d = api_history[path + "_" + method];
52 8d08f18a Kostas Papadimitriou
        if (d) {
53 8d08f18a Kostas Papadimitriou
            url = url + "?changes-since=" + snf.util.ISODateString(d)
54 8d08f18a Kostas Papadimitriou
        }
55 8d08f18a Kostas Papadimitriou
        return url;
56 8d08f18a Kostas Papadimitriou
    }
57 8d08f18a Kostas Papadimitriou
    
58 8d08f18a Kostas Papadimitriou
    // custom sync method
59 8d08f18a Kostas Papadimitriou
    // appends global ajax handlers
60 8d08f18a Kostas Papadimitriou
    // handles changed-since url parameter based on api path
61 8d08f18a Kostas Papadimitriou
    api.sync = function(method, model, options) {
62 8d08f18a Kostas Papadimitriou
63 8d08f18a Kostas Papadimitriou
        var type = methodMap[method];
64 8d08f18a Kostas Papadimitriou
        
65 8d08f18a Kostas Papadimitriou
        if (model && (model.skipMethods || []).indexOf(method) >= 0) {
66 8d08f18a Kostas Papadimitriou
            throw "Model does not support " + method + " calls";
67 8d08f18a Kostas Papadimitriou
        }
68 8d08f18a Kostas Papadimitriou
        
69 8d08f18a Kostas Papadimitriou
        if (!options.url) {
70 8d08f18a Kostas Papadimitriou
            options.url = getUrl(model, options) || urlError();
71 f06ec46e Kostas Papadimitriou
            options.url = options.refresh ? options.url : setChangesSince(options.url, type);
72 8d08f18a Kostas Papadimitriou
            if (!options.refresh && options.cache === undefined) {
73 8d08f18a Kostas Papadimitriou
                options.cache = true;
74 8d08f18a Kostas Papadimitriou
            }
75 8d08f18a Kostas Papadimitriou
        }
76 8d08f18a Kostas Papadimitriou
77 8d08f18a Kostas Papadimitriou
        options.handles_error = options.handles_error || false;
78 8d08f18a Kostas Papadimitriou
        
79 8d08f18a Kostas Papadimitriou
        if (api.stop_calls) {
80 8d08f18a Kostas Papadimitriou
            return;
81 8d08f18a Kostas Papadimitriou
        }
82 8d08f18a Kostas Papadimitriou
83 8d08f18a Kostas Papadimitriou
        var success = options.success || function(){};
84 8d08f18a Kostas Papadimitriou
        var error = options.error || function(){};
85 8d08f18a Kostas Papadimitriou
        var complete = options.complete || function(){};
86 8d08f18a Kostas Papadimitriou
        var before_send = options.beforeSend || function(){};
87 8d08f18a Kostas Papadimitriou
88 8d08f18a Kostas Papadimitriou
        // custom json data.
89 8d08f18a Kostas Papadimitriou
        if (options.data && model && (method == 'create' || method == 'update')) {
90 8d08f18a Kostas Papadimitriou
            options.contentType = 'application/json';
91 8d08f18a Kostas Papadimitriou
            options.data = JSON.stringify(options.data);
92 8d08f18a Kostas Papadimitriou
        }
93 8d08f18a Kostas Papadimitriou
        
94 8d08f18a Kostas Papadimitriou
        var api_params = {};
95 8d08f18a Kostas Papadimitriou
        var api_options = _.extend(api_params, options, {
96 8d08f18a Kostas Papadimitriou
            success: api.handlerWrapper(api.successHandler, success, "success"),
97 8d08f18a Kostas Papadimitriou
            error: api.handlerWrapper(api.errorHandler, error, "error"),
98 8d08f18a Kostas Papadimitriou
            complete: api.handlerWrapper(api.completeHandler, complete, "complete"),
99 8d08f18a Kostas Papadimitriou
            beforeSend: api.handlerWrapper(api.beforeSendHandler, before_send, "beforeSend"),
100 8d08f18a Kostas Papadimitriou
            cache: options.cache || false,
101 8d08f18a Kostas Papadimitriou
            timeout: options.timeout || window.TIMEOUT || 5000
102 8d08f18a Kostas Papadimitriou
        });
103 8d08f18a Kostas Papadimitriou
        return bb.sync(method, model, api_options);
104 8d08f18a Kostas Papadimitriou
    }
105 8d08f18a Kostas Papadimitriou
    
106 8d08f18a Kostas Papadimitriou
    api.handlerWrapper = function(wrap, method, type) {
107 8d08f18a Kostas Papadimitriou
        var cb_type = type;
108 8d08f18a Kostas Papadimitriou
        return function() {
109 8d08f18a Kostas Papadimitriou
            
110 8d08f18a Kostas Papadimitriou
            if (type == "error") {
111 8d08f18a Kostas Papadimitriou
                add_api_error(this, arguments);
112 8d08f18a Kostas Papadimitriou
            }
113 6a3a5bf7 Kostas Papadimitriou
            
114 6a3a5bf7 Kostas Papadimitriou
            var status = 304;
115 6a3a5bf7 Kostas Papadimitriou
            if (arguments[0]) {
116 6a3a5bf7 Kostas Papadimitriou
                var status = arguments[0].status;
117 6a3a5bf7 Kostas Papadimitriou
            }
118 8d08f18a Kostas Papadimitriou
119 6a3a5bf7 Kostas Papadimitriou
            if (type == "error" && this.handles_error && ((status > 499 && status < 600) || status == 400)) { 
120 6a3a5bf7 Kostas Papadimitriou
                arguments.ajax = this;
121 6a3a5bf7 Kostas Papadimitriou
                return method(arguments)
122 6a3a5bf7 Kostas Papadimitriou
            }
123 8d08f18a Kostas Papadimitriou
124 8d08f18a Kostas Papadimitriou
            var args = wrap.apply(this, arguments);
125 8d08f18a Kostas Papadimitriou
            args = _.toArray(args);
126 8d08f18a Kostas Papadimitriou
            var ajax_options = this;
127 8d08f18a Kostas Papadimitriou
            
128 8d08f18a Kostas Papadimitriou
            try {
129 8d08f18a Kostas Papadimitriou
                if (args[1] === "abort") {
130 8d08f18a Kostas Papadimitriou
                    api.trigger("abort");
131 8d08f18a Kostas Papadimitriou
                    return;
132 8d08f18a Kostas Papadimitriou
                }
133 8d08f18a Kostas Papadimitriou
            } catch(error) {
134 8d08f18a Kostas Papadimitriou
                console.error("error aborting", error);
135 8d08f18a Kostas Papadimitriou
            }
136 8d08f18a Kostas Papadimitriou
137 8d08f18a Kostas Papadimitriou
            // FIXME: is this good practice ??
138 8d08f18a Kostas Papadimitriou
            // fetch callbacks wont get called
139 8d08f18a Kostas Papadimitriou
            try {
140 8d08f18a Kostas Papadimitriou
                // identify xhr object
141 8d08f18a Kostas Papadimitriou
                var xhr = args[2];
142 8d08f18a Kostas Papadimitriou
                if (args.length == 2) {
143 8d08f18a Kostas Papadimitriou
                    xhr = args[0];
144 8d08f18a Kostas Papadimitriou
                }
145 8d08f18a Kostas Papadimitriou
146 8d08f18a Kostas Papadimitriou
                // do not call success for 304 responses
147 8d08f18a Kostas Papadimitriou
                if (args[1] === "notmodified" || xhr.status == 0 && $.browser.opera) {
148 8d08f18a Kostas Papadimitriou
                    if (args[2]) {
149 f06ec46e Kostas Papadimitriou
                        addApiCallDate(this.url, new Date(args[2].getResponseHeader('Date')), ajax_options.type);
150 8d08f18a Kostas Papadimitriou
                    }
151 8d08f18a Kostas Papadimitriou
                    return;
152 8d08f18a Kostas Papadimitriou
                }
153 8d08f18a Kostas Papadimitriou
                
154 8d08f18a Kostas Papadimitriou
            } catch (err) {
155 8d08f18a Kostas Papadimitriou
                console.error(err);
156 8d08f18a Kostas Papadimitriou
            }
157 8d08f18a Kostas Papadimitriou
            method.apply(this, args);
158 8d08f18a Kostas Papadimitriou
        }
159 8d08f18a Kostas Papadimitriou
    }
160 8d08f18a Kostas Papadimitriou
161 8d08f18a Kostas Papadimitriou
    api.successHandler = function(data, status, xhr) {
162 8d08f18a Kostas Papadimitriou
        //debug("ajax success", arguments)
163 8d08f18a Kostas Papadimitriou
        // on success, update the last date we called the api url
164 f06ec46e Kostas Papadimitriou
        addApiCallDate(this.url, new Date(xhr.getResponseHeader('Date')), this.type);
165 8d08f18a Kostas Papadimitriou
        return [data, status, xhr];
166 8d08f18a Kostas Papadimitriou
    }
167 8d08f18a Kostas Papadimitriou
168 8d08f18a Kostas Papadimitriou
    api.errorHandler = function(event, xhr, settings, error) {
169 8d08f18a Kostas Papadimitriou
        //debug("ajax error", arguments, this);
170 8d08f18a Kostas Papadimitriou
        arguments.ajax = this;
171 f06ec46e Kostas Papadimitriou
        
172 8d08f18a Kostas Papadimitriou
        // skip aborts
173 8d08f18a Kostas Papadimitriou
        if (xhr != "abort") {
174 6a3a5bf7 Kostas Papadimitriou
            arguments.ajax.critical = arguments.ajax.critical == undefined ? true : arguments.ajax.critical;
175 8d08f18a Kostas Papadimitriou
            if (!settings.handles_error) api.trigger("error", arguments);
176 8d08f18a Kostas Papadimitriou
        }
177 8d08f18a Kostas Papadimitriou
        return arguments;
178 8d08f18a Kostas Papadimitriou
    }
179 8d08f18a Kostas Papadimitriou
180 8d08f18a Kostas Papadimitriou
    api.completeHandler = function(xhr, status) {
181 8d08f18a Kostas Papadimitriou
        //debug("ajax complete", arguments)
182 8d08f18a Kostas Papadimitriou
        return arguments;
183 8d08f18a Kostas Papadimitriou
    }
184 8d08f18a Kostas Papadimitriou
185 8d08f18a Kostas Papadimitriou
    api.beforeSendHandler = function(xhr, settings) {
186 8d08f18a Kostas Papadimitriou
        //debug("ajax beforeSend", arguments)
187 8d08f18a Kostas Papadimitriou
        // ajax settings
188 8d08f18a Kostas Papadimitriou
        var ajax_settings = this;
189 8d08f18a Kostas Papadimitriou
        return arguments;
190 8d08f18a Kostas Papadimitriou
    }
191 8d08f18a Kostas Papadimitriou
192 8d08f18a Kostas Papadimitriou
193 8d08f18a Kostas Papadimitriou
    api.call = function(url, method, data, complete, error, success) {
194 8d08f18a Kostas Papadimitriou
            var self = this;
195 8d08f18a Kostas Papadimitriou
            error = error || function(){};
196 8d08f18a Kostas Papadimitriou
            success = success || function(){};
197 8d08f18a Kostas Papadimitriou
            complete = complete || function(){};
198 adab5d39 Kostas Papadimitriou
            var extra = data ? data._options || {} : {};
199 adab5d39 Kostas Papadimitriou
            if (data && data._options) { delete data['_options'] };
200 bedfe884 Kostas Papadimitriou
201 8d08f18a Kostas Papadimitriou
            var params = {
202 8d08f18a Kostas Papadimitriou
                url: snf.config.api_url + "/" + url,
203 8d08f18a Kostas Papadimitriou
                data: data,
204 8d08f18a Kostas Papadimitriou
                success: success,
205 8d08f18a Kostas Papadimitriou
                complete: function() { api.trigger("call"); complete(this) },
206 8d08f18a Kostas Papadimitriou
                error: error
207 8d08f18a Kostas Papadimitriou
            }
208 f06ec46e Kostas Papadimitriou
209 bedfe884 Kostas Papadimitriou
            params = _.extend(params, extra);
210 8d08f18a Kostas Papadimitriou
            this.sync(method, this, params);
211 8d08f18a Kostas Papadimitriou
        },
212 8d08f18a Kostas Papadimitriou
213 8d08f18a Kostas Papadimitriou
    _.extend(api, bb.Events);
214 8d08f18a Kostas Papadimitriou
215 8d08f18a Kostas Papadimitriou
    
216 8d08f18a Kostas Papadimitriou
    api.updateHandler = function(options) {
217 8d08f18a Kostas Papadimitriou
        this.cb = options.callback;
218 8d08f18a Kostas Papadimitriou
        this.limit = options.limit;
219 8d08f18a Kostas Papadimitriou
        this.timeout = options.timeout;
220 8d08f18a Kostas Papadimitriou
221 8d08f18a Kostas Papadimitriou
        this.normal_timeout = options.timeout;
222 8d08f18a Kostas Papadimitriou
        this.fast_timeout = options.fast;
223 8d08f18a Kostas Papadimitriou
224 8d08f18a Kostas Papadimitriou
        this._called = 0;
225 8d08f18a Kostas Papadimitriou
        this.interval = undefined;
226 8d08f18a Kostas Papadimitriou
        this.call_on_start = options.call_on_start || true;
227 edd1d565 Kostas Papadimitriou
228 edd1d565 Kostas Papadimitriou
        this.running = false;
229 8d08f18a Kostas Papadimitriou
        
230 8d08f18a Kostas Papadimitriou
        // wrapper
231 8d08f18a Kostas Papadimitriou
        function _cb() {
232 8d08f18a Kostas Papadimitriou
            if (this.fast_timeout == this.timeout){
233 8d08f18a Kostas Papadimitriou
                this._called++;
234 8d08f18a Kostas Papadimitriou
            }
235 8d08f18a Kostas Papadimitriou
236 8d08f18a Kostas Papadimitriou
            if (this._called >= this.limit && this.fast_timeout == this.timeout) {
237 8d08f18a Kostas Papadimitriou
                this.timeout = this.normal_timeout;
238 8d08f18a Kostas Papadimitriou
                this.setInterval()
239 8d08f18a Kostas Papadimitriou
            }
240 8d08f18a Kostas Papadimitriou
            this.cb();
241 8d08f18a Kostas Papadimitriou
        };
242 edd1d565 Kostas Papadimitriou
243 8d08f18a Kostas Papadimitriou
        _cb = _.bind(_cb, this);
244 8d08f18a Kostas Papadimitriou
245 8d08f18a Kostas Papadimitriou
        this.faster = function() {
246 8d08f18a Kostas Papadimitriou
            this.timeout = this.fast_timeout;
247 8d08f18a Kostas Papadimitriou
            this._called = 0;
248 8d08f18a Kostas Papadimitriou
            this.setInterval();
249 8d08f18a Kostas Papadimitriou
        }
250 8d08f18a Kostas Papadimitriou
251 8d08f18a Kostas Papadimitriou
        this.setInterval = function() {
252 8d08f18a Kostas Papadimitriou
            this.trigger("clear");
253 8d08f18a Kostas Papadimitriou
            window.clearInterval(this.interval);
254 edd1d565 Kostas Papadimitriou
            this.interval = window.setInterval(_cb, this.timeout);
255 edd1d565 Kostas Papadimitriou
            this.running = true;
256 8d08f18a Kostas Papadimitriou
            if (this.call_on_start) {
257 8d08f18a Kostas Papadimitriou
                _cb();
258 8d08f18a Kostas Papadimitriou
            }
259 8d08f18a Kostas Papadimitriou
        }
260 8d08f18a Kostas Papadimitriou
261 8d08f18a Kostas Papadimitriou
        this.start = function () {
262 8d08f18a Kostas Papadimitriou
            this.setInterval();
263 8d08f18a Kostas Papadimitriou
        }
264 8d08f18a Kostas Papadimitriou
265 8d08f18a Kostas Papadimitriou
        this.stop = function() {
266 8d08f18a Kostas Papadimitriou
            this.trigger("clear");
267 8d08f18a Kostas Papadimitriou
            window.clearInterval(this.interval);
268 edd1d565 Kostas Papadimitriou
            this.running = false;
269 8d08f18a Kostas Papadimitriou
        }
270 8d08f18a Kostas Papadimitriou
    }
271 8d08f18a Kostas Papadimitriou
    
272 8d08f18a Kostas Papadimitriou
    api.stop_calls = false;
273 8d08f18a Kostas Papadimitriou
274 8d08f18a Kostas Papadimitriou
    // make it eventable
275 8d08f18a Kostas Papadimitriou
    _.extend(api.updateHandler.prototype, bb.Events);
276 8d08f18a Kostas Papadimitriou
    
277 8d08f18a Kostas Papadimitriou
})(this);