<commit_before>/**
 * @version     $Id$
 * @category	Nooku
 * @package     Nooku_Media
 * @subpackage  Javascript
 * @copyright   Copyright (C) 2007 - 2010 Johan Janssens. All rights reserved.
 * @license     GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
 * @link        http://www.nooku.org
 */

/**
 * Koowa global namespace
.*
 * @author      Johan Janssens <johan@nooku.org>
 * @category    Nooku
 * @package     Nooku_Media
 * @subpackage  Javascript
 */
if(!Koowa) var Koowa = {};
Koowa.version = 0.7;

/* Section: Functions */
var $get = function(key, defaultValue) {
    return location.search.get(key, defaultValue);
}

/* Section: onDomReady */
window.addEvent('domready', function() {
    $$('.submitable').addEvent('click', function(e){
        e = new Event(e);
        new Koowa.Form(Json.evaluate(e.target.getProperty('rel'))).submit();
    });

    $$('.-koowa-grid').each(function(grid){
        new Koowa.Grid(grid);

        var toolbar = grid.get('data-toolbar') ? grid.get('data-toolbar') : '.toolbar';
        new Koowa.Controller.Grid({form: grid, toolbar: document.getElement(toolbar)});
    });

    $$('.-koowa-form').each(function(form){
        var toolbar = form.get('data-toolbar') ? form.get('data-toolbar') : '.toolbar';
        new Koowa.Controller.Form({form: form, toolbar: document.getElement(toolbar)});
    });
});

/* Section: Classes */
(function(){
var $ = document.id;

/**
 * Creates a 'virtual form'
 *
 * @param   json    Configuration:  method, url, params, formelem
 * @example new KForm({method:'post', url:'foo=bar&id=1', params:{field1:'val1', field2...}}).submit();
 */
Koowa.Form = new Class({

    initialize: function(config) {
        this.config = config;
        if(this.config.element) {
            this.form = $(document[this.config.element]);
        }
        else {
            this.form = new Element('form', {
                name: 'dynamicform',
                method: this.config.method,
                action: this.config.url
            });
            this.form.injectInside($(document.body));
        }
    },

    addField: function(name, value) {
        var elem = new Element('input', {
            name: name,
            value: value,
            type: 'hidden'
        });
        elem.injectInside(this.form);
        return this;
    },

    submit: function() {
        $each(this.config.params, function(value, name){
            this.addField(name, value);
        }.bind(this));
        this.form.submit();
    }
});

/**
 * Grid class
 *
 * @package     Koowa_Media
 * @subpackage  Javascript
 */
Koowa.Grid = new Class({

    initialize: function(element){

        this.element    = $(element);
        this.form       = this.element.match('form') ? this.element : this.element.getParent('form');
        this.toggles    = this.element.getElements('.-koowa-grid-checkall');
        this.checkboxes = this.element.getElements('.-koowa-grid-checkbox');

        var self = this;
        this.toggles.addEvent('change', function(event){
            if(event) self.checkAll(this.get('checked'));
        });

        this.checkboxes.addEvent('change', function(event){
            if(event) self.uncheckAll();
        });
    },

    checkAll: function(value){

        var changed = this.checkboxes.filter(function(checkbox){
            return checkbox.get('checked') !== value;
        });

        this.checkboxes.set('checked', value);

        changed.fireEvent('change');

    },

    uncheckAll: function(){

        var total = this.checkboxes.filter(function(checkbox){
        	return checkbox.get('checked') !== false ;
        }).length;

        this.toggles.set('checked', this.checkboxes.length === total);
        this.toggles.fireEvent('change');

    }
});
/**
 * Find all selected checkboxes' ids in the grid
 *
 * @return  array   The items' ids
 */
Koowa.Grid.getAllSelected = function() {
        var result = new Array;
        var inputs = $$('input[class^=-koowa-grid-checkbox]');
        for (var i=0; i < inputs.length; i++) {
           if (inputs[i].checked) {
              result.include(inputs[i]);
           }
        }
        return result;
};
Koowa.Grid.getIdQuery = function() {
        var result = new Array();
        $each(this.getAllSelected(), function(selected){
            result.include(selected.name+'='+selected.value);
        });
        return result.join('&');
};

/**
 * Controller class, execute actions complete with command chains
 *
 * @package     Koowa_Media
 * @subpackage  Javascript
 */
Koowa.Controller = new Class({

    Implements: [Options, Events],

	form: null,
	toolbar: null,
	buttons: null,

	options: {
		toolbar: false,
		url: window.location.href
	},

	initialize: function(options){

		this.setOptions(options);

		this.form = this.options.form;
		this.toolbar = this.options.toolbar || this.form;

        //Set options that is coming from data attributes on the form element
        this.setOptions(this.getOptions(this.form));

		this.form.store('controller', this);

		//Allows executing actions on the form element itself using fireEvent
		this.form.addEvent('execute', this.execute.bind(this));

		//Attach toolbar buttons actions
		this.buttons = this.toolbar.getElements('.toolbar').filter(function(button){
		    return button.get('data-action');
		});
		var token_name = this.form.get('data-token-name'), token_value = this.form.get('data-token-value');
		this.buttons.each(function(button){
			var data = button.get('data-data'), options = this.getOptions(button), action = button.get('data-action');
		    data = data ? JSON.decode(data) : {};

		    //Set token data
		    if(token_name) data[token_name] = token_value;

		    button.addEvent('click', function(){
		        if(!button.hasClass('disabled')) {
		            this.setOptions(options);
		            this.fireEvent('execute', [action, data, button.get('data-novalidate') == 'novalidate']);
		        }
		    }.bind(this));

		}, this);
    },

    execute: function(action, data, novalidate){
    	var method = '_action'+action.capitalize();

		this.options.action = action;
		if(this.fireEvent('before.'+action, [data, novalidate])) {
		    this[method] ? this[method].call(this, data) : this._action_default.call(this, action, data, novalidate);
		    this.fireEvent('after.'+action, [data, novalidate])
		}

    	return this;
    },

    addEvent: function(type, fn, internal){

        return this.form.addEvent.apply(this.form, [type, fn, internal]);

    },

    fireEvent: function(type, args, delay){
		var events = this.form.retrieve('events');
		if (!events || !events[type]) return this;
		var result = events[type].keys.map(function(fn){
			return fn.create({'bind': this, 'delay': delay, 'arguments': args})() !== false;
		}, this).every(function(v){ return v;});
		return result;
	},

	checkValidity: function(){
	    var buttons = this.buttons.filter(function(button){
	        return button.get('data-novalidate') != 'novalidate';
	    }, this);

	    /* We use a class for this state instead of a data attribute because not all browsers supports attribute selectors */
	    if(this.fireEvent('validate')) {
	        buttons.removeClass('disabled');
	    } else {
	        buttons.addClass('disabled');
	    }
	},

	getOptions: function(element){
	    var options = {};
	    if(element.datasets) {
	        for(key in element.dataset){
	            options[key] = element.dataset[key];
	        }
	    } else {
	        for (i = 0, length = element.attributes.length; i < length; i++){
	            var key = element.attributes[i].name;
	            if(key.substring && key.substring(0, 5) == 'data-') {
	                var name = key.substring(5, key.length).camelCase();
    	            options[name] = element.attributes[i].value;
    	        }
	        }
	    }
	    return options;
	}
});

/**
 * Controller class specialized for grids, extends Koowa.Controller
 *
 * @package     Koowa_Media
 * @subpackage  Javascript
 */
Koowa.Controller.Grid = new Class({

    Extends: Koowa.Controller,

    options: {
        inputs: '.-koowa-grid-checkbox'
    },

    initialize: function(options){

        this.parent(options);

        this.addEvent('validate', this.validate);

        //Perform grid validation and set the right classes on toolbar buttons
        if(this.options.inputs) {
            //This is to allow CSS3 transitions without those animating onload without user interaction
            this.buttons.addClass('beforeload');
            this.checkValidity();
            //Remove the class 1ms afterwards, which is enough for bypassing css transitions onload
            this.buttons.removeClass.delay(1, this.buttons, ['beforeload']);
            this.form.getElements(this.options.inputs).addEvent('change', this.checkValidity.bind(this));
        }

        //<select> elements in headers and footers are for filters, so they need to submit the form on change
        this.form.getElements('thead select, tfoot select').addEvent('change', this.form.submit.bind(this.form));

        //Pick up actions that are in the grid itself
        var token_name = this.form.get('data-token-name'),
            token_value = this.form.get('data-token-value'),
            checkboxes = this.form.getElements('tbody tr .-koowa-grid-checkbox');
        this.form.getElements('tbody tr').each(function(tr){

            //skip rows that are readonly
            if(tr.get('data-readonly') == 1) {
            	return;
            }

            var checkbox = tr.getElement('.-koowa-grid-checkbox');
            if(!checkbox) return;
            var id = {name: checkbox.get('name'), value: checkbox.get('value')},
                //Attributes with hyphens don't work with the MT 1.2 selector engine, it's fixed in 1.3 so this is a workaround
                actions = tr.getElements('*').filter(function(action){
                    return action.get('data-action');
                });

            actions.each(function(action){
                var data = action.get('data-data'),
                    options = this.getOptions(action),
                    actionName = action.get('data-action');
                    data = data ? JSON.decode(data) : {},
                    eventType = action.get('data-event-type');

                //Set token data
                if(token_name) data[token_name] = token_value;

                if(!eventType) {
                    var onchange = ['[type="radio"]', '[type="checkbox"]', 'select'].filter(function(test){
                            return action.match(test);
                        });

                    eventType = onchange.length ? 'change' : 'click';
                }

                action.addEvent(eventType, function(){
                    checkboxes.set('checked', '');
                    checkbox.set('checked', 'checked');
                    checkboxes.fireEvent('change');

                    this.setOptions(options);
                    this.fireEvent('execute', [actionName, data, true]);
                }.bind(this));

            }, this);

        }, this);
    },

    validate: function(){
        if(!Koowa.Grid.getIdQuery()) return false;
    },

    _action_default: function(action, data, novalidate){
        if(!novalidate && !this.fireEvent('validate')) return false;

        var idQuery = Koowa.Grid.getIdQuery(),
            options = {
                method:'post',
                url: this.options.url+(idQuery ? '&'+idQuery : ''),
                params: $merge({
                    action: action
                }, data)
            };
    	new Koowa.Form(options).submit();
    }

});

/**
 * Controller class specialized for forms, extends Koowa.Controller
 *
 * @package     Koowa_Media
 * @subpackage  Javascript
 */
Koowa.Controller.Form = new Class({

    Extends: Koowa.Controller,

    _action_default: function(action, data, novalidate){
        if(!novalidate && !this.fireEvent('validate')) return false;

        this.form.adopt(new Element('input', {name: 'action', type: 'hidden', value: action}));
        this.form.submit();
    }

});

/**
 * Query class
 *
 * @package     Koowa_Media
 * @subpackage  Javascript
 */
Koowa.Query = new Class({

    toString: function() {
        var result = [];

        for (var key in this) {
            // make sure it's not a function
            if (!(this[key] instanceof Function)) {
                // we only go one level deep for now
                if(this[key] instanceof Object) {
                    for (var subkey in this[key]) {
                        result.push(key + '[' + subkey + ']' + '=' + this[key][subkey]);
                    }
                } else {
                    result.push(key + '=' + this[key]);
                }
            }
        }

        return result.join('&');
    }
});

/**
 * Overlay class
 *
 * @package     Koowa_Media
 * @subpackage  Javascript
 */
Koowa.Overlay = new Class({
	Extends: Request,
    element : null,

    options: {
        method      : 'get',
        evalScripts : true,
        evalStyles  : true,

        onComplete: function() {
            var element = new Element('div', {html: this.response.text});
            element.getElement('[id='+this.element.id+']').replaces(this.element);
            if (this.options.evalScripts) {
                scripts = element.getElementsBySelector('script[type=text/javascript]');
                scripts.each(function(script) {
                    new Asset.javascript(script.src, {id: script.id });
                    script.remove();
                }.bind(this))
            }

            if (this.options.evalStyles) {
                styles  = element.getElementsBySelector('link[type=text/css]');
                styles.each(function(style) {
                    new Asset.css(style.href, {id: style.id });
                    style.remove();
                }.bind(this))
            }
        }
    },

    initialize: function(element, options) {
        if(typeof options == 'string') {
            var options = Json.evaluate(options);
        }

        this.element = $(element);
        this.options.url = element.getAttribute('href');
        this.parent(options);

        this.send();
    }
});
})();

/**
 * String class
 *
 * @package     Koowa_Media
 * @subpackage  Javascript
 */
String.extend({
    get : function(key, defaultValue) {
        if(key == "") return;

        var uri   = this.parseUri();
        if($defined(uri['query'])) {
            var query = uri['query'].parseQueryString();
            if($defined(query[key])) {
                return query[key];
            }
        }

        return defaultValue;
    },

    parseUri: function() {
        var bits = this.match(/^(?:([^:\/?#.]+):)?(?:\/\/)?(([^:\/?#]*)(?::(\d*))?)((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[\?#]|$)))*\/?)?([^?#\/]*))?(?:\?([^#]*))?(?:#(.*))?/);
        return (bits)
            ? bits.associate(['uri', 'scheme', 'authority', 'domain', 'port', 'path', 'directory', 'file', 'query', 'fragment'])
            : null;
    }
});<commit_msg>JSLint audit refactor<commit_after>/**
 * @version     $Id$
 * @category	Nooku
 * @package     Nooku_Media
 * @subpackage  Javascript
 * @copyright   Copyright (C) 2007 - 2010 Johan Janssens. All rights reserved.
 * @license     GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
 * @link        http://www.nooku.org
 */

/**
 * Koowa global namespace
 *
 * @author      Johan Janssens <johan@nooku.org>
 * @category    Nooku
 * @package     Nooku_Media
 * @subpackage  Javascript
 */
var Koowa = {
    version: '0.7Alpha3'
};

/* Section: onDomReady */
window.addEvent('domready', function() {
    $$('.submitable').addEvent('click', function(event){
        event = new Event(event);
        new Koowa.Form(Json.evaluate(event.target.getProperty('rel'))).submit();
    });

    $$('.-koowa-grid').each(function(grid){
        new Koowa.Grid(grid);

        var toolbar = grid.get('data-toolbar') || '.toolbar';
        new Koowa.Controller.Grid({form: grid, toolbar: document.getElement(toolbar)});
    });

    $$('.-koowa-form').each(function(form){
        var toolbar = form.get('data-toolbar') || '.toolbar';
        new Koowa.Controller.Form({form: form, toolbar: document.getElement(toolbar)});
    });
});

/* Section: Classes */

/**
 * Creates a 'virtual form'
 *
 * @param   json    Configuration:  method, url, params, formelem
 * @example new KForm({method:'post', url:'foo=bar&id=1', params:{field1:'val1', field2...}}).submit();
 */
Koowa.Form = new Class({

    initialize: function(config) {
        this.config = config;
        if(this.config.element) {
            this.form = document.id(document[this.config.element]);
        }
        else {
            this.form = new Element('form', {
                name: 'dynamicform',
                method: this.config.method,
                action: this.config.url
            });
            this.form.injectInside(document.id(document.body));
        }
    },

    addField: function(name, value) {
        var elem = new Element('input', {
            name: name,
            value: value,
            type: 'hidden'
        });
        elem.injectInside(this.form);
        return this;
    },

    submit: function() {
        $each(this.config.params, function(value, name){
            this.addField(name, value);
        }.bind(this));
        this.form.submit();
    }
});

/**
 * Grid class
 *
 * @package     Koowa_Media
 * @subpackage  Javascript
 */
Koowa.Grid = new Class({

    initialize: function(element){

        this.element    = document.id(element);
        this.form       = this.element.match('form') ? this.element : this.element.getParent('form');
        this.toggles    = this.element.getElements('.-koowa-grid-checkall');
        this.checkboxes = this.element.getElements('.-koowa-grid-checkbox');

        var self = this;
        this.toggles.addEvent('change', function(event){
            if(event) {
                self.checkAll(this.get('checked'));
            }
        });

        this.checkboxes.addEvent('change', function(event){
            if(event) {
                self.uncheckAll();
            }
        });
    },

    checkAll: function(value){

        var changed = this.checkboxes.filter(function(checkbox){
            return checkbox.get('checked') !== value;
        });

        this.checkboxes.set('checked', value);

        changed.fireEvent('change');

    },

    uncheckAll: function(){

        var total = this.checkboxes.filter(function(checkbox){
            return checkbox.get('checked') !== false ;
        }).length;

        this.toggles.set('checked', this.checkboxes.length === total);
        this.toggles.fireEvent('change');

    }
});
/**
 * Find all selected checkboxes' ids in the grid
 *
 * @return  array   The items' ids
 */
Koowa.Grid.getAllSelected = function() {
        var result = [], inputs = $$('input[class^=-koowa-grid-checkbox]'), i;
        for (i=0; i < inputs.length; i++) {
           if (inputs[i].checked) {
              result.include(inputs[i]);
           }
        }
        return result;
};
Koowa.Grid.getIdQuery = function() {
        var result = [];
        $each(this.getAllSelected(), function(selected){
            result.include(selected.name+'='+selected.value);
        });
        return result.join('&');
};

/**
 * Controller class, execute actions complete with command chains
 *
 * @package     Koowa_Media
 * @subpackage  Javascript
 */
Koowa.Controller = new Class({

    Implements: [Options, Events],

    form: null,
    toolbar: null,
    buttons: null,

    options: {
        toolbar: false,
        url: window.location.href
    },

    initialize: function(options){

        this.setOptions(options);

        this.form = this.options.form;
        this.toolbar = this.options.toolbar || this.form;

        //Set options that is coming from data attributes on the form element
        this.setOptions(this.getOptions(this.form));

        this.form.store('controller', this);

        //Allows executing actions on the form element itself using fireEvent
        this.form.addEvent('execute', this.execute.bind(this));

        //Attach toolbar buttons actions
        this.buttons = this.toolbar.getElements('.toolbar').filter(function(button){
            return button.get('data-action');
        });
        var token_name = this.form.get('data-token-name'), token_value = this.form.get('data-token-value');
        this.buttons.each(function(button){
            var data = button.get('data-data'), options = this.getOptions(button), action = button.get('data-action');
            data = data ? JSON.decode(data) : {};

            //Set token data
            if(token_name) {
                data[token_name] = token_value;
            }

            button.addEvent('click', function(){
                if(!button.hasClass('disabled')) {
                    this.setOptions(options);
                    this.fireEvent('execute', [action, data, button.get('data-novalidate') === 'novalidate']);
                }
            }.bind(this));

        }, this);
    },

    execute: function(action, data, novalidate){
        var method = '_action'+action.capitalize();

        this.options.action = action;
        if(this.fireEvent('before.'+action, [data, novalidate])) {
            if(this[method]) {
                this[method].call(this, data, novalidate);
            } else {
                this._action_default.call(this, action, data, novalidate);
            }
            this.fireEvent('after.'+action, [data, novalidate]);
        }

        return this;
    },

    addEvent: function(type, fn, internal){

        return this.form.addEvent.apply(this.form, [type, fn, internal]);

    },

    fireEvent: function(type, args, delay){
        var events = this.form.retrieve('events'), result;
        if (!events || !events[type]) {
            return this;
        }
        result = events[type].keys.map(function(fn){
            return fn.create({'bind': this, 'delay': delay, 'arguments': args})() !== false;
        }, this).every(function(v){ return v;});
        return result;
    },

    checkValidity: function(){
        var buttons = this.buttons.filter(function(button){
            return button.get('data-novalidate') !== 'novalidate';
        }, this);

        /* We use a class for this state instead of a data attribute because not all browsers supports attribute selectors */
        if(this.fireEvent('validate')) {
            buttons.removeClass('disabled');
        } else {
            buttons.addClass('disabled');
        }
    },

    getOptions: function(element){
        var options = {}, i = 0, total, key, name;
        if(element.dataset) {
            for(key in element.dataset){
                options[key] = element.dataset[key];
            }
        } else {
            total = element.attributes.length;
            for (i = 0; i < total; i++){
                key = element.attributes[i].name;
                if(key.substring && key.substring(0, 5) === 'data-') {
                    name = key.substring(5, key.length).camelCase();
                    options[name] = element.attributes[i].value;
                }
            }
        }
        return options;
    }
});

/**
 * Controller class specialized for grids, extends Koowa.Controller
 *
 * @package     Koowa_Media
 * @subpackage  Javascript
 */
Koowa.Controller.Grid = new Class({

    Extends: Koowa.Controller,

    options: {
        inputs: '.-koowa-grid-checkbox'
    },

    initialize: function(options){

        this.parent(options);

        this.addEvent('validate', this.validate);

        //Perform grid validation and set the right classes on toolbar buttons
        if(this.options.inputs) {
            //This is to allow CSS3 transitions without those animating onload without user interaction
            this.buttons.addClass('beforeload');
            this.checkValidity();
            //Remove the class 1ms afterwards, which is enough for bypassing css transitions onload
            this.buttons.removeClass.delay(1, this.buttons, ['beforeload']);
            this.form.getElements(this.options.inputs).addEvent('change', this.checkValidity.bind(this));
        }

        //<select> elements in headers and footers are for filters, so they need to submit the form on change
        this.form.getElements('thead select, tfoot select').addEvent('change', this.form.submit.bind(this.form));

        //Pick up actions that are in the grid itself
        var token_name = this.form.get('data-token-name'),
            token_value = this.form.get('data-token-value'),
            checkboxes = this.form.getElements('tbody tr .-koowa-grid-checkbox');
        this.form.getElements('tbody tr').each(function(tr){

            //skip rows that are readonly
            if(tr.get('data-readonly')) {
                return;
            }

            var checkbox = tr.getElement('.-koowa-grid-checkbox'), id, actions;
            if(!checkbox) {
                return;
            }

            id = {name: checkbox.get('name'), value: checkbox.get('value')};
            //Attributes with hyphens don't work with the MT 1.2 selector engine, it's fixed in 1.3 so this is a workaround
            actions = tr.getElements('*').filter(function(action){
                return action.get('data-action');
            });

            actions.each(function(action){
                var data = action.get('data-data'),
                    options = this.getOptions(action),
                    actionName = action.get('data-action'),
                    eventType = action.get('data-event-type'),
                    onchange;

                data = data ? JSON.decode(data) : {};

                //Set token data
                if(token_name) {
                    data[token_name] = token_value;
                }

                if(!eventType) {
                    onchange = ['[type="radio"]', '[type="checkbox"]', 'select'].filter(function(test){
                        return action.match(test);
                    });

                    eventType = onchange.length ? 'change' : 'click';
                }

                action.addEvent(eventType, function(){
                    checkboxes.set('checked', '');
                    checkbox.set('checked', 'checked');
                    checkboxes.fireEvent('change');

                    this.setOptions(options);
                    this.fireEvent('execute', [actionName, data, true]);
                }.bind(this));

            }, this);

        }, this);
    },

    validate: function(){
        return Koowa.Grid.getIdQuery() || false;
    },

    _action_default: function(action, data, novalidate){
        if(!novalidate && !this.fireEvent('validate')) {
            return false;
        }

        var idQuery = Koowa.Grid.getIdQuery(),
            options = {
                method:'post',
                url: this.options.url+(idQuery ? '&'+idQuery : ''),
                params: $merge({
                    action: action
                }, data)
            };
        new Koowa.Form(options).submit();
    }

});

/**
 * Controller class specialized for forms, extends Koowa.Controller
 *
 * @package     Koowa_Media
 * @subpackage  Javascript
 */
Koowa.Controller.Form = new Class({

    Extends: Koowa.Controller,

    _action_default: function(action, data, novalidate){
        if(!novalidate && !this.fireEvent('validate')) {
            return false;
        }

        this.form.adopt(new Element('input', {name: 'action', type: 'hidden', value: action}));
        this.form.submit();
    }

});

/**
 * Query class
 *
 * @package     Koowa_Media
 * @subpackage  Javascript
 */
Koowa.Query = new Class({

    toString: function() {
        var result = [], key, subkey;

        for (key in this) {
            // make sure it's not a function
            if (!(this[key] instanceof Function)) {
                // we only go one level deep for now
                if(this[key] instanceof Object) {
                    for (subkey in this[key]) {
                        result.push(key + '[' + subkey + ']' + '=' + this[key][subkey]);
                    }
                } else {
                    result.push(key + '=' + this[key]);
                }
            }
        }

        return result.join('&');
    }
});

/**
 * Overlay class
 *
 * @package     Koowa_Media
 * @subpackage  Javascript
 */
Koowa.Overlay = new Class({
    Extends: Request,
    element : null,

    options: {
        method: 'get',
        evalScripts: true,
        evalStyles: true,

        onComplete: function() {
            var element = new Element('div', {html: this.response.text}), scripts, styles;
            element.getElement('[id='+this.element.id+']').replaces(this.element);
            if (this.options.evalScripts) {
                scripts = element.getElementsBySelector('script[type=text/javascript]');
                scripts.each(function(script) {
                    new Asset.javascript(script.src, {id: script.id });
                    script.remove();
                }.bind(this));
            }

            if (this.options.evalStyles) {
                styles = element.getElementsBySelector('link[type=text/css]');
                styles.each(function(style) {
                    new Asset.css(style.href, {id: style.id });
                    style.remove();
                }.bind(this));
            }
        }
    },

    initialize: function(element, options) {
        if(typeof options === 'string') {
            options = Json.evaluate(options);
        }

        this.element = document.id(element);
        this.options.url = element.getAttribute('href');
        this.parent(options);

        this.send();
    }
});

/**
 * String class
 *
 * @package     Koowa_Media
 * @subpackage  Javascript
 */
String.extend({
    get : function(key, defaultValue) {
        if(!key) {
            return;
        }

        var uri   = this.parseUri(), query;
        if(typeof uri.query !== 'undefined') {
            query = uri.query.parseQueryString();
            if(typeof query[key] !== 'undefined') {
                return query[key];
            }
        }

        return defaultValue;
    },

    parseUri: function() {
        var bits = this.match(/^(?:([^:\/?#.]+):)?(?:\/\/)?(([^:\/?#]*)(?::(\d*))?)((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[\?#]|$)))*\/?)?([^?#\/]*))?(?:\?([^#]*))?(?:#(.*))?/);
        return (bits)
            ? bits.associate(['uri', 'scheme', 'authority', 'domain', 'port', 'path', 'directory', 'file', 'query', 'fragment'])
            : null;
    }
});<|endoftext|>


<commit_before>import React, {Component} from 'react'
import fetch from './fetch'
import {parse} from 'markdown'

export class Text extends Component {

  constructor(props) {
    super(props)

    const {body} = this.props

    this.state = {
      text: {
        __html: body
      }
    }
  }

  componentDidMount () {
    const {file} = this.props

    if (file) {
      fetch(file, null).then(res => {
        this.setState({
          text: {
            __html: parse(res)
          }
        })
      })
    }

  }

  render () {
    const {title} = this.props
    const {text} = this.state

    return (

          {title}

    )
  }
}
<commit_msg>clean up<commit_after>import {parse} from 'markdown'
import React, {Component} from 'react'
import fetch from './fetch'

export class Text extends Component {

  constructor(props) {
    super(props)

    const {body} = this.props

    this.state = {
      text: {
        __html: body
      }
    }
  }

  componentDidMount () {
    const {file} = this.props

    if (file) {
      fetch(file, null).then(res => {
        this.setState({
          text: {
            __html: parse(res)
          }
        })
      })
    }

  }

  render () {
    const {title} = this.props
    const {text} = this.state

    return (

          {title}

    )
  }
}
<|endoftext|>


<commit_before>this.Backbone.Model = (function ( Model, _ ) {

  'use strict';

  return Model.extend({

    _computedProps: {},

    // Override Boostrap's default constructor, setting up listeners for dependencies.
    constructor: function () {
      var classProps = this._computedProps;
      var computedProps = this._computedProps = {};
      var prop, propSpec, l;

      this._cachedProps = {};

      for ( prop in classProps ) {
        if ( classProps.hasOwnProperty( prop ) ) {
          propSpec = classProps[ prop ];
          this.addProperty( prop, propSpec.deps, propSpec.action );
        }
      }

      return Model.apply( this, arguments );
    },

    /*
       .addProperty( name, [deps ,] action )

         name:   The name of the computed property.
         deps:   List of properties the new property should
                 depend on.
         action: A function for getting and setting the
                 computed property.

       Adds a new computed property to the model instance.
     */

    addProperty: function ( name, deps, action ) {
      if ( action === undefined ) {
        action = deps;
        deps = [];
      }

      var computedProps = this._computedProps;

      computedProps[ name ] = {
        action: action,
        deps: deps
      };

      this.setupDepsListeners( name, deps );

      return this;
    },

    get: _.wrap( Model.prototype.get, function ( get, attr ) {
      var computedProps = this._computedProps;
      var newValue;

      if ( computedProps[ attr ] ) {
        newValue = computedProps[ attr ].action.call( this );
        this._cachedProps[ attr ] = newValue;
        return newValue;
      }

      return get.call( this, attr );
    }),

    set: _.wrap( Model.prototype.set, function ( set, attr, values ) {
      var computedProps = this._computedProps;
      var newValue;

      if ( computedProps[ attr ] ) {
        newValue = computedProps[ attr ].action.call( this, values );
        if ( this._cachedProps[ attr ] !== newValue ) {
          this._cachedProps[ attr ] = newValue;
          this.trigger( 'change:' + attr, this, attr );
        }
        return this;
      }

      return set.call( this, attr, values );
    }),

    setupDepsListeners: function setupDepsListeners( prop, deps ) {
      deps = deps || [];
      var l = deps.length;
      while ( l-- ) {
        this.on( 'change:' + deps[ l ], this.triggerPropChange( prop ) );
      }
    },

    triggerPropChange: function ( prop ) {
      var context = this;
      return function () {
        context.trigger( 'change:' + prop, context, prop );
      };
    }

  }, {

    // Override Bootstrap.Model's extend
    extend: _.wrap( Model.extend, function ( extend, properties, classProperties ) {
      var prop, action, propSpec, deps;

      properties = properties || {};

      // Setup computed properties
      var computedProps = this.normalizeComputedProps( properties.properties );
      delete properties.properties;

      // Extend our class from Boostrap.Model
      var Class = extend.call( this, properties, classProperties );

      _.extend( Class.prototype, {
        _computedProps: computedProps
      });

      return Class;
    }),

    normalizeComputedProps: function ( computedProps ) {
      var propSpec, action, prop, deps;
      var normalizedProps = {};

      if ( ! computedProps ) {
        return {};
      }

      for ( prop in computedProps ) {
        if ( computedProps.hasOwnProperty( prop ) ) {
          propSpec = computedProps[ prop ];

          if ( propSpec instanceof Function ) {
            action = propSpec;
            deps = [];
          } else {
            action = propSpec.action;
            deps = propSpec.depends || [];
          }

          normalizedProps[ prop ] = {
            action: action,
            deps: deps
          };
        }
      }

      return normalizedProps;
    }

  });

}( this.Backbone.Model, this._ ));
<commit_msg>Clean up .setupDepsListeners<commit_after>this.Backbone.Model = (function ( Model, _ ) {

  'use strict';

  return Model.extend({

    _computedProps: {},

    // Override Boostrap's default constructor, setting up listeners for dependencies.
    constructor: function () {
      var classProps = this._computedProps;
      var computedProps = this._computedProps = {};
      var prop, propSpec, l;

      this._cachedProps = {};

      for ( prop in classProps ) {
        if ( classProps.hasOwnProperty( prop ) ) {
          propSpec = classProps[ prop ];
          this.addProperty( prop, propSpec.deps, propSpec.action );
        }
      }

      return Model.apply( this, arguments );
    },

    /*
       .addProperty( name, [deps ,] action )

         name:   The name of the computed property.
         deps:   List of properties the new property should
                 depend on.
         action: A function for getting and setting the
                 computed property.

       Adds a new computed property to the model instance.
     */

    addProperty: function ( name, deps, action ) {
      if ( action === undefined ) {
        action = deps;
        deps = [];
      }

      var computedProps = this._computedProps;

      computedProps[ name ] = {
        action: action,
        deps: deps
      };

      this.setupDepsListeners( name, deps );

      return this;
    },

    get: _.wrap( Model.prototype.get, function ( get, attr ) {
      var computedProps = this._computedProps;
      var newValue;

      if ( computedProps[ attr ] ) {
        newValue = computedProps[ attr ].action.call( this );
        this._cachedProps[ attr ] = newValue;
        return newValue;
      }

      return get.call( this, attr );
    }),

    set: _.wrap( Model.prototype.set, function ( set, attr, values ) {
      var computedProps = this._computedProps;
      var newValue;

      if ( computedProps[ attr ] ) {
        newValue = computedProps[ attr ].action.call( this, values );
        if ( this._cachedProps[ attr ] !== newValue ) {
          this._cachedProps[ attr ] = newValue;
          this.trigger( 'change:' + attr, this, attr );
        }
        return this;
      }

      return set.call( this, attr, values );
    }),

    setupDepsListeners: function setupDepsListeners( prop, deps ) {
      deps = deps || [];

      var context = this;
      var triggerPropChange = function () {
        context.trigger( 'change:' + prop, context, prop );
      };

      var l = deps.length;
      while ( l-- ) {
        this.on( 'change:' + deps[ l ], triggerPropChange );
      }
    }

  }, {

    // Override Bootstrap.Model's extend
    extend: _.wrap( Model.extend, function ( extend, properties, classProperties ) {
      var prop, action, propSpec, deps;

      properties = properties || {};

      // Setup computed properties
      var computedProps = this.normalizeComputedProps( properties.properties );
      delete properties.properties;

      // Extend our class from Boostrap.Model
      var Class = extend.call( this, properties, classProperties );

      _.extend( Class.prototype, {
        _computedProps: computedProps
      });

      return Class;
    }),

    normalizeComputedProps: function ( computedProps ) {
      var propSpec, action, prop, deps;
      var normalizedProps = {};

      if ( ! computedProps ) {
        return {};
      }

      for ( prop in computedProps ) {
        if ( computedProps.hasOwnProperty( prop ) ) {
          propSpec = computedProps[ prop ];

          if ( propSpec instanceof Function ) {
            action = propSpec;
            deps = [];
          } else {
            action = propSpec.action;
            deps = propSpec.depends || [];
          }

          normalizedProps[ prop ] = {
            action: action,
            deps: deps
          };
        }
      }

      return normalizedProps;
    }

  });

}( this.Backbone.Model, this._ ));
<|endoftext|>


<commit_before>'use strict';

function loadDependenciesFromHub(codeManagerHost, dependencies, cb) {
    // TODO
    console.warn('Famous Hub-based dependency loading not yet implemented');
    cb(null, dependencies);
}

module.exports = {
    loadDependenciesFromHub: loadDependenciesFromHub
};
<commit_msg>Remove log message that seems scary to newcomers<commit_after>'use strict';

function loadDependenciesFromHub(codeManagerHost, dependencies, cb) {
    // TODO
    cb(null, dependencies);
}

module.exports = {
    loadDependenciesFromHub: loadDependenciesFromHub
};
<|endoftext|>


<commit_before>var inside = require('point-in-polygon');
var URI = require('uri-js');

var GeoJSON = module.exports = {

///////////////////////////////////////////////////////////////////// VALIDATION

    isGeoJSON: function ( x ) {
        // Validate a GeoJSON object.
        return (
            GeoJSON.isGeometry(x) ||
            GeoJSON.isFeature(x)  ||
            GeoJSON.isFeatureCollection(x)
        );
    },

    isGeometry: function ( x ) {
        // Validate a GeoJSON Geometry.
        return (
            GeoJSON.isPoint(x) ||
            GeoJSON.isMultiPoint(x) ||
            GeoJSON.isLineString(x) ||
            GeoJSON.isMultiLineString(x) ||
            GeoJSON.isPolygon(x) ||
            GeoJSON.isMultiPolygon(x) ||
            GeoJSON.isGeometryCollection(x)
        );
    },

    isPosition: function ( x ) {
        // Validate a GeoJSON Position.
        return (
            Array.isArray(x) &&
            x.length > 1 &&
            x.every(
                function ( element ) {
                    return (typeof element === 'number');
                }
            )
        );
    },

    isPointPosition: function ( x ) {
        // Validate the coordinates of a GeoJSON Point.
        return GeoJSON.isPosition(x);
    },

    isMultiPointPosition: function ( x ) {
        // Validate the coordinates of a GeoJSON MultiPoint.
        return (
            Array.isArray(x) &&
            x.every(GeoJSON.isPosition)
        );
    },

    isLineStringPosition: function ( x ) {
        // Validate the coordinates of a GeoJSON LineString.
        return (
            Array.isArray(x) &&
            x.length > 1 &&
            GeoJSON.isMultiPointPosition(x)
        );
    },

    isLinearRingPosition: function ( x ) {
        // Validate the coordinates of a GeoJSON LinearRing.
        return (
            Array.isArray(x) &&
            x.length > 3 &&
            GeoJSON.isLineStringPosition(x) &&
            GeoJSON.equalPositions(x[0],x[x.length-1])
        );
    },

    isMultiLineStringCoordinates: function ( x ) {
        // Validate the coordinates of a GeoJSON MultiLineString.
        return (
            Array.isArray(x) &&
            x.every(GeoJSON.isLineStringPosition)
        );
    },

    isPolygonCoordinates: function ( x ) {
        // Validate the coordinates of a GeoJSON Polygon.
        return (
            Array.isArray(x) &&
            x.every(GeoJSON.isLinearRingPosition) &&
            x.every(
                function ( LinearRingCoordinates , i , PolygonCoordinates ) {
                    if (i == 0) return true;
                    else return GeoJSON.containedPolygon(
                        LinearRingCoordinates,
                        PolygonCoordinates[0]
                    );
                }
            )
        );
    },

    isMultiPolygonCoordinates: function ( x ) {
        // Validate the coordinates of a GeoJSON MultiPolygon.
        return (
            Array.isArray(x) &&
            x.every(GeoJSON.isPolygonCoordinates)
        );
    },

    isPoint: function ( x ) {
        // Validate a GeoJSON Point.
        return (
            x != null &&
            x.type === 'Point' &&
            GeoJSON.isPointPosition(x.coordinates) &&
            GeoJSON.validCRS(x) &&
            GeoJSON.validBbox(x)
        );
    },

    isMultiPoint: function ( x ) {
        // Validate a GeoJSON MultiPoint.
        return (
            x != null &&
            x.type === 'MultiPoint' &&
            GeoJSON.isMultiPointPosition(x.coordinates) &&
            GeoJSON.validCRS(x) &&
            GeoJSON.validBbox(x)
        );
    },

    isLineString: function ( x ) {
        // Validate a GeoJSON LineString.
        return (
            x != null &&
            x.type === 'LineString' &&
            GeoJSON.isLineStringPosition(x.coordinates) &&
            GeoJSON.validCRS(x) &&
            GeoJSON.validBbox(x)
        );
    },

    isMultiLineString: function ( x ) {
        // Validate a GeoJSON MultiLineString.
        return (
            x != null &&
            x.type === 'MultiLineString' &&
            GeoJSON.isMultiLineStringCoordinates(x.coordinates) &&
            GeoJSON.validCRS(x) &&
            GeoJSON.validBbox(x)
        );
    },

    isPolygon: function ( x ) {
        // Validate a GeoJSON Polygon.
        return (
            x != null &&
            x.type === 'Polygon' &&
            GeoJSON.isPolygonCoordinates(x.coordinates) &&
            GeoJSON.validCRS(x) &&
            GeoJSON.validBbox(x)
        );
    },

    isMultiPolygon: function ( x ) {
        // Validate a GeoJSON MultiPolygon.
        return (
            x != null &&
            x.type === 'MultiPolygon' &&
            GeoJSON.isMultiPolygonCoordinates(x.coordinates) &&
            GeoJSON.validCRS(x) &&
            GeoJSON.validBbox(x)
        );
    },

    isGeometryCollection: function ( x ) {
        // Validate a GeoJSON GeometryCollection.
        return (
            x != null &&
            x.type === 'GeometryCollection' &&
            Array.isArray(x.geometries) &&
            x.geometries.every(GeoJSON.isGeometry) &&
            GeoJSON.validCRS(x) &&
            GeoJSON.validBbox(x)
        );
    },

    isFeature: function ( x ) {
        // Validate a GeoJSON Feature.
        return (
            x != null &&
            x.type === 'Feature' &&
            typeof x.properties === 'object' &&
            GeoJSON.validCRS(x) &&
            GeoJSON.validBbox(x) &&
            (
                x.geometry === null ||
                GeoJSON.isGeometry(x.geometry)
            )
        );
    },

    isFeatureCollection: function ( x ) {
        // Validate a GeoJSON FeatureCollection.
        return (
            x != null &&
            x.type === 'FeatureCollection' &&
            Array.isArray(x.features) &&
            x.features.every(GeoJSON.isFeature) &&
            GeoJSON.validCRS(x) &&
            GeoJSON.validBbox(x)
        );
    },

    isCRS: function ( x ) {
        // Validate a GeoJSON Coordinate Reference System.
        return (
            x != null &&
            (
                (
                    x.type === 'name' &&
                    x.properties != null &&
                    typeof x.properties.name === 'string' &&
                    x.properties.name.length > 0
                ) ||
                (
                    x.type === 'link' &&
                    GeoJSON.isLink(x.properties)
                )
            )
        );
    },

    validCRS: function ( x ) {
        // Validate the CRS property of a GeoJSON object.
        return (
            x != null &&
            (
                typeof x.crs === 'undefined' ||
                x.crs === null ||
                GeoJSON.isCRS(x.crs)
            )
        )
    },

    isBbox: function ( x ) {
        throw 'Not Implemented'
    },

    validBbox: function ( x ) {
        // Validate the bbox property of a GeoJSON object.
        return (
            x != null &&
            (
                typeof x.bbox === 'undefined' ||
                (
                    Array.isArray(x.bbox) &&
                    x.bbox.length%2 === 0 &&
                    GeoJSON.isBbox()
                )
            )
        );
    },

    isLink: function ( x ) {
        // Validate a GeoJSON Link.
        try {
            return (
                x != null &&
                x.properties != null &&
                typeof x.properties.href === 'string' &&
                URI.parse(x.properties.href) &&
                (
                    typeof x.properties.type === 'undefined' ||
                    (
                        typeof x.properties.type === 'string' &&
                        x.properties.type.length > 0
                    )
                )
            );
        }
        catch (e) {
            return false;
        }
    },

//////////////////////////////////////////////////////////////////////// UTILITY

    equalPositions: function ( a , b ) {
        // Compare two GeoJSON Positions for equality.
        return (
            GeoJSON.isPosition(a) && GeoJSON.isPosition(b) &&
            a.length == b.length &&
            a.every(
                function ( element , index ) {
                    return (a[index] == b[index]);
                }
            )
        );
    },

    containedPolygon: function ( inner , outer ) {
        // Determine whether one GeoJSON LinearRing contains another.
        return inner.every(
            function ( Position ) {
                return inside(Position,outer);
            }
        );
    },

    Point: function ( latitude , longitude , altitude ) {
        // Create a valid GeoJSON Point geometry.
        var Point = null;
        if (typeof longitude === 'number' && typeof latitude === 'number') {
            Point = {
                type: 'Point',
                coordinates: [longitude,latitude]
            };
            if (typeof altitude === 'number') Point.coordinates.push(altitude);
        }
        return Point;
    },

    Feature: function ( Geometry , properties ) {
        // Create a valid GeoJSON Feature.
        var Feature = {
            type: 'Feature',
            geometry: null,
            properties: (typeof properties === 'object') ? properties : {}
        };
        if (GeoJSON.isGeometry(Geometry)) Feature.geometry = Geometry;
        return Feature;
    },

    FeatureCollection: function ( Features ) {
        // Create a valid GeoJSON FeatureCollection.
        var FeatureCollection = {
            type: 'FeatureCollection',
            features: []
        };
        if (GeoJSON.isFeature(Features)) {
            FeatureCollection.features.push(Features);
        }
        else if (Array.isArray(Features)) Features.forEach(
            function ( Feature ) {
                if (GeoJSON.isFeature(Feature)) {
                    FeatureCollection.features.push(Feature);
                }
            }
        );
        return FeatureCollection;
    },

    GeometryCollection: function ( Geometries ) {
        // Create a valid GeoJSON GeometryCollection.
        var GeometryCollection = {
            type: 'GeometryCollection',
            geometries: []
        };
        if (GeoJSON.isGeometry(Geometries)) {
            GeometryCollection.geometries.push(Geometries);
        }
        else if (Array.isArray(Geometries)) Geometries.forEach(
            function ( Geometry ) {
                if (GeoJSON.isGeometry(Geometry)) {
                    GeometryCollection.geometries.push(Geometry);
                }
            }
        );
        return GeometryCollection;
    },

    positionsOf: function ( geojson ) {
        // Find all the Positions in a valid GeoJSON object.
        var positions = [];
        var extractLineStringPositions = function ( LineStringCoordinates ) {
            LineStringCoordinates.forEach(
                function ( Position ) {
                    positions.push(Position);
                }
            );
        };
        var extractPolygonPositions = function ( PolygonCoordinates ) {
            PolygonCoordinates.forEach(extractLineStringPositions);
        };
        switch (geojson.type) {
            case 'Point':
                positions.push(geojson.coordinates);
                break;
            case 'MultiPoint':
            case 'LineString':
                extractLineStringPositions(geojson.coordinates);
                break;
            case 'MultiLineString':
            case 'Polygon':
                extractPolygonPositions(geojson.coordinates);
                break;
            case 'MultiPolygon':
                geojson.coordinates.forEach(extractPolygonPositions);
                break;
            case 'GeometryCollection':
                extractLineStringPositions(
                    GeoJSON.positionsOf(geojson.geometries)
                );
                break;
            case 'Feature':
                extractLineStringPositions(
                    GeoJSON.positionsOf(geojson.geometry)
                );
                break;
            case 'FeatureCollection':
                geojson.features.forEach(
                    function ( Feature ) {
                        extractLineStringPositions(
                            GeoJSON.positionsOf(Feature.geometry)
                        );
                    }
                );
                break;
        }
        return positions;
    },

    featuresOf: function ( geojson ) {
        // Find all Features in a valid GeoJSON object.
        var features = [];
        switch (geojson.type) {
            case 'Feature':
                features.push(geojson);
                break;
            case 'FeatureCollection':
                geojson.features.forEach(
                    function ( Feature ) {
                        GeoJSON.featuresOf(Feature).forEach(
                            function ( Feature ) {
                                features.push(Feature);
                            }
                        );
                    }
                );
                break;
        }
        return features;
    },

    geometriesOf: function ( geojson ) {
        // Find all Geometries in a valid GeoJSON object.
        var geometries = [];
        var extractFeatureGeometries = function ( Feature ) {
            GeoJSON.geometriesOf(Feature.geometry).forEach(
                function ( Geometry ) {
                    geometries.push(Geometry);
                }
            );
        };
        switch (geojson.type) {
            case 'Point':
            case 'MultiPoint':
            case 'LineString':
            case 'MultiLineString':
            case 'Polygon':
            case 'MultiPolygon':
                geometries.push(geojson);
                break;
            case 'GeometryCollection':
                geojson.geometries.forEach(
                    function ( Geometry ) {
                        GeoJSON.geometriesOf(Geometry).forEach(
                            function ( Geometry ) {
                                geometries.push(Geometry);
                            }
                        );
                    }
                );
                break;
            case 'Feature':
                extractFeatureGeometries(geojson);
                break;
            case 'FeatureCollection':
                geojson.features.forEach(extractFeatureGeometries);
                break;
        }
        return geometries;
    },

///////////////////////////////////////////////////////////////////// DEPRECATED

    isPointCoordinates: function ( x ) {
        // DEPRECATED: Use isPointPosition.
        return GeoJSON.isPointPosition(x);
    },

    isMultiPointCoordinates: function ( x ) {
        // DEPRECATED: Use isMultiPointPosition.
        return GeoJSON.isMultiPointPosition(x);
    },

    isLineStringCoordinates: function ( x ) {
        // DEPRECATED: Use isLineStringPosition.
        return GeoJSON.isLineStringPosition(x);
    },

    isLinearRingCoordinates: function ( x ) {
        // DEPRECATED: Use isLinearRingPosition.
        return GeoJSON.isLinearRingPosition(x);
    }

};
<commit_msg>Deprecate isMultiLineStringCoordinates. #5<commit_after>var inside = require('point-in-polygon');
var URI = require('uri-js');

var GeoJSON = module.exports = {

///////////////////////////////////////////////////////////////////// VALIDATION

    isGeoJSON: function ( x ) {
        // Validate a GeoJSON object.
        return (
            GeoJSON.isGeometry(x) ||
            GeoJSON.isFeature(x)  ||
            GeoJSON.isFeatureCollection(x)
        );
    },

    isGeometry: function ( x ) {
        // Validate a GeoJSON Geometry.
        return (
            GeoJSON.isPoint(x) ||
            GeoJSON.isMultiPoint(x) ||
            GeoJSON.isLineString(x) ||
            GeoJSON.isMultiLineString(x) ||
            GeoJSON.isPolygon(x) ||
            GeoJSON.isMultiPolygon(x) ||
            GeoJSON.isGeometryCollection(x)
        );
    },

    isPosition: function ( x ) {
        // Validate a GeoJSON Position.
        return (
            Array.isArray(x) &&
            x.length > 1 &&
            x.every(
                function ( element ) {
                    return (typeof element === 'number');
                }
            )
        );
    },

    isPointPosition: function ( x ) {
        // Validate the coordinates of a GeoJSON Point.
        return GeoJSON.isPosition(x);
    },

    isMultiPointPosition: function ( x ) {
        // Validate the coordinates of a GeoJSON MultiPoint.
        return (
            Array.isArray(x) &&
            x.every(GeoJSON.isPosition)
        );
    },

    isLineStringPosition: function ( x ) {
        // Validate the coordinates of a GeoJSON LineString.
        return (
            Array.isArray(x) &&
            x.length > 1 &&
            GeoJSON.isMultiPointPosition(x)
        );
    },

    isLinearRingPosition: function ( x ) {
        // Validate the coordinates of a GeoJSON LinearRing.
        return (
            Array.isArray(x) &&
            x.length > 3 &&
            GeoJSON.isLineStringPosition(x) &&
            GeoJSON.equalPositions(x[0],x[x.length-1])
        );
    },

    isMultiLineStringPosition: function ( x ) {
        // Validate the coordinates of a GeoJSON MultiLineString.
        return (
            Array.isArray(x) &&
            x.every(GeoJSON.isLineStringPosition)
        );
    },

    isPolygonCoordinates: function ( x ) {
        // Validate the coordinates of a GeoJSON Polygon.
        return (
            Array.isArray(x) &&
            x.every(GeoJSON.isLinearRingPosition) &&
            x.every(
                function ( LinearRingCoordinates , i , PolygonCoordinates ) {
                    if (i == 0) return true;
                    else return GeoJSON.containedPolygon(
                        LinearRingCoordinates,
                        PolygonCoordinates[0]
                    );
                }
            )
        );
    },

    isMultiPolygonCoordinates: function ( x ) {
        // Validate the coordinates of a GeoJSON MultiPolygon.
        return (
            Array.isArray(x) &&
            x.every(GeoJSON.isPolygonCoordinates)
        );
    },

    isPoint: function ( x ) {
        // Validate a GeoJSON Point.
        return (
            x != null &&
            x.type === 'Point' &&
            GeoJSON.isPointPosition(x.coordinates) &&
            GeoJSON.validCRS(x) &&
            GeoJSON.validBbox(x)
        );
    },

    isMultiPoint: function ( x ) {
        // Validate a GeoJSON MultiPoint.
        return (
            x != null &&
            x.type === 'MultiPoint' &&
            GeoJSON.isMultiPointPosition(x.coordinates) &&
            GeoJSON.validCRS(x) &&
            GeoJSON.validBbox(x)
        );
    },

    isLineString: function ( x ) {
        // Validate a GeoJSON LineString.
        return (
            x != null &&
            x.type === 'LineString' &&
            GeoJSON.isLineStringPosition(x.coordinates) &&
            GeoJSON.validCRS(x) &&
            GeoJSON.validBbox(x)
        );
    },

    isMultiLineString: function ( x ) {
        // Validate a GeoJSON MultiLineString.
        return (
            x != null &&
            x.type === 'MultiLineString' &&
            GeoJSON.isMultiLineStringPosition(x.coordinates) &&
            GeoJSON.validCRS(x) &&
            GeoJSON.validBbox(x)
        );
    },

    isPolygon: function ( x ) {
        // Validate a GeoJSON Polygon.
        return (
            x != null &&
            x.type === 'Polygon' &&
            GeoJSON.isPolygonCoordinates(x.coordinates) &&
            GeoJSON.validCRS(x) &&
            GeoJSON.validBbox(x)
        );
    },

    isMultiPolygon: function ( x ) {
        // Validate a GeoJSON MultiPolygon.
        return (
            x != null &&
            x.type === 'MultiPolygon' &&
            GeoJSON.isMultiPolygonCoordinates(x.coordinates) &&
            GeoJSON.validCRS(x) &&
            GeoJSON.validBbox(x)
        );
    },

    isGeometryCollection: function ( x ) {
        // Validate a GeoJSON GeometryCollection.
        return (
            x != null &&
            x.type === 'GeometryCollection' &&
            Array.isArray(x.geometries) &&
            x.geometries.every(GeoJSON.isGeometry) &&
            GeoJSON.validCRS(x) &&
            GeoJSON.validBbox(x)
        );
    },

    isFeature: function ( x ) {
        // Validate a GeoJSON Feature.
        return (
            x != null &&
            x.type === 'Feature' &&
            typeof x.properties === 'object' &&
            GeoJSON.validCRS(x) &&
            GeoJSON.validBbox(x) &&
            (
                x.geometry === null ||
                GeoJSON.isGeometry(x.geometry)
            )
        );
    },

    isFeatureCollection: function ( x ) {
        // Validate a GeoJSON FeatureCollection.
        return (
            x != null &&
            x.type === 'FeatureCollection' &&
            Array.isArray(x.features) &&
            x.features.every(GeoJSON.isFeature) &&
            GeoJSON.validCRS(x) &&
            GeoJSON.validBbox(x)
        );
    },

    isCRS: function ( x ) {
        // Validate a GeoJSON Coordinate Reference System.
        return (
            x != null &&
            (
                (
                    x.type === 'name' &&
                    x.properties != null &&
                    typeof x.properties.name === 'string' &&
                    x.properties.name.length > 0
                ) ||
                (
                    x.type === 'link' &&
                    GeoJSON.isLink(x.properties)
                )
            )
        );
    },

    validCRS: function ( x ) {
        // Validate the CRS property of a GeoJSON object.
        return (
            x != null &&
            (
                typeof x.crs === 'undefined' ||
                x.crs === null ||
                GeoJSON.isCRS(x.crs)
            )
        )
    },

    isBbox: function ( x ) {
        throw 'Not Implemented'
    },

    validBbox: function ( x ) {
        // Validate the bbox property of a GeoJSON object.
        return (
            x != null &&
            (
                typeof x.bbox === 'undefined' ||
                (
                    Array.isArray(x.bbox) &&
                    x.bbox.length%2 === 0 &&
                    GeoJSON.isBbox()
                )
            )
        );
    },

    isLink: function ( x ) {
        // Validate a GeoJSON Link.
        try {
            return (
                x != null &&
                x.properties != null &&
                typeof x.properties.href === 'string' &&
                URI.parse(x.properties.href) &&
                (
                    typeof x.properties.type === 'undefined' ||
                    (
                        typeof x.properties.type === 'string' &&
                        x.properties.type.length > 0
                    )
                )
            );
        }
        catch (e) {
            return false;
        }
    },

//////////////////////////////////////////////////////////////////////// UTILITY

    equalPositions: function ( a , b ) {
        // Compare two GeoJSON Positions for equality.
        return (
            GeoJSON.isPosition(a) && GeoJSON.isPosition(b) &&
            a.length == b.length &&
            a.every(
                function ( element , index ) {
                    return (a[index] == b[index]);
                }
            )
        );
    },

    containedPolygon: function ( inner , outer ) {
        // Determine whether one GeoJSON LinearRing contains another.
        return inner.every(
            function ( Position ) {
                return inside(Position,outer);
            }
        );
    },

    Point: function ( latitude , longitude , altitude ) {
        // Create a valid GeoJSON Point geometry.
        var Point = null;
        if (typeof longitude === 'number' && typeof latitude === 'number') {
            Point = {
                type: 'Point',
                coordinates: [longitude,latitude]
            };
            if (typeof altitude === 'number') Point.coordinates.push(altitude);
        }
        return Point;
    },

    Feature: function ( Geometry , properties ) {
        // Create a valid GeoJSON Feature.
        var Feature = {
            type: 'Feature',
            geometry: null,
            properties: (typeof properties === 'object') ? properties : {}
        };
        if (GeoJSON.isGeometry(Geometry)) Feature.geometry = Geometry;
        return Feature;
    },

    FeatureCollection: function ( Features ) {
        // Create a valid GeoJSON FeatureCollection.
        var FeatureCollection = {
            type: 'FeatureCollection',
            features: []
        };
        if (GeoJSON.isFeature(Features)) {
            FeatureCollection.features.push(Features);
        }
        else if (Array.isArray(Features)) Features.forEach(
            function ( Feature ) {
                if (GeoJSON.isFeature(Feature)) {
                    FeatureCollection.features.push(Feature);
                }
            }
        );
        return FeatureCollection;
    },

    GeometryCollection: function ( Geometries ) {
        // Create a valid GeoJSON GeometryCollection.
        var GeometryCollection = {
            type: 'GeometryCollection',
            geometries: []
        };
        if (GeoJSON.isGeometry(Geometries)) {
            GeometryCollection.geometries.push(Geometries);
        }
        else if (Array.isArray(Geometries)) Geometries.forEach(
            function ( Geometry ) {
                if (GeoJSON.isGeometry(Geometry)) {
                    GeometryCollection.geometries.push(Geometry);
                }
            }
        );
        return GeometryCollection;
    },

    positionsOf: function ( geojson ) {
        // Find all the Positions in a valid GeoJSON object.
        var positions = [];
        var extractLineStringPositions = function ( LineStringCoordinates ) {
            LineStringCoordinates.forEach(
                function ( Position ) {
                    positions.push(Position);
                }
            );
        };
        var extractPolygonPositions = function ( PolygonCoordinates ) {
            PolygonCoordinates.forEach(extractLineStringPositions);
        };
        switch (geojson.type) {
            case 'Point':
                positions.push(geojson.coordinates);
                break;
            case 'MultiPoint':
            case 'LineString':
                extractLineStringPositions(geojson.coordinates);
                break;
            case 'MultiLineString':
            case 'Polygon':
                extractPolygonPositions(geojson.coordinates);
                break;
            case 'MultiPolygon':
                geojson.coordinates.forEach(extractPolygonPositions);
                break;
            case 'GeometryCollection':
                extractLineStringPositions(
                    GeoJSON.positionsOf(geojson.geometries)
                );
                break;
            case 'Feature':
                extractLineStringPositions(
                    GeoJSON.positionsOf(geojson.geometry)
                );
                break;
            case 'FeatureCollection':
                geojson.features.forEach(
                    function ( Feature ) {
                        extractLineStringPositions(
                            GeoJSON.positionsOf(Feature.geometry)
                        );
                    }
                );
                break;
        }
        return positions;
    },

    featuresOf: function ( geojson ) {
        // Find all Features in a valid GeoJSON object.
        var features = [];
        switch (geojson.type) {
            case 'Feature':
                features.push(geojson);
                break;
            case 'FeatureCollection':
                geojson.features.forEach(
                    function ( Feature ) {
                        GeoJSON.featuresOf(Feature).forEach(
                            function ( Feature ) {
                                features.push(Feature);
                            }
                        );
                    }
                );
                break;
        }
        return features;
    },

    geometriesOf: function ( geojson ) {
        // Find all Geometries in a valid GeoJSON object.
        var geometries = [];
        var extractFeatureGeometries = function ( Feature ) {
            GeoJSON.geometriesOf(Feature.geometry).forEach(
                function ( Geometry ) {
                    geometries.push(Geometry);
                }
            );
        };
        switch (geojson.type) {
            case 'Point':
            case 'MultiPoint':
            case 'LineString':
            case 'MultiLineString':
            case 'Polygon':
            case 'MultiPolygon':
                geometries.push(geojson);
                break;
            case 'GeometryCollection':
                geojson.geometries.forEach(
                    function ( Geometry ) {
                        GeoJSON.geometriesOf(Geometry).forEach(
                            function ( Geometry ) {
                                geometries.push(Geometry);
                            }
                        );
                    }
                );
                break;
            case 'Feature':
                extractFeatureGeometries(geojson);
                break;
            case 'FeatureCollection':
                geojson.features.forEach(extractFeatureGeometries);
                break;
        }
        return geometries;
    },

///////////////////////////////////////////////////////////////////// DEPRECATED

    isPointCoordinates: function ( x ) {
        // DEPRECATED: Use isPointPosition.
        return GeoJSON.isPointPosition(x);
    },

    isMultiPointCoordinates: function ( x ) {
        // DEPRECATED: Use isMultiPointPosition.
        return GeoJSON.isMultiPointPosition(x);
    },

    isLineStringCoordinates: function ( x ) {
        // DEPRECATED: Use isLineStringPosition.
        return GeoJSON.isLineStringPosition(x);
    },

    isLinearRingCoordinates: function ( x ) {
        // DEPRECATED: Use isLinearRingPosition.
        return GeoJSON.isLinearRingPosition(x);
    },

    isMultiLineStringCoordinates: function ( x ) {
        // DEPRECATED: Use isMultiLineStringPosition.
        return GeoJSON.isMultiLineStringPosition(x);
    }

};
<|endoftext|>


<commit_before>// ReSharper disable UnusedParameter
// ReSharper disable InconsistentNaming
(function (testFns) {
    "use strict";

    /*********************************************************
    * Breeze configuration and module setup
    *********************************************************/
    // Classes we'll need from the breeze namespaces
    var EntityQuery = breeze.EntityQuery;
    var newGuidComb = testFns.newGuidComb;
    var handleFail = testFns.handleFail;
    var handleSaveFailed = testFns.handleSaveFailed;

    /*********************************************************
    * Northwind Saves
    *********************************************************/
    // Target the Northwind service by default
    var northwindService = testFns.northwindServiceName;
    var newNorthwindEm = testFns.newEmFactory(northwindService);

    var alfredsID = testFns.wellKnownData.alfredsID;

    module("saveNorthwindTests", {
        setup: function () {
            testFns.populateMetadataStore(newNorthwindEm);
        },
        teardown: function () {
            testFns.teardown_northwindReset(); // restore original db state after each test
        }
    });

    asyncTest("can save a new Customer entity", function () {
        expect(1);
        // Create and initialize entity to save
        var em = newNorthwindEm();
        var customer = em.createEntity('Customer', {
            CustomerID: newGuidComb(),
            CompanyName: "Test1 " + new Date().toISOString()
        });

        entitySaveTester(customer, /*shouldSave*/ true);

    });

    asyncTest("can modify my own Customer entity", function () {
        expect(2);
        var timestamp = new Date().toISOString();
        var em = newNorthwindEm();

        var customer = em.createEntity('Customer', {
            CustomerID: newGuidComb(),
            CompanyName: "Test2A " + timestamp
        });

        em.saveChanges().then(modifyCustomer).fail(handleSaveFailed).fin(start);

        function modifyCustomer(saveResults) {
            var saved = saveResults.entities[0];
            ok(saved && saved === customer,
                "save of added customer should have succeeded");
            customer.CompanyName("Test2M " + timestamp);
            return em.saveChanges()
            .then(confirmCustomerSaved);
        }

        function confirmCustomerSaved(saveResults) {
            var saved = saveResults.entities[0];
            ok(saved && saved === customer,
                "save of modified customer, '{0}', should have succeeded"
                .format(saved && saved.CompanyName()));
        }

    });

    asyncTest("can delete my own Customer entity", function () {
        expect(3);
        var timestamp = new Date().toISOString();
        var em = newNorthwindEm();

        var customer = em.createEntity('Customer', {
            CustomerID: newGuidComb(),
            CompanyName: "Test3A " + timestamp
        });

        em.saveChanges().then(deleteCustomer).fail(handleSaveFailed).fin(start);

        function deleteCustomer(saveResults) {
            var saved = saveResults.entities[0];
            ok(saved && saved === customer,
                "save of added customer should have succeeded");
            customer.entityAspect.setDeleted();
            return em.saveChanges()
            .then(confirmCustomerSaved);
        }

        function confirmCustomerSaved(saveResults) {
            var saved = saveResults.entities[0];
            ok(saved && saved === customer,
                "save of deleted customer, '{0}', should have succeeded"
                .format(saved && saved.CompanyName()));

            var state = customer.entityAspect.entityState.name;
            equal(state, breeze.EntityState.Detached.name,
                "customer object should be 'Detached'");
        }

    });

    asyncTest("save new Orders and OrderDetails in one transaction", function () {
        expect(3);
        var em = newNorthwindEm();
        var order = em.createEntity('Order', { ShipName: 'Add OrderGraphTest' });
        var orderID = order.OrderID();

        em.createEntity('OrderDetail', { OrderID: orderID, ProductID: 1, UnitPrice: 42.42 });
        em.createEntity('OrderDetail', { OrderID: orderID, ProductID: 2, UnitPrice: 42.42 });
        em.createEntity('OrderDetail', { OrderID: orderID, ProductID: 3, UnitPrice: 42.42 });

        em.saveChanges()
          .catch(handleSaveFailed)
          .then(requery).catch(handleFail).fin(start);

        function requery() {
            // get the permanent key from the updated Order entity
            orderID = order.OrderID();
            em.clear(); // clear the cache because we want to be sure
            return EntityQuery.from('Orders')
            .where('OrderID', 'eq', orderID)
            .expand('OrderDetails')
            .using(em).execute()
            .then(confirmOrderGraph);
        }

        function confirmOrderGraph(data) {
            var order = data.results[0];
            equal(order.ShipName(), 'Add OrderGraphTest', "re-queried order.ShipName as expected");
            var details = (order && order.OrderDetails()) || [];
            equal(details.length, 3, 'requery of saved new Order graph came with 3 details');
            var gotExpectedDetails = details.every(function (od) {
                return od.UnitPrice() === 42.42;
            });
            ok(gotExpectedDetails, 'every OrderDetail had the expected UnitPrice of 42.42');
        }
    });

    /*
     * This test removed when we made InternationalOrder a subclass of Order
     * Restore it if/when decide to demo IO as a separate entity related in 1..(0,1)
     *
    asyncTest("can save a new Northwind Order & InternationalOrder [1..(0,1) relationship]", function () {
        expect(2);
        // Create and initialize entity to save
        var em = newNorthwindEm();

        var order = em.createEntity('Order', {
            CustomerID: testFns.wellKnownData.alfredsID,
            EmployeeID: testFns.wellKnownData.nancyID,
            ShipName: "Test " + new Date().toISOString()
        });

        var internationalOrder = em.createEntity('InternationalOrder', {
            Order: order, // sets OrderID and pulls it into the order's manager
            CustomsDescription: "rare, exotic birds"
        });

        em.saveChanges()
            .then(successfulSave).fail(handleSaveFailed).fin(start);

        function successfulSave(saveResults) {
            var orderId = order.OrderID();
            var internationalOrderID = internationalOrder.OrderID();

            equal(internationalOrderID, orderId,
                "the new internationalOrder should have the same OrderID as its new parent Order, " + orderId);
            ok(orderId > 0, "the OrderID is positive, indicating it is a permanent order");
        }

    });
     */

    asyncTest("delete of Product clears its related Category BEFORE save", function () {
        expect(4);
        var em = newNorthwindEm();

        EntityQuery.from('Products').top(1)
            .expand('Category')
            .using(em).execute()
            .then(prepDelete).catch(handleFail).fin(start);

        // Don't dare actually save because
        // this demo lacks ability to restore the db after deletes.
        function prepDelete(data) {
            var product = data.results[0];
            ok(product != null, "should have a product");
            if (product) {
                // precondition
                ok(product.Category() !== null, "product should have a Category before delete");

                // ACT
                product.entityAspect.setDeleted();

                ok(product.Category() === null, "product should NOT have a Category after product deleted");
                // FKs of principle related entities are retained. Should they be cleared too?
                ok(product.CategoryID() !== 0, "product should have a non-zero CategoryID after product deleted");
            }
        }

    });

    asyncTest("delete of Order clears its related entities BEFORE save", function () {
        expect(10);
        var em = newNorthwindEm();

        EntityQuery.from('Orders').top(1)
            .expand('Customer, Employee, OrderDetails')
            .using(em).execute()
            .then(prepDelete).fail(handleFail).fin(start);

        // Don't dare actually save because
        // this demo lacks ability to restore the db after deletes.
        function prepDelete(data) {
            var order = data.results[0];
            ok(order != null, "should have an order");
            if (order) {
                // precondition
                ok(order.Customer() !== null, "order should have a Customer before delete");
                ok(order.Employee() !== null, "order should have a Employee before delete");
                var details = order.OrderDetails();
                ok(details.length !== 0, "order should have OrderDetails before delete");

                // ACT
                order.entityAspect.setDeleted();

                // ASSERT
                ok(order.Customer() === null, "order should NOT have a Customer after order deleted");
                ok(order.Employee() === null, "order should NOT have a Employee after order deleted");
                ok(order.OrderDetails().length === 0,
                    "order should NOT have OrderDetails after order deleted");

                // FKs of principle related entities are retained. Should they be cleared too?
                ok(order.CustomerID() !== 0, "order should have a non-zero CustomerID after order deleted");
                ok(order.EmployeeID() !== 0, "order should have a non-zero EmployeeID after order deleted");

                // Breeze should have set OrderID of all former details to zero
                var allOrderIdAreZero = true;
                details.forEach(function (d) {
                    allOrderIdAreZero = allOrderIdAreZero && d.OrderID() === 0;
                });
                ok(allOrderIdAreZero, "OrderID of every original detail should be zero");
            }
        }
    });

    /************************** TEST HELPERS *************************/
    function entitySaveTester(masterEntity, shouldSave) {
        var typeName = masterEntity.entityType.shortName;
        var operation = masterEntity.entityAspect.entityState.name;
        var msgPart = " save the " + operation + " " + typeName;

        var manager = masterEntity.entityAspect.entityManager;
        manager.saveChanges()
        .then(function (saveResults) {
            var prefix = shouldSave ? "should" : "should not";
            ok(shouldSave, prefix + " have been able to" + msgPart +
                " with key: " + JSON.stringify(masterEntity.entityAspect.getKey().values));
        })
        .fail(function (error) {
            var prefix = shouldSave ? "should not" : "should";
            ok(!shouldSave, "server " + prefix + " have rejected " + msgPart +
                " with the error: " + error.message);
        }).fin(start);
    }
})(docCode.testFns);<commit_msg>DocCode: cleaned up the "can save new Order and its OrderDetails in one transaction" test in saveNorthwindTests<commit_after>// ReSharper disable UnusedParameter
// ReSharper disable InconsistentNaming
(function (testFns) {
    "use strict";

    /*********************************************************
    * Breeze configuration and module setup
    *********************************************************/
    // Classes we'll need from the breeze namespaces
    var EntityQuery = breeze.EntityQuery;
    var newGuidComb = testFns.newGuidComb;
    var handleFail = testFns.handleFail;
    var handleSaveFailed = testFns.handleSaveFailed;

    /*********************************************************
    * Northwind Saves
    *********************************************************/
    // Target the Northwind service by default
    var northwindService = testFns.northwindServiceName;
    var newNorthwindEm = testFns.newEmFactory(northwindService);

    var alfredsID = testFns.wellKnownData.alfredsID;

    module("saveNorthwindTests", {
        setup: function () {
            testFns.populateMetadataStore(newNorthwindEm);
        },
        teardown: function () {
            testFns.teardown_northwindReset(); // restore original db state after each test
        }
    });

    asyncTest("can save a new Customer entity", function () {
        expect(1);
        // Create and initialize entity to save
        var em = newNorthwindEm();
        var customer = em.createEntity('Customer', {
            CustomerID: newGuidComb(),
            CompanyName: "Test1 " + new Date().toISOString()
        });

        entitySaveTester(customer, /*shouldSave*/ true);

    });

    asyncTest("can modify my own Customer entity", function () {
        expect(2);
        var timestamp = new Date().toISOString();
        var em = newNorthwindEm();

        var customer = em.createEntity('Customer', {
            CustomerID: newGuidComb(),
            CompanyName: "Test2A " + timestamp
        });

        em.saveChanges().then(modifyCustomer).fail(handleSaveFailed).fin(start);

        function modifyCustomer(saveResults) {
            var saved = saveResults.entities[0];
            ok(saved && saved === customer,
                "save of added customer should have succeeded");
            customer.CompanyName("Test2M " + timestamp);
            return em.saveChanges()
            .then(confirmCustomerSaved);
        }

        function confirmCustomerSaved(saveResults) {
            var saved = saveResults.entities[0];
            ok(saved && saved === customer,
                "save of modified customer, '{0}', should have succeeded"
                .format(saved && saved.CompanyName()));
        }

    });

    asyncTest("can delete my own Customer entity", function () {
        expect(3);
        var timestamp = new Date().toISOString();
        var em = newNorthwindEm();

        var customer = em.createEntity('Customer', {
            CustomerID: newGuidComb(),
            CompanyName: "Test3A " + timestamp
        });

        em.saveChanges().then(deleteCustomer).fail(handleSaveFailed).fin(start);

        function deleteCustomer(saveResults) {
            var saved = saveResults.entities[0];
            ok(saved && saved === customer,
                "save of added customer should have succeeded");
            customer.entityAspect.setDeleted();
            return em.saveChanges()
            .then(confirmCustomerSaved);
        }

        function confirmCustomerSaved(saveResults) {
            var saved = saveResults.entities[0];
            ok(saved && saved === customer,
                "save of deleted customer, '{0}', should have succeeded"
                .format(saved && saved.CompanyName()));

            var state = customer.entityAspect.entityState.name;
            equal(state, breeze.EntityState.Detached.name,
                "customer object should be 'Detached'");
        }

    });

    asyncTest("can save new Order and its OrderDetails in one transaction", function () {
        expect(3);
        var em = newNorthwindEm();
        var order = em.createEntity('Order', { ShipName: 'Add OrderGraphTest' });

        em.createEntity('OrderDetail', { Order: order, ProductID: 1, UnitPrice: 42.42 });
        em.createEntity('OrderDetail', { Order: order, ProductID: 2, UnitPrice: 42.42 });
        em.createEntity('OrderDetail', { Order: order, ProductID: 3, UnitPrice: 42.42 });

        em.saveChanges()
          .catch(handleSaveFailed)
          .then(requery).catch(handleFail).fin(start);

        function requery() {
            // get the permanent key from the updated Order entity
            var orderID = order.OrderID();

            em.clear(); // clear the cache because we want to be sure

            return EntityQuery.from('Orders')
            .where('OrderID', 'eq', orderID)
            .expand('OrderDetails')
            .using(em).execute()
            .then(confirmOrderGraph);
        }

        function confirmOrderGraph(data) {
            var order = data.results[0];
            equal(order.ShipName(), 'Add OrderGraphTest', "'ShipName' of the re-queried order is expected value");

            var details = (order && order.OrderDetails()) || [];
            equal(details.length, 3, 'requery of saved new Order graph came with the expected 3 details');

            var gotExpectedDetails = details.every(function (od) {
                return od.UnitPrice() === 42.42;
            });
            ok(gotExpectedDetails, 'every OrderDetail has the expected UnitPrice of 42.42');
        }
    });

    /*
     * This test removed when we made InternationalOrder a subclass of Order
     * Restore it if/when decide to demo IO as a separate entity related in 1..(0,1)
     *
    asyncTest("can save a new Northwind Order & InternationalOrder [1..(0,1) relationship]", function () {
        expect(2);
        // Create and initialize entity to save
        var em = newNorthwindEm();

        var order = em.createEntity('Order', {
            CustomerID: testFns.wellKnownData.alfredsID,
            EmployeeID: testFns.wellKnownData.nancyID,
            ShipName: "Test " + new Date().toISOString()
        });

        var internationalOrder = em.createEntity('InternationalOrder', {
            Order: order, // sets OrderID and pulls it into the order's manager
            CustomsDescription: "rare, exotic birds"
        });

        em.saveChanges()
            .then(successfulSave).fail(handleSaveFailed).fin(start);

        function successfulSave(saveResults) {
            var orderId = order.OrderID();
            var internationalOrderID = internationalOrder.OrderID();

            equal(internationalOrderID, orderId,
                "the new internationalOrder should have the same OrderID as its new parent Order, " + orderId);
            ok(orderId > 0, "the OrderID is positive, indicating it is a permanent order");
        }

    });
     */

    asyncTest("delete of Product clears its related Category BEFORE save", function () {
        expect(4);
        var em = newNorthwindEm();

        EntityQuery.from('Products').top(1)
            .expand('Category')
            .using(em).execute()
            .then(prepDelete).catch(handleFail).fin(start);

        // Don't dare actually save because
        // this demo lacks ability to restore the db after deletes.
        function prepDelete(data) {
            var product = data.results[0];
            ok(product != null, "should have a product");
            if (product) {
                // precondition
                ok(product.Category() !== null, "product should have a Category before delete");

                // ACT
                product.entityAspect.setDeleted();

                ok(product.Category() === null, "product should NOT have a Category after product deleted");
                // FKs of principle related entities are retained. Should they be cleared too?
                ok(product.CategoryID() !== 0, "product should have a non-zero CategoryID after product deleted");
            }
        }

    });

    asyncTest("delete of Order clears its related entities BEFORE save", function () {
        expect(10);
        var em = newNorthwindEm();

        EntityQuery.from('Orders').top(1)
            .expand('Customer, Employee, OrderDetails')
            .using(em).execute()
            .then(prepDelete).fail(handleFail).fin(start);

        // Don't dare actually save because
        // this demo lacks ability to restore the db after deletes.
        function prepDelete(data) {
            var order = data.results[0];
            ok(order != null, "should have an order");
            if (order) {
                // precondition
                ok(order.Customer() !== null, "order should have a Customer before delete");
                ok(order.Employee() !== null, "order should have a Employee before delete");
                var details = order.OrderDetails();
                ok(details.length !== 0, "order should have OrderDetails before delete");

                // ACT
                order.entityAspect.setDeleted();

                // ASSERT
                ok(order.Customer() === null, "order should NOT have a Customer after order deleted");
                ok(order.Employee() === null, "order should NOT have a Employee after order deleted");
                ok(order.OrderDetails().length === 0,
                    "order should NOT have OrderDetails after order deleted");

                // FKs of principle related entities are retained. Should they be cleared too?
                ok(order.CustomerID() !== 0, "order should have a non-zero CustomerID after order deleted");
                ok(order.EmployeeID() !== 0, "order should have a non-zero EmployeeID after order deleted");

                // Breeze should have set OrderID of all former details to zero
                var allOrderIdAreZero = true;
                details.forEach(function (d) {
                    allOrderIdAreZero = allOrderIdAreZero && d.OrderID() === 0;
                });
                ok(allOrderIdAreZero, "OrderID of every original detail should be zero");
            }
        }
    });

    /************************** TEST HELPERS *************************/
    function entitySaveTester(masterEntity, shouldSave) {
        var typeName = masterEntity.entityType.shortName;
        var operation = masterEntity.entityAspect.entityState.name;
        var msgPart = " save the " + operation + " " + typeName;

        var manager = masterEntity.entityAspect.entityManager;
        manager.saveChanges()
        .then(function (saveResults) {
            var prefix = shouldSave ? "should" : "should not";
            ok(shouldSave, prefix + " have been able to" + msgPart +
                " with key: " + JSON.stringify(masterEntity.entityAspect.getKey().values));
        })
        .fail(function (error) {
            var prefix = shouldSave ? "should not" : "should";
            ok(!shouldSave, "server " + prefix + " have rejected " + msgPart +
                " with the error: " + error.message);
        }).fin(start);
    }
})(docCode.testFns);<|endoftext|>


<commit_before>/*
This module provides direct integration with the cantools memcache service via
the _memcache.py request handler. Some key functions are defined below.

### CT.memcache.get(key, cb, localCache)
Return the (server-side) value for the specified key.

### CT.memcache.set(key, val, cb, localCache)
Instruct the server to remember the specified key and value.

### CT.memcache.forget(key, cb, localCache)
Instruct the server to forget the specified key and any associated value.

### CT.memcache.countdown.get(key, cb)
Return the (server-side) TTL and token for the specified key.

### CT.memcache.countdown.set(key, seconds, cb)
Instruct the server to associate a countdown (seconds in the future) and token
(randomly-generated) with the specified key.
*/

CT.memcache = {
	_local: {},
	_rq: function(action, cb, key, val, params, netargs) {
		params = CT.merge(params, {
			action: action,
			key: key
		});
		if (val) {
			if (params.mode == "blob")
				return CT.net.formUp(val, CT.merge(netargs, {
					cb: cb,
					params: params,
					path: "/_memcache"
				}), "value");
			params.value = val;
		}
		CT.net.post(CT.merge(netargs, {
			cb: cb,
			params: params,
			path: "/_memcache"
		}));
	},
	get: function(key, cb, localCache) {
		var local = localCache && CT.memcache._local[key];
		if (local)
			cb(local);
		else
			CT.memcache._rq("get", cb, key);
	},
	set: function(key, val, cb, localCache) {
		if (localCache)
			CT.memcache._local[key] = val;
		CT.memcache._rq("set", cb, key, val);
	},
	forget: function(key, cb, localCache) {
		if (localCache && CT.memcache._local[key])
			delete CT.memcache._local[key];
		CT.memcache._rq("forget", cb, key);
	}
};

CT.memcache.blob = {
	get: function(key, cb, btype) {
		CT.memcache._rq("get", function(data) {
			cb(new Blob([data], { type : btype }));
		}, key, null, { mode: "blob" }, { basic: true });
	},
	set: function(key, blob, cb, transcode) {
		var opts = { mode: "blob" };
		if (transcode) // transcoding doesn't work well for chrome-generated webm chunks
			opts.transcode = true;
		CT.memcache._rq("set", cb, key, blob, opts);
	}
};

CT.memcache.countdown = {
	get: function(key, cb) {
		CT.memcache._rq("get", cb, key, null, { mode: "countdown" });
	},
	set: function(key, seconds, cb, meta) {
		var params = { mode: "countdown" };
		if (meta)
			params.meta = meta;
		CT.memcache._rq("set", cb, key, seconds, params);
	},
	list: function(cb) {
		CT.memcache._rq("get", cb, "_countdown_list", null, { mode: "countdown" });
	}
};<commit_msg>CT.memcache.blob.get(): use "blob" responseType<commit_after>/*
This module provides direct integration with the cantools memcache service via
the _memcache.py request handler. Some key functions are defined below.

### CT.memcache.get(key, cb, localCache)
Return the (server-side) value for the specified key.

### CT.memcache.set(key, val, cb, localCache)
Instruct the server to remember the specified key and value.

### CT.memcache.forget(key, cb, localCache)
Instruct the server to forget the specified key and any associated value.

### CT.memcache.countdown.get(key, cb)
Return the (server-side) TTL and token for the specified key.

### CT.memcache.countdown.set(key, seconds, cb)
Instruct the server to associate a countdown (seconds in the future) and token
(randomly-generated) with the specified key.
*/

CT.memcache = {
	_local: {},
	_rq: function(action, cb, key, val, params, netargs) {
		params = CT.merge(params, {
			action: action,
			key: key
		});
		if (val) {
			if (params.mode == "blob")
				return CT.net.formUp(val, CT.merge(netargs, {
					cb: cb,
					params: params,
					path: "/_memcache"
				}), "value");
			params.value = val;
		}
		CT.net.post(CT.merge(netargs, {
			cb: cb,
			params: params,
			path: "/_memcache"
		}));
	},
	get: function(key, cb, localCache) {
		var local = localCache && CT.memcache._local[key];
		if (local)
			cb(local);
		else
			CT.memcache._rq("get", cb, key);
	},
	set: function(key, val, cb, localCache) {
		if (localCache)
			CT.memcache._local[key] = val;
		CT.memcache._rq("set", cb, key, val);
	},
	forget: function(key, cb, localCache) {
		if (localCache && CT.memcache._local[key])
			delete CT.memcache._local[key];
		CT.memcache._rq("forget", cb, key);
	}
};

CT.memcache.blob = {
	get: function(key, cb, btype) {
		CT.memcache._rq("get", function(data) {
			cb(new Blob([data], { type : btype }));
		}, key, null, { mode: "blob" }, { basic: true, responseType: "blob" });
	},
	set: function(key, blob, cb, transcode) {
		var opts = { mode: "blob" };
		if (transcode) // transcoding doesn't work well for chrome-generated webm chunks
			opts.transcode = true;
		CT.memcache._rq("set", cb, key, blob, opts);
	}
};

CT.memcache.countdown = {
	get: function(key, cb) {
		CT.memcache._rq("get", cb, key, null, { mode: "countdown" });
	},
	set: function(key, seconds, cb, meta) {
		var params = { mode: "countdown" };
		if (meta)
			params.meta = meta;
		CT.memcache._rq("set", cb, key, seconds, params);
	},
	list: function(cb) {
		CT.memcache._rq("get", cb, "_countdown_list", null, { mode: "countdown" });
	}
};<|endoftext|>