﻿/*---------------
INHERITANCE
---------------*/
var Base = function() {};
Base.prototype.base = {};
Base.prototype.construct = function() {};

Base.extend = function(subclass) {
    if(typeof subclass == 'undefined') subclass = {};
    var construct = subclass.construct || this.prototype.construct;

    var foo = function() {
        construct.apply(this, arguments);
    };
    
    foo.extend = this.extend;
    var proto = this.prototype;
    
    for(p in proto) {
        foo.prototype[p] = proto[p];
    }
    
    for(p in subclass) {
        foo.prototype[p] = subclass[p];
    }
    
    for(p in proto) {
        foo.prototype.base[p] = proto[p];
    }
    
    return foo;
};

/*---------------
REST SKELETON
---------------*/
var Model = Base.extend({
    add: function(callback) {
        var resources = this.getResources();
    
        if(!resources['add']) {
            $.errorBox('core.js \'Model\' says: Undefined URI for \'add\' in Javascript object model');
            return;
        }
        
        var self = this;
        $.$post(resources['add'], this.toJSON(), function(xmlDoc) {
            var xml = $('response', xmlDoc).get(0);
            var instance = self.parseXML(xml);
            if(typeof callback == 'function') callback(instance);
        });
    },

    get: function(resource, callback) {
        if(typeof resource == 'function') {
            callback = resource;
            resource = 'get';
        }
    
        resource = resource || 'get';
        var resources = this.getResources();
    
        if(!resources[resource]) {
            $.errorBox('core.js \'Model\' says: Undefined URI for \'' + resource + '\' in Javascript object model');
            return;
        }
        
        var self = this;
        $.$get(resources[resource], function(xmlDoc) {
            var xml = $('response', xmlDoc).get(0);
            var instance = self.parseXML(xml);
            if(typeof callback == 'function') callback(instance);
        });
    },
    
    getResources: function() {
        return (typeof this.resources == 'function') ? this.resources() : this.resources;
    },
    
    each: function(callback) {
        if(!this.items || this.items.length < 1) return;
        if(typeof callback != 'function') return;
        
        for(var i = 0; i < this.items.length; i++) {
            callback(this.items[i]);
        }
    },
    
    items: [],
    
    parseJSON: function(JSON, self) {
        self = self || this;
    
        for(p in this) {
            var type = typeof this[p];
            if(p == 'base') continue;
            if(p == 'items') continue;
            if(type == 'function') continue;
            self[p] = JSON[p] || null;
        }
        
        return self;
    },
    
    parseXML: function(XML, self) {
        self = self || this;
        
        for(p in this) {
            var type = typeof this[p];
            if(p == 'base') continue;
            if(p == 'items') continue;
            if(type == 'function') continue;
            
            if($(p, XML).size() < 1) {
                self[p] = null;
                continue;
            }
            
            if(type == 'object' && this[p] != null) {
                var subsetXML = $(p, XML).html();
                self[p] = this.parseXML.call(this[p], subsetXML);
                continue;
            }

            self[p] = $(p, XML).text();
        }
        
        return self;
    },
    
    save: function(callback) {
        var resources = this.getResources();
    
        if(!resources['save']) {
            $.errorBox('core.js \'Model\' says: Undefined URI for \'save\' in Javascript object model');
            return;
        }
        
        var self = this;
        $.$post(resources['save'], this.toJSON(), function(xmlDoc) {
            var xml = $('response', xmlDoc).get(0);
            var instance = self.parseXML(xml);
            if(typeof callback == 'function') callback(instance);
        });
    },
    
    search: function(params, callback) {
        if(typeof params == 'function') {
            callback = params;
            params = null;
        }
    
        params = params || {};
        var page = params.page || 1;
        var perPage = params.perPage || 20;
        var resources = this.getResources();
    
        if(!resources['search']) {
            $.errorBox('core.js \'Model\' says: Undefined URI for \'search\' in Javascript object model');
            return;
        }
        
        var self = this;
        $.$get(resources['search'], {page: page, per_page: perPage}, function(xmlDoc) {
            var children = $('response', xmlDoc).children().not('collection_size').not('count');

            if(children.size() < 1) {
                if(typeof callback == 'function') callback();
                return;
            }

            var instances = [];        
            children.each(function(i) {
                var instance = self.parseXML(children[i], {});
                instances[i] = instance;
            });
            
            
            self.items = instances;
            var xml = $('response', xmlDoc) || $('<div></div>').append($('response', xmlDoc)).html();
            self.collection_size = $('collection_size', xml).text();
            self.count = $('count', xml).text();
            if(typeof callback == 'function') callback();
        });
    },
    
    toJSON: function() {
        var JSON = {};

        for(p in this) {
            var type = typeof this[p];
            if(p == 'base') continue;
            else if(type == 'function') continue;
            else if(this[p] == null) continue;
            else if(p == 'items' && this[p].length == 0) continue;
            else if(type == 'object') JSON[p] = this.toJSON.call(this[p]);
            else JSON[p] = this[p];
        }
        
        return JSON;
    },
    
    toXML: function() {
        var XML = '';
        
        for(p in this) {
            var type = typeof this[p];
            if(p == 'base') continue;
            if(type == 'function') continue;
            var append = '<' + p + '>';
            if(this[p] == null) continue;
            else if(p == 'items' && this[p].length == 0) continue;
            else if(typeof this[p] == 'object') append += this.toXML.call(this[p]);
            else append += this[p];            
            append += '</' + p + '>';
            XML += append;
        }
        
        return XML;
    }    
});

//------------------------------
//JQUERY EXTENSION FUNCTIONS
//------------------------------

jQuery.extend({
    //makes an AJAX request and handles errors
    $holdGet: function(url, params, callback) {
        if(typeof params == 'function') {
            callback = params;
            params = null;
        }
    
        $.hold();
        $.$get(url, params, function(xmlDoc) {
            $.holdStop(function() {
                callback(xmlDoc);
            });
        });
    },
    
    //makes an AJAX request and handles errors
    $holdPost: function(url, params, callback) {
        if(typeof params == 'function') {
            callback = params;
            params = null;
        }
    
        $.hold();
        $.$post(url, params, function(xmlDoc) {
            $.holdStop(function() {
                callback(xmlDoc);
            });
        });
    },
    
    //makes an AJAX request and handles errors
    $get: function(url, params, callback) {
        if(typeof params == 'function') {
            callback = params;
            params = null;
        }
    
        $.get(url, params, function(xmlDoc) {
            var error = $('error_message', xmlDoc).text();
            
            if(error) {
                $.errorBox(error);
                return;
            }

            if(typeof callback != 'function') return;
            callback(xmlDoc);
        });
    },
    
    //makes an AJAX request and handles errors
    $post: function(url, params, callback) {
        if(typeof params == 'function') {
            callback = params;
            params = null;
        }
    
        $.post(url, params, function(xmlDoc) {
            var error = $('error_message', xmlDoc).text();

            if(error) {
                $.errorBox(error);
                return;
            }

            if(typeof callback != 'function') return;
            callback(xmlDoc);
        });
    }
});