<commit_before>/**
 * @WNS - A NodeJS MVC Framework and HTTP Server
 *
 * @author: Pedro Nasser
 * @link: http://wns.yept.net/
 * @license: http://yept.net/projects/wns/#license
 * @copyright: Copyright © 2012 WNS
 */

/**
 * Description coming soon.
 *
 * @author Pedro Nasser
 * @package system
 * @since 1.0.0
 */

var memory = process.memoryUsage().rss;

 console.log('\n  o       o o--o  o--o (TM)');
 console.log('  |   o   | |   | o__');
 console.log('   \\ / \\ /  |   |    o');
 console.log('    o   o   o   o o--o');
 console.log('\n         powered by YEPT(R)');
 console.log();

// Loading requirements..
try
{

	global.sourcePath = 'src/';
	global.cwd = __dirname+'/../';
	global._r = require;
	console.log(' CWD: '+cwd);
	console.log(' SOURCEPATH: '+cwd+sourcePath);
	console.log();

	// WNS object.
	// Will contain the classes to build and load.
	global.wns = {};
	global.wns.info=_r(cwd+'package.json');

	console.log(' Loading and compiling:');

	console.log(' - Required utilities..');
	global._walk = _r(cwd+sourcePath+'util/recursiveReadDir');
	Object.extend = _r(cwd+sourcePath+'util/extend');
	Object.extend(true,Object,_r(cwd+sourcePath+'util/object'));

	process.stdout.write(' - Required node modules..');
	var nm = ['http','fs','path','url','zlib','crypto','stream','util','events','buffer'];
	for (d in wns.info.dependencies)
		nm.push(d);
	for (n in nm)
		global[nm[n]] = _r(nm[n]);
	if (fs.existsSync == undefined)
		fs.existsSync = path.existsSync;
	global.emitter = events.EventEmitter;
	global.Buffer = buffer.Buffer;
	cwd=path.normalize(cwd);
	sourcePath=path.normalize(sourcePath);
	process.stdout.write(' ('+nm.length+' modules)\n');

} catch (e) {
	console.log('Failed to load some dependencies...');
	throw e;
	process.exit();
}

// We need this class for building the rest of the required classes.
wns.wnBuild = _r(cwd+sourcePath+'wnBuild.js');

process.stdout.write(' - Required classes...');

var _coreClasses={}, toBuild = {};
// Recursivelly getting list of all classes in the core/
_walk(cwd+sourcePath+'core', function (err, classes) {

	// Load core class sources to the memory.
	for (c in classes)
	{

		// Loading class source.
		var _class = fs.readFileSync(classes[c]).toString(),
			className = classes[c].split('/').pop().split('.')[0];

		// Compiling the class.
		var module = {};
		eval(_class);

		// Check structure.
		if (wns.wnBuild.prototype.checkStructure(module.exports))
		{
			// Send it to the build list.
			toBuild[className] = module.exports;
		} else
		{
			// Send it to the WNS.
			wns[className] = module.exports;
		}

		// Store class source.
		_coreClasses[className] = _class;
	}

	// We will compile the new classes to the WNS object.
	var compiled = (new wns.wnBuild(toBuild).build()),
		loaded = 0;
	for (c in toBuild)
	{
		if (compiled[c].loaded)
		{
			// Store compiled classes as read-only.
			Object.defineProperty(wns, c, {
				value: compiled[c],
				writable: false,
				configurable: false,
				enumerable: true
			});
			loaded++;
		}
	}

	process.stdout.write(' ('+loaded+' classes)\n');

});

// Sending coreClasses to global, read-only.
Object.defineProperty(global, 'coreClasses', { value: _coreClasses, writable: false, configurable: false });

// Clear require cache.
memory = (new Number((process.memoryUsage().rss - memory) / 1024 / 1024)).toFixed(2);
console.log(' - WNS version: '+wns.info.version);
console.log(' - Core memory usage: '+memory+' mb');

console.log('');

// Create a new console
wns.console = new wns.wnConsole({ modulePath: cwd }, cwd);

<<<<<<< HEAD
=======
//
>>>>>>> ced8203515307155e8a4e0da8f44ba42a0529a62
<commit_msg>fixes<commit_after>/**
 * @WNS - A NodeJS MVC Framework and HTTP Server
 *
 * @author: Pedro Nasser
 * @link: http://wns.yept.net/
 * @license: http://yept.net/projects/wns/#license
 * @copyright: Copyright © 2012 WNS
 */

/**
 * Description coming soon.
 *
 * @author Pedro Nasser
 * @package system
 * @since 1.0.0
 */

var memory = process.memoryUsage().rss;

 console.log('\n  o       o o--o  o--o (TM)');
 console.log('  |   o   | |   | o__');
 console.log('   \\ / \\ /  |   |    o');
 console.log('    o   o   o   o o--o');
 console.log('\n         powered by YEPT(R)');
 console.log();

// Loading requirements..
try
{

	global.sourcePath = 'src/';
	global.cwd = __dirname+'/../';
	global._r = require;
	console.log(' CWD: '+cwd);
	console.log(' SOURCEPATH: '+cwd+sourcePath);
	console.log();

	// WNS object.
	// Will contain the classes to build and load.
	global.wns = {};
	global.wns.info=_r(cwd+'package.json');

	console.log(' Loading and compiling:');

	console.log(' - Required utilities..');
	global._walk = _r(cwd+sourcePath+'util/recursiveReadDir');
	Object.extend = _r(cwd+sourcePath+'util/extend');
	Object.extend(true,Object,_r(cwd+sourcePath+'util/object'));

	process.stdout.write(' - Required node modules..');
	var nm = ['http','fs','path','url','zlib','crypto','stream','util','events','buffer'];
	for (d in wns.info.dependencies)
		nm.push(d);
	for (n in nm)
		global[nm[n]] = _r(nm[n]);
	if (fs.existsSync == undefined)
		fs.existsSync = path.existsSync;
	global.emitter = events.EventEmitter;
	global.Buffer = buffer.Buffer;
	cwd=path.normalize(cwd);
	sourcePath=path.normalize(sourcePath);
	process.stdout.write(' ('+nm.length+' modules)\n');

} catch (e) {
	console.log('Failed to load some dependencies...');
	throw e;
	process.exit();
}

// We need this class for building the rest of the required classes.
wns.wnBuild = _r(cwd+sourcePath+'wnBuild.js');

process.stdout.write(' - Required classes...');

var _coreClasses={}, toBuild = {};
// Recursivelly getting list of all classes in the core/
_walk(cwd+sourcePath+'core', function (err, classes) {

	// Load core class sources to the memory.
	for (c in classes)
	{

		// Loading class source.
		var _class = fs.readFileSync(classes[c]).toString(),
			className = classes[c].split('/').pop().split('.')[0];

		// Compiling the class.
		var module = {};
		eval(_class);

		// Check structure.
		if (wns.wnBuild.prototype.checkStructure(module.exports))
		{
			// Send it to the build list.
			toBuild[className] = module.exports;
		} else
		{
			// Send it to the WNS.
			wns[className] = module.exports;
		}

		// Store class source.
		_coreClasses[className] = _class;
	}

	// We will compile the new classes to the WNS object.
	var compiled = (new wns.wnBuild(toBuild).build()),
		loaded = 0;
	for (c in toBuild)
	{
		if (compiled[c].loaded)
		{
			// Store compiled classes as read-only.
			Object.defineProperty(wns, c, {
				value: compiled[c],
				writable: false,
				configurable: false,
				enumerable: true
			});
			loaded++;
		}
	}

	process.stdout.write(' ('+loaded+' classes)\n');

});

// Sending coreClasses to global, read-only.
Object.defineProperty(global, 'coreClasses', { value: _coreClasses, writable: false, configurable: false });

// Clear require cache.
memory = (new Number((process.memoryUsage().rss - memory) / 1024 / 1024)).toFixed(2);
console.log(' - WNS version: '+wns.info.version);
console.log(' - Core memory usage: '+memory+' mb');

console.log('');

// Create a new console
wns.console = new wns.wnConsole({ modulePath: cwd }, cwd);<|endoftext|>


<commit_before>define([
    'jquery',
    'underscore',
    'XMLWriter',
    'vellum/util'
], function (
    $,
    _,
    XMLWriter,
    util
) {
    var createXForm = function (form) {
        var xmlWriter = new XMLWriter( 'UTF-8', '1.0' );
        form.vellum.beforeSerialize();

        xmlWriter.writeStartDocument();
        //Generate header boilerplate up to instance level
        xmlWriter.writeStartElement('h:html');
        write_html_tag_boilerplate(xmlWriter);
        xmlWriter.writeStartElement('h:head');
        xmlWriter.writeStartElement('h:title');
        xmlWriter.writeString(form.formName);
        xmlWriter.writeEndElement();       //CLOSE TITLE

        xmlWriter.writeStartElement('model');
        xmlWriter.writeStartElement('instance');
        _writeInstanceAttributes(xmlWriter, form.instanceMetadata[0]);

        createDataBlock(form, xmlWriter);
        xmlWriter.writeEndElement(); //CLOSE MAIN INSTANCE

        // other instances
        for (var i = 1; i < form.instanceMetadata.length; i++) {
            _writeInstance(xmlWriter, form.instanceMetadata[i], true);
        }

        createBindList(form, xmlWriter);

        _.each(form.getSetValues(), function (setValue) {
            xmlWriter.writeStartElement('setvalue');
            xmlWriter.writeAttributeString('event', setValue.event);
            xmlWriter.writeAttributeString('ref', setValue.ref);
            xmlWriter.writeAttributeString('value', setValue.value);
            xmlWriter.writeEndElement();
        });

        form.vellum.contributeToModelXML(xmlWriter);

        xmlWriter.writeEndElement(); //CLOSE MODEL

        form.vellum.contributeToHeadXML(xmlWriter, form);

        xmlWriter.writeEndElement(); //CLOSE HEAD

        xmlWriter.writeStartElement('h:body');
        createControlBlock(form, xmlWriter);
        xmlWriter.writeEndElement(); //CLOSE BODY
        xmlWriter.writeEndElement(); //CLOSE HTML
        xmlWriter.writeEndDocument(); //CLOSE DOCUMENT

        var ret = xmlWriter.flush();

        return ret;
    };

    var createModelHeader = function (form, xmlWriter) {
        var uuid, jrm;
        //assume we're currently pointed at the opening date block tag
        //e.g. <model><instance><data> <--- we're at <data> now.

        jrm = form.formJRM;
        if(!jrm) {
            jrm = "http://dev.commcarehq.org/jr/xforms";
        }

        uuid = form.formUuid; //gets set at parse time/by UI
        if(!uuid) {
            uuid = "http://openrosa.org/formdesigner/" + util.generate_xmlns_uuid();
        }

        xmlWriter.writeAttributeString("xmlns:jrm",jrm);
        xmlWriter.writeAttributeString("xmlns", uuid);
        xmlWriter.writeAttributeString("uiVersion", form.formUIVersion || "1");
        xmlWriter.writeAttributeString("version", form.formVersion || "1");
        xmlWriter.writeAttributeString("name", form.formName || "New Form");
    };

    function write_html_tag_boilerplate (xmlWriter) {
        xmlWriter.writeAttributeString( "xmlns:h", "http://www.w3.org/1999/xhtml" );
        xmlWriter.writeAttributeString( "xmlns:orx", "http://openrosa.org/jr/xforms" );
        xmlWriter.writeAttributeString( "xmlns", "http://www.w3.org/2002/xforms" );
        xmlWriter.writeAttributeString( "xmlns:xsd", "http://www.w3.org/2001/XMLSchema" );
        xmlWriter.writeAttributeString( "xmlns:jr", "http://openrosa.org/javarosa" );
        xmlWriter.writeAttributeString( "xmlns:vellum", "http://commcarehq.org/xforms/vellum" );
    }

    var _writeInstanceAttributes = function (writer, instanceMetadata) {
        for (var attrId in instanceMetadata.attributes) {
            if (instanceMetadata.attributes.hasOwnProperty(attrId)) {
                writer.writeAttributeString(attrId, instanceMetadata.attributes[attrId]);
            }
        }
    };

    var _writeInstance = function (writer, instanceMetadata, manualChildren) {
        writer.writeStartElement('instance');
        _writeInstanceAttributes(writer, instanceMetadata);
        if (manualChildren && instanceMetadata.children) {
            // seriously, this is what you have to do
            // HT: http://stackoverflow.com/questions/652763/jquery-object-to-string
            writer.writeXML($('<div>').append(instanceMetadata.children).clone().html());
        }
        writer.writeEndElement();
    };

    var createDataBlock = function (form, xmlWriter) {
        form.dataTree.walk(function (mug, nodeID, processChildren) {
            xmlWriter.writeStartElement(nodeID);
            if (!mug) {
                // tree root
                createModelHeader(form, xmlWriter);
            } else {
                var rawDataAttributes = mug.p.rawDataAttributes;
                // Write any custom attributes first
                for (var k in rawDataAttributes) {
                    if (rawDataAttributes.hasOwnProperty(k)) {
                        xmlWriter.writeAttributeString(k, rawDataAttributes[k]);
                    }
                }

                var dataValue = mug.p.dataValue,
                    keyAttr = mug.p.keyAttr,
                    xmlnsAttr = mug.p.xmlnsAttr;

                if (dataValue){
                    xmlWriter.writeString(dataValue);
                }
                if (keyAttr){
                    xmlWriter.writeAttributeString("key", keyAttr);
                }
                if (xmlnsAttr){
                    xmlWriter.writeAttributeString("xmlns", xmlnsAttr);
                }
                if (mug.__className === "Repeat"){
                    xmlWriter.writeAttributeString("jr:template","");
                }
            }
            processChildren(mug && mug.options.dataChildFilter);
            xmlWriter.writeEndElement();
        });
    };

    var createBindList = function (form, xmlWriter) {
        var bList = form.getBindList(),
            mug, i, attrs, j;

        function populateVariables (mug){
            var constraintMsgItextID = mug.p.constraintMsgItextID,
                constraintMsg;
            if (constraintMsgItextID && !constraintMsgItextID.isEmpty()) {
                constraintMsg = "jr:itext('" + constraintMsgItextID.id + "')";
            } else {
                constraintMsg = mug.p.constraintMsgAttr;
            }

            return {
                nodeset: form.getAbsolutePath(mug),
                type: mug.p.dataType,
                constraint: mug.p.constraintAttr,
                "jr:constraintMsg": constraintMsg,
                relevant: mug.p.relevantAttr,
                required: util.createXPathBoolFromJS(mug.p.requiredAttr),
                calculate: mug.p.calculateAttr,
                "jr:preload": mug.p.preload,
                "jr:preloadParams": mug.p.preloadParams
            };
        }

        for (i in bList) {
            if(bList.hasOwnProperty(i)){
                mug = bList[i];
                attrs = populateVariables(mug);
                if(attrs.nodeset){
                    xmlWriter.writeStartElement('bind');
                    for (j in attrs) {
                        if (attrs.hasOwnProperty(j) && attrs[j]) {
                            xmlWriter.writeAttributeString(j, attrs[j]);
                        }
                    }
                    _(mug.p.rawBindAttributes).each(function (v, k) {
                        if (!attrs.hasOwnProperty(k)) {
                            xmlWriter.writeAttributeString(k, v);
                        }
                    });
                    xmlWriter.writeEndElement();
                }
            }
        }
    };

    var createControlBlock = function (form, xmlWriter) {
        form.controlTree.walk(function (mug, nodeID, processChildren) {
            if(!mug) {
                // root node
                processChildren();
                return;
            }

            if (mug.__className === "ReadOnly") {
                xmlWriter.writeXML($('<div>').append(mug.p.rawControlXML).clone().html());
                return;
            }

            function createOpenControlTag(form, xmlWriter, mug) {
                var tagName = mug.p.tagName.toLowerCase(),
                    isGroupOrRepeat = (tagName === 'group' || tagName === 'repeat'),
                    isODKMedia = (tagName === 'upload');

                /**
                 * Creates the label tag inside of a control Element in the xform
                 */
                function createLabel() {
                    var labelItextID = mug.p.labelItextID,
                        labelRef;
                    if (labelItextID) {
                        labelRef = "jr:itext('" + labelItextID.id + "')";
                        // iID is optional so by extension Itext is optional.
                        if (labelItextID.isEmpty() &&
                                mug.spec.labelItextID.presence === 'optional') {
                            labelRef = '';
                        }
                    }
                    if (labelRef || mug.p.label) {
                        xmlWriter.writeStartElement('label');
                        if (labelRef) {
                            xmlWriter.writeAttributeString('ref', labelRef);
                        }
                        if (mug.p.label) {
                            xmlWriter.writeString(mug.p.label);
                        }
                        xmlWriter.writeEndElement(); // close label
                    }
                }

                // Special logic block to make sure the label ends up in the right place
                if (isGroupOrRepeat) {
                    xmlWriter.writeStartElement('group');
                    createLabel();
                    if (tagName === 'repeat') {
                        xmlWriter.writeStartElement('repeat');
                    }
                } else {
                    xmlWriter.writeStartElement(tagName);
                }
                if (tagName !== 'group' && tagName !== 'repeat' &&
                    tagName !== 'itemset')
                {
                    createLabel();
                }

                var defaultValue = mug.p.defaultValue;
                if (tagName === 'item' && defaultValue) {
                    //do a value tag for an item Mug
                    xmlWriter.writeStartElement('value');
                    xmlWriter.writeString(defaultValue);
                    xmlWriter.writeEndElement();
                }

                if (tagName === 'itemset') {
                    var data = mug.p.itemsetData;
                    xmlWriter.writeAttributeString(
                        'nodeset', data.getAttr('nodeset', ''));
                    xmlWriter.writeStartElement('label');
                    xmlWriter.writeAttributeString(
                        'ref', data.getAttr('labelRef', ''));
                    xmlWriter.writeEndElement();
                    xmlWriter.writeStartElement('value');
                    xmlWriter.writeAttributeString(
                        'ref', data.getAttr('valueRef', ''));
                    xmlWriter.writeEndElement();
                }

                // Write any custom attributes first
                // HACK skip legacy attributes that should not be preserved
                var skip = (tagName === "repeat") ?
                    function (k) { return k.toLowerCase() === "jr:noaddremove"; } :
                    function (k) { return false; };
                var rawControlAttributes = mug.p.rawControlAttributes;
                for (var k in rawControlAttributes) {
                    if (rawControlAttributes.hasOwnProperty(k) && !skip(k)) {
                        xmlWriter.writeAttributeString(k, rawControlAttributes[k]);
                    }
                }

                // Set the nodeset/ref attribute correctly
                if (tagName !== 'item' && tagName !== 'itemset') {
                    var attr, absPath;
                    if (tagName === 'repeat') {
                        attr = 'nodeset';
                    } else {
                        attr = 'ref';
                    }
                    absPath = form.getAbsolutePath(mug);
                    xmlWriter.writeAttributeString(attr, absPath);
                }

                // Set other relevant attributes

                if (tagName === 'repeat') {
                    var r_count = mug.p.repeat_count;
                    if (r_count) {
                        xmlWriter.writeAttributeString("jr:count", String(r_count));
                        xmlWriter.writeAttributeString("jr:noAddRemove", "true()");
                    }
                } else if (isODKMedia) {
                    var mediaType = mug.p.mediaType;
                    if (mediaType) {
                        xmlWriter.writeAttributeString("mediatype", mediaType);
                    }
                }

                var appearanceAttr = mug.getAppearanceAttribute();
                if (appearanceAttr) {
                    xmlWriter.writeAttributeString("appearance", appearanceAttr);
                }

                // Do hint label
                if( tagName !== 'item' && tagName !== 'repeat'){
                    var hintLabel = mug.p.hintLabel,
                        hintItextID = mug.p.hintItextID;
                    if(hintLabel || (hintItextID && hintItextID.id)) {
                        xmlWriter.writeStartElement('hint');
                        if(hintLabel){
                            xmlWriter.writeString(hintLabel);
                        }
                        if(hintItextID.id){
                            var ref = "jr:itext('" + hintItextID.id + "')";
                            xmlWriter.writeAttributeString('ref',ref);
                        }
                        xmlWriter.writeEndElement();
                    }
                }
            }

            createOpenControlTag(form, xmlWriter, mug);

            processChildren(mug.options.controlChildFilter);

            //finish off
            xmlWriter.writeEndElement(); //close control tag.
            if(mug.p.tagName === 'repeat'){
                xmlWriter.writeEndElement(); //special case where we have to close the repeat as well as the group tag.
            }
        });
    };

    return {
        createXForm: createXForm
    };
});
<commit_msg>Writer: moving code around<commit_after>define([
    'jquery',
    'underscore',
    'XMLWriter',
    'vellum/util'
], function (
    $,
    _,
    XMLWriter,
    util
) {
    var createXForm = function (form) {
        var xmlWriter = new XMLWriter( 'UTF-8', '1.0' );
        form.vellum.beforeSerialize();

        xmlWriter.writeStartDocument();
        //Generate header boilerplate up to instance level
        xmlWriter.writeStartElement('h:html');
        write_html_tag_boilerplate(xmlWriter);
        xmlWriter.writeStartElement('h:head');
        xmlWriter.writeStartElement('h:title');
        xmlWriter.writeString(form.formName);
        xmlWriter.writeEndElement();       //CLOSE TITLE

        xmlWriter.writeStartElement('model');
        xmlWriter.writeStartElement('instance');
        _writeInstanceAttributes(xmlWriter, form.instanceMetadata[0]);

        createDataBlock(form, xmlWriter);
        xmlWriter.writeEndElement(); //CLOSE MAIN INSTANCE

        // other instances
        for (var i = 1; i < form.instanceMetadata.length; i++) {
            _writeInstance(xmlWriter, form.instanceMetadata[i], true);
        }

        createBindList(form, xmlWriter);

        _.each(form.getSetValues(), function (setValue) {
            xmlWriter.writeStartElement('setvalue');
            xmlWriter.writeAttributeString('event', setValue.event);
            xmlWriter.writeAttributeString('ref', setValue.ref);
            xmlWriter.writeAttributeString('value', setValue.value);
            xmlWriter.writeEndElement();
        });

        form.vellum.contributeToModelXML(xmlWriter);

        xmlWriter.writeEndElement(); //CLOSE MODEL

        form.vellum.contributeToHeadXML(xmlWriter, form);

        xmlWriter.writeEndElement(); //CLOSE HEAD

        xmlWriter.writeStartElement('h:body');
        createControlBlock(form, xmlWriter);
        xmlWriter.writeEndElement(); //CLOSE BODY
        xmlWriter.writeEndElement(); //CLOSE HTML
        xmlWriter.writeEndDocument(); //CLOSE DOCUMENT

        var ret = xmlWriter.flush();

        return ret;
    };

    var createModelHeader = function (form, xmlWriter) {
        var uuid, jrm;
        //assume we're currently pointed at the opening date block tag
        //e.g. <model><instance><data> <--- we're at <data> now.

        jrm = form.formJRM;
        if(!jrm) {
            jrm = "http://dev.commcarehq.org/jr/xforms";
        }

        uuid = form.formUuid; //gets set at parse time/by UI
        if(!uuid) {
            uuid = "http://openrosa.org/formdesigner/" + util.generate_xmlns_uuid();
        }

        xmlWriter.writeAttributeString("xmlns:jrm",jrm);
        xmlWriter.writeAttributeString("xmlns", uuid);
        xmlWriter.writeAttributeString("uiVersion", form.formUIVersion || "1");
        xmlWriter.writeAttributeString("version", form.formVersion || "1");
        xmlWriter.writeAttributeString("name", form.formName || "New Form");
    };

    function write_html_tag_boilerplate (xmlWriter) {
        xmlWriter.writeAttributeString( "xmlns:h", "http://www.w3.org/1999/xhtml" );
        xmlWriter.writeAttributeString( "xmlns:orx", "http://openrosa.org/jr/xforms" );
        xmlWriter.writeAttributeString( "xmlns", "http://www.w3.org/2002/xforms" );
        xmlWriter.writeAttributeString( "xmlns:xsd", "http://www.w3.org/2001/XMLSchema" );
        xmlWriter.writeAttributeString( "xmlns:jr", "http://openrosa.org/javarosa" );
        xmlWriter.writeAttributeString( "xmlns:vellum", "http://commcarehq.org/xforms/vellum" );
    }

    var _writeInstanceAttributes = function (writer, instanceMetadata) {
        for (var attrId in instanceMetadata.attributes) {
            if (instanceMetadata.attributes.hasOwnProperty(attrId)) {
                writer.writeAttributeString(attrId, instanceMetadata.attributes[attrId]);
            }
        }
    };

    var _writeInstance = function (writer, instanceMetadata, manualChildren) {
        writer.writeStartElement('instance');
        _writeInstanceAttributes(writer, instanceMetadata);
        if (manualChildren && instanceMetadata.children) {
            // seriously, this is what you have to do
            // HT: http://stackoverflow.com/questions/652763/jquery-object-to-string
            writer.writeXML($('<div>').append(instanceMetadata.children).clone().html());
        }
        writer.writeEndElement();
    };

    var createDataBlock = function (form, xmlWriter) {
        form.dataTree.walk(function (mug, nodeID, processChildren) {
            xmlWriter.writeStartElement(nodeID);
            if (!mug) {
                // tree root
                createModelHeader(form, xmlWriter);
            } else {
                var rawDataAttributes = mug.p.rawDataAttributes;
                // Write any custom attributes first
                for (var k in rawDataAttributes) {
                    if (rawDataAttributes.hasOwnProperty(k)) {
                        xmlWriter.writeAttributeString(k, rawDataAttributes[k]);
                    }
                }

                var dataValue = mug.p.dataValue,
                    keyAttr = mug.p.keyAttr,
                    xmlnsAttr = mug.p.xmlnsAttr;

                if (dataValue){
                    xmlWriter.writeString(dataValue);
                }
                if (keyAttr){
                    xmlWriter.writeAttributeString("key", keyAttr);
                }
                if (xmlnsAttr){
                    xmlWriter.writeAttributeString("xmlns", xmlnsAttr);
                }
                if (mug.__className === "Repeat"){
                    xmlWriter.writeAttributeString("jr:template","");
                }
            }
            processChildren(mug && mug.options.dataChildFilter);
            xmlWriter.writeEndElement();
        });
    };

    var createBindList = function (form, xmlWriter) {
        var bList = form.getBindList(),
            mug, i, attrs, j;

        function populateVariables (mug){
            var constraintMsgItextID = mug.p.constraintMsgItextID,
                constraintMsg;
            if (constraintMsgItextID && !constraintMsgItextID.isEmpty()) {
                constraintMsg = "jr:itext('" + constraintMsgItextID.id + "')";
            } else {
                constraintMsg = mug.p.constraintMsgAttr;
            }

            return {
                nodeset: form.getAbsolutePath(mug),
                type: mug.p.dataType,
                constraint: mug.p.constraintAttr,
                "jr:constraintMsg": constraintMsg,
                relevant: mug.p.relevantAttr,
                required: util.createXPathBoolFromJS(mug.p.requiredAttr),
                calculate: mug.p.calculateAttr,
                "jr:preload": mug.p.preload,
                "jr:preloadParams": mug.p.preloadParams
            };
        }

        for (i in bList) {
            if(bList.hasOwnProperty(i)){
                mug = bList[i];
                attrs = populateVariables(mug);
                if(attrs.nodeset){
                    xmlWriter.writeStartElement('bind');
                    for (j in attrs) {
                        if (attrs.hasOwnProperty(j) && attrs[j]) {
                            xmlWriter.writeAttributeString(j, attrs[j]);
                        }
                    }
                    _(mug.p.rawBindAttributes).each(function (v, k) {
                        if (!attrs.hasOwnProperty(k)) {
                            xmlWriter.writeAttributeString(k, v);
                        }
                    });
                    xmlWriter.writeEndElement();
                }
            }
        }
    };

    var createControlBlock = function (form, xmlWriter) {
        form.controlTree.walk(function (mug, nodeID, processChildren) {
            if(!mug) {
                // root node
                processChildren();
                return;
            }

            if (mug.__className === "ReadOnly") {
                xmlWriter.writeXML($('<div>').append(mug.p.rawControlXML).clone().html());
                return;
            }

            createOpenControlTag(form, xmlWriter, mug);

            processChildren(mug.options.controlChildFilter);

            //finish off
            xmlWriter.writeEndElement(); //close control tag.
            if(mug.p.tagName === 'repeat'){
                xmlWriter.writeEndElement(); //special case where we have to close the repeat as well as the group tag.
            }
        });
    };

    function createOpenControlTag(form, xmlWriter, mug) {
        var tagName = mug.p.tagName.toLowerCase(),
            isGroupOrRepeat = (tagName === 'group' || tagName === 'repeat'),
            isODKMedia = (tagName === 'upload');

        /**
         * Creates the label tag inside of a control Element in the xform
         */
        function createLabel() {
            var labelItextID = mug.p.labelItextID,
                labelRef;
            if (labelItextID) {
                labelRef = "jr:itext('" + labelItextID.id + "')";
                // iID is optional so by extension Itext is optional.
                if (labelItextID.isEmpty() &&
                        mug.spec.labelItextID.presence === 'optional') {
                    labelRef = '';
                }
            }
            if (labelRef || mug.p.label) {
                xmlWriter.writeStartElement('label');
                if (labelRef) {
                    xmlWriter.writeAttributeString('ref', labelRef);
                }
                if (mug.p.label) {
                    xmlWriter.writeString(mug.p.label);
                }
                xmlWriter.writeEndElement(); // close label
            }
        }

        // Special logic block to make sure the label ends up in the right place
        if (isGroupOrRepeat) {
            xmlWriter.writeStartElement('group');
            createLabel();
            if (tagName === 'repeat') {
                xmlWriter.writeStartElement('repeat');
            }
        } else {
            xmlWriter.writeStartElement(tagName);
        }
        if (tagName !== 'group' && tagName !== 'repeat' &&
            tagName !== 'itemset')
        {
            createLabel();
        }

        var defaultValue = mug.p.defaultValue;
        if (tagName === 'item' && defaultValue) {
            //do a value tag for an item Mug
            xmlWriter.writeStartElement('value');
            xmlWriter.writeString(defaultValue);
            xmlWriter.writeEndElement();
        }

        if (tagName === 'itemset') {
            var data = mug.p.itemsetData;
            xmlWriter.writeAttributeString(
                'nodeset', data.getAttr('nodeset', ''));
            xmlWriter.writeStartElement('label');
            xmlWriter.writeAttributeString(
                'ref', data.getAttr('labelRef', ''));
            xmlWriter.writeEndElement();
            xmlWriter.writeStartElement('value');
            xmlWriter.writeAttributeString(
                'ref', data.getAttr('valueRef', ''));
            xmlWriter.writeEndElement();
        }

        // Write any custom attributes first
        // HACK skip legacy attributes that should not be preserved
        var skip = (tagName === "repeat") ?
            function (k) { return k.toLowerCase() === "jr:noaddremove"; } :
            function (k) { return false; };
        var rawControlAttributes = mug.p.rawControlAttributes;
        for (var k in rawControlAttributes) {
            if (rawControlAttributes.hasOwnProperty(k) && !skip(k)) {
                xmlWriter.writeAttributeString(k, rawControlAttributes[k]);
            }
        }

        // Set the nodeset/ref attribute correctly
        if (tagName !== 'item' && tagName !== 'itemset') {
            var attr, absPath;
            if (tagName === 'repeat') {
                attr = 'nodeset';
            } else {
                attr = 'ref';
            }
            absPath = form.getAbsolutePath(mug);
            xmlWriter.writeAttributeString(attr, absPath);
        }

        // Set other relevant attributes

        if (tagName === 'repeat') {
            var r_count = mug.p.repeat_count;
            if (r_count) {
                xmlWriter.writeAttributeString("jr:count", String(r_count));
                xmlWriter.writeAttributeString("jr:noAddRemove", "true()");
            }
        } else if (isODKMedia) {
            var mediaType = mug.p.mediaType;
            if (mediaType) {
                xmlWriter.writeAttributeString("mediatype", mediaType);
            }
        }

        var appearanceAttr = mug.getAppearanceAttribute();
        if (appearanceAttr) {
            xmlWriter.writeAttributeString("appearance", appearanceAttr);
        }

        // Do hint label
        if( tagName !== 'item' && tagName !== 'repeat'){
            var hintLabel = mug.p.hintLabel,
                hintItextID = mug.p.hintItextID;
            if(hintLabel || (hintItextID && hintItextID.id)) {
                xmlWriter.writeStartElement('hint');
                if(hintLabel){
                    xmlWriter.writeString(hintLabel);
                }
                if(hintItextID.id){
                    var ref = "jr:itext('" + hintItextID.id + "')";
                    xmlWriter.writeAttributeString('ref',ref);
                }
                xmlWriter.writeEndElement();
            }
        }
    }

    return {
        createXForm: createXForm
    };
});
<|endoftext|>


<commit_before>/***
 *
 * Paper.js
 *
 * A JavaScript Vector Graphics Library, based on Scriptographer.org and
 * designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved.
 *
 ***
 *
 * Bootstrap.js JavaScript Framework.
 * http://bootstrapjs.org/
 *
 * Distributed under the MIT license.
 *
 * Copyright (c) 2006 - 2011 Juerg Lehni
 * http://lehni.org/
 *
 ***
 *
 * Parse-JS, A JavaScript tokenizer / parser / generator.
 *
 * Distributed under the BSD license.
 *
 * Copyright (c) 2010, Mihai Bazon <mihai.bazon@gmail.com>
 * http://mihai.bazon.net/blog/
 *
 ***/

var paper = new function() {
// Inline Bootstrap core (the Base class) inside the paper scope first:
//#include "../lib/bootstrap.js"

//#include "core/Base.js"
//#include "core/PaperScope.js"

// Include Paper classes, which are later injected into PaperScope by setting
// them on the 'this' object, e.g.:
// var Point = this.Point = Base.extend(...);

//#include "basic/Point.js"
//#include "basic/Size.js"
//#include "basic/Rectangle.js"
//#include "basic/Matrix.js"
//#include "basic/Line.js"

//#include "project/ProjectView.js"
//#include "project/Project.js"
//#include "project/Symbol.js"

//#include "item/ChangeFlags.js"
//#include "item/Item.js"
//#include "item/Group.js"
//#include "item/Layer.js"
//#include "item/Raster.js"
//#include "item/PlacedSymbol.js"
//#include "item/PathStyle.js"

//#include "path/Segment.js"
//#include "path/SegmentPoint.js"
//#include "path/SelectionState.js"
//#include "path/Curve.js"
//#include "path/CurveLocation.js"
//#include "path/PathItem.js"
//#include "path/Path.js"
//#include "path/CompoundPath.js"
//#include "path/Path.Constructors.js"

//#include "text/ParagraphStyle.js"
//#include "text/CharacterStyle.js"
//#include "text/TextItem.js"
//#include "text/PointText.js"

//#include "color/Color.js"
//#include "color/RGBColor.js"
//#include "color/HSBColor.js"
//#include "color/GrayColor.js"
//#include "color/GradientColor.js"
//#include "color/Gradient.js"
//#include "color/GradientStop.js"

//#ifdef BROWSER

//#include "browser/DomElement.js"
//#include "browser/DomEvent.js"
//#include "browser/Request.js"

//#include "ui/Event.js"
//#include "ui/KeyEvent.js"
//#include "ui/Key.js"

//#include "tool/ToolEvent.js"
//#include "tool/ToolHandler.js"
//#include "tool/Tool.js"

//#endif // BROWSER

//#include "util/CanvasProvider.js"
//#include "util/Numerical.js"
//#include "util/BlendMode.js"

//#include "core/PaperScript.js"

// Finally inject the classes set on 'this' into the PaperScope class and create
// the first PaperScope and return it, all in one statement.
return new (PaperScope.inject(this));
};
<commit_msg>Remove reference to inexisting Request.js file.<commit_after>/***
 *
 * Paper.js
 *
 * A JavaScript Vector Graphics Library, based on Scriptographer.org and
 * designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved.
 *
 ***
 *
 * Bootstrap.js JavaScript Framework.
 * http://bootstrapjs.org/
 *
 * Distributed under the MIT license.
 *
 * Copyright (c) 2006 - 2011 Juerg Lehni
 * http://lehni.org/
 *
 ***
 *
 * Parse-JS, A JavaScript tokenizer / parser / generator.
 *
 * Distributed under the BSD license.
 *
 * Copyright (c) 2010, Mihai Bazon <mihai.bazon@gmail.com>
 * http://mihai.bazon.net/blog/
 *
 ***/

var paper = new function() {
// Inline Bootstrap core (the Base class) inside the paper scope first:
//#include "../lib/bootstrap.js"

//#include "core/Base.js"
//#include "core/PaperScope.js"

// Include Paper classes, which are later injected into PaperScope by setting
// them on the 'this' object, e.g.:
// var Point = this.Point = Base.extend(...);

//#include "basic/Point.js"
//#include "basic/Size.js"
//#include "basic/Rectangle.js"
//#include "basic/Matrix.js"
//#include "basic/Line.js"

//#include "project/ProjectView.js"
//#include "project/Project.js"
//#include "project/Symbol.js"

//#include "item/ChangeFlags.js"
//#include "item/Item.js"
//#include "item/Group.js"
//#include "item/Layer.js"
//#include "item/Raster.js"
//#include "item/PlacedSymbol.js"
//#include "item/PathStyle.js"

//#include "path/Segment.js"
//#include "path/SegmentPoint.js"
//#include "path/SelectionState.js"
//#include "path/Curve.js"
//#include "path/CurveLocation.js"
//#include "path/PathItem.js"
//#include "path/Path.js"
//#include "path/CompoundPath.js"
//#include "path/Path.Constructors.js"

//#include "text/ParagraphStyle.js"
//#include "text/CharacterStyle.js"
//#include "text/TextItem.js"
//#include "text/PointText.js"

//#include "color/Color.js"
//#include "color/RGBColor.js"
//#include "color/HSBColor.js"
//#include "color/GrayColor.js"
//#include "color/GradientColor.js"
//#include "color/Gradient.js"
//#include "color/GradientStop.js"

//#ifdef BROWSER

//#include "browser/DomElement.js"
//#include "browser/DomEvent.js"

//#include "ui/Event.js"
//#include "ui/KeyEvent.js"
//#include "ui/Key.js"

//#include "tool/ToolEvent.js"
//#include "tool/ToolHandler.js"
//#include "tool/Tool.js"

//#endif // BROWSER

//#include "util/CanvasProvider.js"
//#include "util/Numerical.js"
//#include "util/BlendMode.js"

//#include "core/PaperScript.js"

// Finally inject the classes set on 'this' into the PaperScope class and create
// the first PaperScope and return it, all in one statement.
return new (PaperScope.inject(this));
};
<|endoftext|>


<commit_before>"use strict";
module.exports = function(
    Promise, PromiseArray, tryConvertToPromise, apiRejection) {
var ASSERT = require("./assert");
var util = require("./util");
var isObject = util.isObject;
var es5 = require("./es5");
var Es6Map;
if (typeof Map === "function") Es6Map = Map;

var mapToEntries = (function() {
    var index = 0;
    var size = 0;

    function extractEntry(value, key) {
        this[index] = value;
        this[index + size] = key;
        index++;
    }

    return function mapToEntries(map) {
        size = map.size;
        index = 0;
        var ret = new Array(map.size * 2);
        map.forEach(extractEntry, ret);
        return ret;
    };
})();

var entriesToMap = function(entries) {
    var ret = new Es6Map();
    var length = entries.length / 2 | 0;
    for (var i = 0; i < length; ++i) {
        var key = entries[length + i];
        var value = entries[i];
        ret.set(key, value);
    }
    return ret;
};

function PropertiesPromiseArray(obj) {
    var isMap = false;
    var entries;
    if (Es6Map !== undefined && obj instanceof Es6Map) {
        entries = mapToEntries(obj);
        isMap = true;
    } else {
        var keys = es5.keys(obj);
        var len = keys.length;
        entries = new Array(len * 2);
        for (var i = 0; i < len; ++i) {
            var key = keys[i];
            entries[i] = obj[key];
            entries[i + len] = key;
        }
    }
    this.constructor$(entries);
    this._isMap = isMap;
    this._init$(undefined, RESOLVE_OBJECT);
}
util.inherits(PropertiesPromiseArray, PromiseArray);

//Override
PropertiesPromiseArray.prototype._init = function () {};

//Override
PropertiesPromiseArray.prototype._promiseFulfilled = function (value, index) {
    ASSERT(!this._isResolved());
    ASSERT(!(value instanceof Promise));
    this._values[index] = value;
    var totalResolved = ++this._totalResolved;
    if (totalResolved >= this._length) {
        var val;
        if (this._isMap) {
            val = entriesToMap(this._values);
        } else {
            val = {};
            var keyOffset = this.length();
            for (var i = 0, len = this.length(); i < len; ++i) {
                val[this._values[i + keyOffset]] = this._values[i];
            }
        }
        this._resolve(val);
        return true;
    }
    return false;
};

// Override
PropertiesPromiseArray.prototype.shouldCopyValues = function () {
    return false;
};

// Override
PropertiesPromiseArray.prototype.getActualLength = function (len) {
    return len >> 1;
};

function props(promises) {
    var ret;
    var castValue = tryConvertToPromise(promises);

    if (!isObject(castValue)) {
        return apiRejection(PROPS_TYPE_ERROR);
    } else if (castValue instanceof Promise) {
        ret = castValue._then(
            Promise.props, undefined, undefined, undefined, undefined);
    } else {
        ret = new PropertiesPromiseArray(castValue).promise();
    }

    if (castValue instanceof Promise) {
        ret._propagateFrom(castValue, PROPAGATE_BIND);
    }
    return ret;
}

Promise.prototype.props = function () {
    return props(this);
};

Promise.props = function (promises) {
    return props(promises);
};
};
<commit_msg>workaround for safari not liking mangled code<commit_after>"use strict";
module.exports = function(
    Promise, PromiseArray, tryConvertToPromise, apiRejection) {
var ASSERT = require("./assert");
var util = require("./util");
var isObject = util.isObject;
var es5 = require("./es5");
var Es6Map;
if (typeof Map === "function") Es6Map = Map;

var mapToEntries = (function() {
    var index = 0;
    var size = 0;

    function extractEntry(value, key) {
        this[index] = value;
        this[index + size] = key;
        index++;
    }

    // NOTE: this used to be named function
    // but making it anonymous to make safari
    // happy. safari did not like
    // optimized/mangled output of this named function.
    return function (map) {
        size = map.size;
        index = 0;
        var ret = new Array(map.size * 2);
        map.forEach(extractEntry, ret);
        return ret;
    };
})();

var entriesToMap = function(entries) {
    var ret = new Es6Map();
    var length = entries.length / 2 | 0;
    for (var i = 0; i < length; ++i) {
        var key = entries[length + i];
        var value = entries[i];
        ret.set(key, value);
    }
    return ret;
};

function PropertiesPromiseArray(obj) {
    var isMap = false;
    var entries;
    if (Es6Map !== undefined && obj instanceof Es6Map) {
        entries = mapToEntries(obj);
        isMap = true;
    } else {
        var keys = es5.keys(obj);
        var len = keys.length;
        entries = new Array(len * 2);
        for (var i = 0; i < len; ++i) {
            var key = keys[i];
            entries[i] = obj[key];
            entries[i + len] = key;
        }
    }
    this.constructor$(entries);
    this._isMap = isMap;
    this._init$(undefined, RESOLVE_OBJECT);
}
util.inherits(PropertiesPromiseArray, PromiseArray);

//Override
PropertiesPromiseArray.prototype._init = function () {};

//Override
PropertiesPromiseArray.prototype._promiseFulfilled = function (value, index) {
    ASSERT(!this._isResolved());
    ASSERT(!(value instanceof Promise));
    this._values[index] = value;
    var totalResolved = ++this._totalResolved;
    if (totalResolved >= this._length) {
        var val;
        if (this._isMap) {
            val = entriesToMap(this._values);
        } else {
            val = {};
            var keyOffset = this.length();
            for (var i = 0, len = this.length(); i < len; ++i) {
                val[this._values[i + keyOffset]] = this._values[i];
            }
        }
        this._resolve(val);
        return true;
    }
    return false;
};

// Override
PropertiesPromiseArray.prototype.shouldCopyValues = function () {
    return false;
};

// Override
PropertiesPromiseArray.prototype.getActualLength = function (len) {
    return len >> 1;
};

function props(promises) {
    var ret;
    var castValue = tryConvertToPromise(promises);

    if (!isObject(castValue)) {
        return apiRejection(PROPS_TYPE_ERROR);
    } else if (castValue instanceof Promise) {
        ret = castValue._then(
            Promise.props, undefined, undefined, undefined, undefined);
    } else {
        ret = new PropertiesPromiseArray(castValue).promise();
    }

    if (castValue instanceof Promise) {
        ret._propagateFrom(castValue, PROPAGATE_BIND);
    }
    return ret;
}

Promise.prototype.props = function () {
    return props(this);
};

Promise.props = function (promises) {
    return props(promises);
};
};
<|endoftext|>


<commit_before>(function(){

  var global = this;

  var boundMethodFlag = {};
  var Proxy = function(target){
    _.raiseIf(window.jQuery && target instanceof window.jQuery || target.nodeType === 1, 'bound() cannot yet proxy node-like objects');
    _.raiseIf(target instanceof Proxy, "can't bind a proxy to another proxy");
    if(target.hasOwnProperty('bound')){
      return isBoundMethod(target.bound) ? target.bound('proxy') : _.raise("'bound' key already on object");
    }
    this.target = target;
    var proxy = this;
    _.extend(target, {
      _dependentContextSets: {},
      bound: function(commandName) {
        _.raiseIf(this !== target, "cannot call bound on foreign objects.");
        _.raiseIf(target.bound === undefined, "cannot call bound on objects that lack a bound method.");
        return proxy[commandName].apply(proxy, _.toArray(arguments).slice(1));
      }
    });
    target.bound.prototype = boundMethodFlag;
  };

  Proxy.prototype = {
    constructor: Proxy,

    proxy: function(){
      return this;
    },

    // when no command is passed at all
    'undefined': function(){
      //todo: this probably never deletes context sets stored at keys that are entirely cleared from contexts
      _.invoke(this.target._dependentContextSets, 'invalidateAll');
      return this;
    },

    has: function(key){
      this._addKeyDependency(key);
      return key in this.target;
    },
    get: function(key){
      this._addKeyDependency(key);
      return this.target[key];
    },
    set: function(key, value){
      // todo: keep track of the current state to compare to future states, here and in del
      this.target[key] = value;
      this._ensuredContextSet(key).invalidateAll();
    },
    del: function(key){
      delete this.target[key];
      this._ensuredContextSet(key).invalidateAll();
    },
    owns: function(key){
      this._addKeyDependency(key);
      return this.hasOwnProperty(key);
    },
    run: function(){
    },
    exec: function(){
    },
    pub: function(){
    },
    sub: function(){
    },
    meta: function(){
    },

    _addKeyDependency: function(key){
      this._ensuredContextSet(key).addCurrentContext();
    },

    _ensuredContextSet: function(key){
      return (this.target._dependentContextSets[key] = this.target._dependentContextSets[key] || new bound._ContextSet());
    }

  };

  var isBoundMethod = function(item){
    return item && item.prototype === boundMethodFlag;
  };

  new Proxy(global);
  global.bound.proxy = function(target){
    return new Proxy(target).target;
  };

  bound.isBoundMethod = isBoundMethod;

  global.bound.each = function(collection, block, context){
    var args = _.extend([], arguments);
    args[1] = function(item, key){
      if(key !== bound || !bound.isBoundMethod(item)){
        block.apply(this, arguments);
      }
    };
    _.each.apply(_, args);
  };

}());
<commit_msg>todos<commit_after>(function(){

  var global = this;

  var boundMethodFlag = {};
  var Proxy = function(target){
    _.raiseIf(window.jQuery && target instanceof window.jQuery || target.nodeType === 1, 'bound() cannot yet proxy node-like objects');
    _.raiseIf(target instanceof Proxy, "can't bind a proxy to another proxy");
    if(target.hasOwnProperty('bound')){
      return isBoundMethod(target.bound) ? target.bound('proxy') : _.raise("'bound' key already on object");
    }
    this.target = target;
    var proxy = this;
    _.extend(target, {
      _dependentContextSets: {},
      bound: function(commandName) {
        _.raiseIf(this !== target, "cannot call bound on foreign objects.");
        _.raiseIf(target.bound === undefined, "cannot call bound on objects that lack a bound method.");
        return proxy[commandName].apply(proxy, _.toArray(arguments).slice(1));
      }
    });
    target.bound.prototype = boundMethodFlag;
  };

  Proxy.prototype = {
    constructor: Proxy,

    proxy: function(){
      return this;
    },

    // when no command is passed at all
    'undefined': function(){
      //todo: this probably never deletes context sets stored at keys that are entirely cleared from contexts
      _.invoke(this.target._dependentContextSets, 'invalidateAll');
      return this;
    },

    has: function(key){
      this._addKeyDependency(key);
      return key in this.target;
    },
    get: function(key){
      this._addKeyDependency(key);
      return this.target[key];
    },
    set: function(key, value){
      // todo: keep track of the current state to compare to future states, here and in del
      this.target[key] = value;
      this._ensuredContextSet(key).invalidateAll();
    },
    del: function(key){
      delete this.target[key];
      this._ensuredContextSet(key).invalidateAll();
    },
    owns: function(key){
      this._addKeyDependency(key);
      return this.hasOwnProperty(key);
    },
    run: function(){
    },
    exec: function(){
    },
    pub: function(){
    },
    sub: function(){
    },
    meta: function(){
    },

    _addKeyDependency: function(key){
      this._ensuredContextSet(key).addCurrentContext();
    },

    _ensuredContextSet: function(key){
      return (this.target._dependentContextSets[key] = this.target._dependentContextSets[key] || new bound._ContextSet());
    }

  };

  var isBoundMethod = function(item){
    return item && item.prototype === boundMethodFlag;
  };

  new Proxy(global);
  // todo: rename to .ify()
  global.bound.proxy = function(target){
    return new Proxy(target).target;
  };

  bound.isBoundMethod = isBoundMethod;

  global.bound.each = function(collection, block, context){
    var args = _.extend([], arguments);
    args[1] = function(item, key){
      //todo: write tests for bound.each
      if(key !== 'bound' || !bound.isBoundMethod(item)){
        block.apply(this, arguments);
      }
    };
    _.each.apply(_, args);
  };

}());
<|endoftext|>


<commit_before>/*!
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*!
 * @module datastore/query
 */

'use strict';

var arrify = require('arrify');

/*! Developer Documentation
 *
 * @param {module:datastore|module:transaction} scope - The parent scope the
 *     query was created from.
 */
/**
 * Build a Query object.
 *
 * **Queries are built with {module:datastore#createQuery} and
 * {module:transaction#createQuery}.**
 *
 * @resource [Datastore Queries]{@link http://goo.gl/Cag0r6}
 *
 * @constructor
 * @alias module:datastore/query
 *
 * @param {string=} namespace - Namespace to query entities from.
 * @param {string} kind - Kind to query.
 *
 * @example
 * var query = datastore.createQuery('AnimalNamespace', 'Lion');
 */
function Query(scope, namespace, kinds) {
  if (!kinds) {
    kinds = namespace;
    namespace = null;
  }

  this.scope = scope;

  this.namespace = namespace || null;
  this.kinds = kinds;

  this.filters = [];
  this.orders = [];
  this.groupByVal = [];
  this.selectVal = [];

  // pagination
  this.startVal = null;
  this.endVal = null;
  this.limitVal = -1;
  this.offsetVal = -1;
}

/**
 * Datastore allows querying on properties. Supported comparison operators
 * are `=`, `<`, `>`, `<=`, and `>=`. "Not equal" and `IN` operators are
 * currently not supported.
 *
 * *To filter by ancestors, see {module:datastore/query#hasAncestor}.*
 *
 * @resource [Datastore Filters]{@link https://cloud.google.com/datastore/docs/concepts/queries#Datastore_Filters}
 *
 * @param {string} property - The field name.
 * @param {string=} operator - Operator (=, <, >, <=, >=). Default: `=`
 * @param {*} value - Value to compare property to.
 * @return {module:datastore/query}
 *
 * @example
 * //-
 * // List all companies that are located in California.
 * //-
 * var caliQuery = query.filter('state', 'CA');
 *
 * //-
 * // List all companies named Google that have less than 400 employees.
 * //-
 * var companyQuery = query
 *   .filter('name', 'Google')
 *   .filter('size', '<', 400);
 *
 * //-
 * // To filter by key, use `__key__` for the property name. Filter on keys
 * // stored as properties is not currently supported.
 * //-
 * var key = datastore.key(['Company', 'Google']);
 * var keyQuery = query.filter('__key__', key);
 */
Query.prototype.filter = function(property, operator, value) {
  if (arguments.length === 2) {
    value = operator;
    operator = '=';
  }

  // TODO: Add filter validation.
  this.filters.push({
    name: property.trim(),
    op: operator.trim(),
    val: value
  });
  return this;
};

/**
 * Filter a query by ancestors.
 *
 * @resource [Datastore Ancestor Filters]{@link https://cloud.google.com/datastore/docs/concepts/queries#Datastore_Ancestor_filters}
 *
 * @param {Key} key - Key object to filter by.
 * @return {module:datastore/query}
 *
 * @example
 * var ancestoryQuery = query.hasAncestor(datastore.key(['Parent', 123]));
 */
Query.prototype.hasAncestor = function(key) {
  this.filters.push({ name: '__key__', op: 'HAS_ANCESTOR', val: key });
  return this;
};

/**
 * Sort the results by a property name in ascending or descending order. By
 * default, an ascending sort order will be used.
 *
 * @resource [Datastore Sort Orders]{@link https://cloud.google.com/datastore/docs/concepts/queries#Datastore_Sort_orders}
 *
 * @param {string} property - The property to order by.
 * @param {object=} options - Options object.
 * @param {boolean} options.descending - Sort the results by a property name
 *     in descending order. Default: `false`.
 * @return {module:datastore/query}
 *
 * @example
 * // Sort by size ascendingly.
 * var companiesAscending = companyQuery.order('size');
 *
 * // Sort by size descendingly.
 * var companiesDescending = companyQuery.order('size', {
 *   descending: true
 * });
 */
Query.prototype.order = function(property, options) {
  var sign = options && options.descending ? '-' : '+';

  this.orders.push({ name: property, sign: sign });
  return this;
};

/**
 * Group query results by a list of properties.
 *
 * @param {array} properties - Properties to group by.
 * @return {module:datastore/query}
 *
 * @example
 * var groupedQuery = companyQuery.groupBy(['name', 'size']);
 */
Query.prototype.groupBy = function(fieldNames) {
  this.groupByVal = arrify(fieldNames);
  return this;
};

/**
 * Retrieve only select properties from the matched entities.
 *
 * Queries that select a subset of properties are called Projection Queries.
 *
 * @resource [Projection Queries]{@link https://cloud.google.com/datastore/docs/concepts/projectionqueries}
 *
 * @param {string|string[]} fieldNames - Properties to return from the matched
 *     entities.
 * @return {module:datastore/query}
 *
 * @example
 * // Only retrieve the name property.
 * var selectQuery = companyQuery.select('name');
 *
 * // Only retrieve the name and size properties.
 * var selectQuery = companyQuery.select(['name', 'size']);
 */
Query.prototype.select = function(fieldNames) {
  this.selectVal = arrify(fieldNames);
  return this;
};

/**
 * Set a starting cursor to a query.
 *
 * @resource [Query Cursors]{@link https://cloud.google.com/datastore/docs/concepts/queries#Datastore_Query_cursors}
 *
 * @param {string} cursorToken - The starting cursor token.
 * @return {module:datastore/query}
 *
 * @example
 * var cursorToken = 'X';
 *
 * // Retrieve results starting from cursorToken.
 * var startQuery = companyQuery.start(cursorToken);
 */
Query.prototype.start = function(start) {
  this.startVal = start;
  return this;
};

/**
 * Set an ending cursor to a query.
 *
 * @resource [Query Cursors]{@link https://cloud.google.com/datastore/docs/concepts/queries#Datastore_Query_cursors}
 *
 * @param {string} cursorToken - The ending cursor token.
 * @return {module:datastore/query}
 *
 * @example
 * var cursorToken = 'X';
 *
 * // Retrieve results limited to the extent of cursorToken.
 * var endQuery = companyQuery.end(cursorToken);
 */
Query.prototype.end = function(end) {
  this.endVal = end;
  return this;
};

/**
 * Set a limit on a query.
 *
 * @resource [Query Limits]{@link https://cloud.google.com/datastore/docs/concepts/queries#Datastore_Retrieving_results}
 *
 * @param {number} n - The number of results to limit the query to.
 * @return {module:datastore/query}
 *
 * @example
 * // Limit the results to 10 entities.
 * var limitQuery = companyQuery.limit(10);
 */
Query.prototype.limit = function(n) {
  this.limitVal = n;
  return this;
};

/**
 * Set an offset on a query.
 *
 * @resource [Query Offsets]{@link https://cloud.google.com/datastore/docs/concepts/queries#Datastore_Retrieving_results}
 *
 * @param {number} n - The offset to start from after the start cursor.
 * @return {module:datastore/query}
 *
 * @example
 * // Start from the 101st result.
 * var offsetQuery = companyQuery.offset(100);
 */
Query.prototype.offset = function(n) {
  this.offsetVal = n;
  return this;
};

/**
 * Run the query.
 *
 * @param {object=} options - Optional configuration.
 * @param {string} options.consistency - Specify either `strong` or `eventual`.
 *     If not specified, default values are chosen by Datastore for the
 *     operation. Learn more about strong and eventual consistency
 *     [here](https://cloud.google.com/datastore/docs/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore).
 * @param {function=} callback - The callback function. If omitted, a readable
 *     stream instance is returned.
 * @param {?error} callback.err - An error returned while making this request
 * @param {object[]} callback.entities - A list of entities.
 * @param {object} callback.info - An object useful for pagination.
 * @param {?string} callback.info.endCursor - Use this in a follow-up query to
 *     begin from where these results ended.
 * @param {string} callback.info.moreResults - Datastore responds with one of:
 *
 *     - {module:datastore#MORE_RESULTS_AFTER_LIMIT}: There *may* be more
 *       results after the specified limit.
 *     - {module:datastore#MORE_RESULTS_AFTER_CURSOR}: There *may* be more
 *       results after the specified end cursor.
 *     - {module:datastore#NO_MORE_RESULTS}: There are no more results.
 *
 * @example
 * query.run(function(err, entities, info) {
 *   // entities = An array of records.
 *
 *   // Access the Key object for an entity.
 *   var firstEntityKey = entities[0][datastore.KEY];
 * });
 *
 * //-
 * // A keys-only query returns just the keys of the result entities instead of
 * // the entities themselves, at lower latency and cost.
 * //-
 * query.select('__key__');
 *
 * query.run(function(err, entities) {
 *   var keys = entities.map(function(entity) {
 *     return entity[datastore.KEY];
 *   });
 * });
 *
 * //-
 * // If the callback is omitted, we'll return a Promise.
 * //-
 * query.run().then(function(data) {
 *   var entities = data[0];
 * });
 */
Query.prototype.run = function() {
  var query = this;
  var args = [query].concat([].slice.call(arguments));

  return this.scope.runQuery.apply(this.scope, args);
};

/**
 * Run the query as a readable object stream.
 *
 * @param {object=} options - Optional configuration. See
 *     {module:datastore/query#run} for a complete list of options.
 * @return {stream}
 *
 * @example
 * query.runStream()
 *   .on('error', console.error)
 *   .on('data', function (entity) {
 *     // Access the Key object for this entity.
 *     var key = entity[datastore.KEY];
 *   })
 *   .on('info', function(info) {})
 *   .on('end', function() {
 *     // All entities retrieved.
 *   });
 *
 * //-
 * // If you anticipate many results, you can end a stream early to prevent
 * // unnecessary processing and API requests.
 * //-
 * query.runStream()
 *   .on('data', function (entity) {
 *     this.end();
 *   });
 */
Query.prototype.runStream = function() {
  var query = this;
  var args = [query].concat([].slice.call(arguments));

  return this.scope.runQueryStream.apply(this.scope, args);
};

module.exports = Query;
<commit_msg>Docs fixes (#2170)<commit_after>/*!
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*!
 * @module datastore/query
 */

'use strict';

var arrify = require('arrify');

/*! Developer Documentation
 *
 * @param {module:datastore|module:transaction} scope - The parent scope the
 *     query was created from.
 */
/**
 * Build a Query object.
 *
 * **Queries are built with {module:datastore#createQuery} and
 * {module:transaction#createQuery}.**
 *
 * @resource [Datastore Queries]{@link http://goo.gl/Cag0r6}
 *
 * @constructor
 * @alias module:datastore/query
 *
 * @param {string=} namespace - Namespace to query entities from.
 * @param {string} kind - Kind to query.
 *
 * @example
 * var query = datastore.createQuery('AnimalNamespace', 'Lion');
 */
function Query(scope, namespace, kinds) {
  if (!kinds) {
    kinds = namespace;
    namespace = null;
  }

  this.scope = scope;

  this.namespace = namespace || null;
  this.kinds = kinds;

  this.filters = [];
  this.orders = [];
  this.groupByVal = [];
  this.selectVal = [];

  // pagination
  this.startVal = null;
  this.endVal = null;
  this.limitVal = -1;
  this.offsetVal = -1;
}

/**
 * Datastore allows querying on properties. Supported comparison operators
 * are `=`, `<`, `>`, `<=`, and `>=`. "Not equal" and `IN` operators are
 * currently not supported.
 *
 * *To filter by ancestors, see {module:datastore/query#hasAncestor}.*
 *
 * @resource [Datastore Filters]{@link https://cloud.google.com/datastore/docs/concepts/queries#datastore-property-filter-nodejs}
 *
 * @param {string} property - The field name.
 * @param {string=} operator - Operator (=, <, >, <=, >=). Default: `=`
 * @param {*} value - Value to compare property to.
 * @return {module:datastore/query}
 *
 * @example
 * //-
 * // List all companies that are located in California.
 * //-
 * var caliQuery = query.filter('state', 'CA');
 *
 * //-
 * // List all companies named Google that have less than 400 employees.
 * //-
 * var companyQuery = query
 *   .filter('name', 'Google')
 *   .filter('size', '<', 400);
 *
 * //-
 * // To filter by key, use `__key__` for the property name. Filter on keys
 * // stored as properties is not currently supported.
 * //-
 * var key = datastore.key(['Company', 'Google']);
 * var keyQuery = query.filter('__key__', key);
 */
Query.prototype.filter = function(property, operator, value) {
  if (arguments.length === 2) {
    value = operator;
    operator = '=';
  }

  // TODO: Add filter validation.
  this.filters.push({
    name: property.trim(),
    op: operator.trim(),
    val: value
  });
  return this;
};

/**
 * Filter a query by ancestors.
 *
 * @resource [Datastore Ancestor Filters]{@link https://cloud.google.com/datastore/docs/concepts/queries#datastore-ancestor-query-nodejs}
 *
 * @param {Key} key - Key object to filter by.
 * @return {module:datastore/query}
 *
 * @example
 * var ancestoryQuery = query.hasAncestor(datastore.key(['Parent', 123]));
 */
Query.prototype.hasAncestor = function(key) {
  this.filters.push({ name: '__key__', op: 'HAS_ANCESTOR', val: key });
  return this;
};

/**
 * Sort the results by a property name in ascending or descending order. By
 * default, an ascending sort order will be used.
 *
 * @resource [Datastore Sort Orders]{@link https://cloud.google.com/datastore/docs/concepts/queries#datastore-ascending-sort-nodejs}
 *
 * @param {string} property - The property to order by.
 * @param {object=} options - Options object.
 * @param {boolean} options.descending - Sort the results by a property name
 *     in descending order. Default: `false`.
 * @return {module:datastore/query}
 *
 * @example
 * // Sort by size ascendingly.
 * var companiesAscending = companyQuery.order('size');
 *
 * // Sort by size descendingly.
 * var companiesDescending = companyQuery.order('size', {
 *   descending: true
 * });
 */
Query.prototype.order = function(property, options) {
  var sign = options && options.descending ? '-' : '+';

  this.orders.push({ name: property, sign: sign });
  return this;
};

/**
 * Group query results by a list of properties.
 *
 * @param {array} properties - Properties to group by.
 * @return {module:datastore/query}
 *
 * @example
 * var groupedQuery = companyQuery.groupBy(['name', 'size']);
 */
Query.prototype.groupBy = function(fieldNames) {
  this.groupByVal = arrify(fieldNames);
  return this;
};

/**
 * Retrieve only select properties from the matched entities.
 *
 * Queries that select a subset of properties are called Projection Queries.
 *
 * @resource [Projection Queries]{@link https://cloud.google.com/datastore/docs/concepts/projectionqueries}
 *
 * @param {string|string[]} fieldNames - Properties to return from the matched
 *     entities.
 * @return {module:datastore/query}
 *
 * @example
 * // Only retrieve the name property.
 * var selectQuery = companyQuery.select('name');
 *
 * // Only retrieve the name and size properties.
 * var selectQuery = companyQuery.select(['name', 'size']);
 */
Query.prototype.select = function(fieldNames) {
  this.selectVal = arrify(fieldNames);
  return this;
};

/**
 * Set a starting cursor to a query.
 *
 * @resource [Query Cursors]{@link https://cloud.google.com/datastore/docs/concepts/queries#cursors_limits_and_offsets}
 *
 * @param {string} cursorToken - The starting cursor token.
 * @return {module:datastore/query}
 *
 * @example
 * var cursorToken = 'X';
 *
 * // Retrieve results starting from cursorToken.
 * var startQuery = companyQuery.start(cursorToken);
 */
Query.prototype.start = function(start) {
  this.startVal = start;
  return this;
};

/**
 * Set an ending cursor to a query.
 *
 * @resource [Query Cursors]{@link https://cloud.google.com/datastore/docs/concepts/queries#Datastore_Query_cursors}
 *
 * @param {string} cursorToken - The ending cursor token.
 * @return {module:datastore/query}
 *
 * @example
 * var cursorToken = 'X';
 *
 * // Retrieve results limited to the extent of cursorToken.
 * var endQuery = companyQuery.end(cursorToken);
 */
Query.prototype.end = function(end) {
  this.endVal = end;
  return this;
};

/**
 * Set a limit on a query.
 *
 * @resource [Query Limits]{@link https://cloud.google.com/datastore/docs/concepts/queries#datastore-limit-nodejs}
 *
 * @param {number} n - The number of results to limit the query to.
 * @return {module:datastore/query}
 *
 * @example
 * // Limit the results to 10 entities.
 * var limitQuery = companyQuery.limit(10);
 */
Query.prototype.limit = function(n) {
  this.limitVal = n;
  return this;
};

/**
 * Set an offset on a query.
 *
 * @resource [Query Offsets]{@link https://cloud.google.com/datastore/docs/concepts/queries#datastore-limit-nodejs}
 *
 * @param {number} n - The offset to start from after the start cursor.
 * @return {module:datastore/query}
 *
 * @example
 * // Start from the 101st result.
 * var offsetQuery = companyQuery.offset(100);
 */
Query.prototype.offset = function(n) {
  this.offsetVal = n;
  return this;
};

/**
 * Run the query.
 *
 * @param {object=} options - Optional configuration.
 * @param {string} options.consistency - Specify either `strong` or `eventual`.
 *     If not specified, default values are chosen by Datastore for the
 *     operation. Learn more about strong and eventual consistency
 *     [here](https://cloud.google.com/datastore/docs/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore).
 * @param {function=} callback - The callback function. If omitted, a readable
 *     stream instance is returned.
 * @param {?error} callback.err - An error returned while making this request
 * @param {object[]} callback.entities - A list of entities.
 * @param {object} callback.info - An object useful for pagination.
 * @param {?string} callback.info.endCursor - Use this in a follow-up query to
 *     begin from where these results ended.
 * @param {string} callback.info.moreResults - Datastore responds with one of:
 *
 *     - {module:datastore#MORE_RESULTS_AFTER_LIMIT}: There *may* be more
 *       results after the specified limit.
 *     - {module:datastore#MORE_RESULTS_AFTER_CURSOR}: There *may* be more
 *       results after the specified end cursor.
 *     - {module:datastore#NO_MORE_RESULTS}: There are no more results.
 *
 * @example
 * query.run(function(err, entities, info) {
 *   // entities = An array of records.
 *
 *   // Access the Key object for an entity.
 *   var firstEntityKey = entities[0][datastore.KEY];
 * });
 *
 * //-
 * // A keys-only query returns just the keys of the result entities instead of
 * // the entities themselves, at lower latency and cost.
 * //-
 * query.select('__key__');
 *
 * query.run(function(err, entities) {
 *   var keys = entities.map(function(entity) {
 *     return entity[datastore.KEY];
 *   });
 * });
 *
 * //-
 * // If the callback is omitted, we'll return a Promise.
 * //-
 * query.run().then(function(data) {
 *   var entities = data[0];
 * });
 */
Query.prototype.run = function() {
  var query = this;
  var args = [query].concat([].slice.call(arguments));

  return this.scope.runQuery.apply(this.scope, args);
};

/**
 * Run the query as a readable object stream.
 *
 * @param {object=} options - Optional configuration. See
 *     {module:datastore/query#run} for a complete list of options.
 * @return {stream}
 *
 * @example
 * query.runStream()
 *   .on('error', console.error)
 *   .on('data', function (entity) {
 *     // Access the Key object for this entity.
 *     var key = entity[datastore.KEY];
 *   })
 *   .on('info', function(info) {})
 *   .on('end', function() {
 *     // All entities retrieved.
 *   });
 *
 * //-
 * // If you anticipate many results, you can end a stream early to prevent
 * // unnecessary processing and API requests.
 * //-
 * query.runStream()
 *   .on('data', function (entity) {
 *     this.end();
 *   });
 */
Query.prototype.runStream = function() {
  var query = this;
  var args = [query].concat([].slice.call(arguments));

  return this.scope.runQueryStream.apply(this.scope, args);
};

module.exports = Query;
<|endoftext|>


<commit_before>//@flow
import React from 'react'
import { connect } from 'react-redux'
import * as R from 'ramda'

import contextTypes from './context-types'

import { FETCH } from './query-reducer'

export default (
  query: string,
  queryParams: any
) => (Comp : ReactClass<any>) => (componentProps: Object) => {

  class DataHolder extends React.Component {
    constructor(props) {
      super(props)
    }
    componentWillMount() {
      const { fetch, mustFetch, params } = this.props
      const { fetcher, ttl, retryInterval } = this.context
      if (mustFetch(ttl, retryInterval)) {
        fetch(fetcher, query, params)
      }
    }
    componentWillReceiveProps(nextProps) {
      // TODO: repeats what we do in componentWillMount, but with `nextProps`
      // which causes fetching double (in some cases)
      const { fetch, mustFetch, params } = nextProps
      const { fetcher, ttl, retryInterval } = this.context
      // console.log('componentWillReceiveProps', nextProps)
      if (mustFetch(ttl, retryInterval)) {
        fetch(fetcher, query, params)
      }
    }
    shouldComponentUpdate(nextProps, nextState) {
      // console.log('shouldComponentUpdate, this.props, nextProps', this.props, nextProps)
      const { data, params, lastError } = this.props
      if (R.equals(nextProps.data, data)
      && R.equals(nextProps.params, params)
      && R.equals(nextProps.lastError, lastError)) {
        // console.log('shouldComponentUpdate, FALSE');
        return false
      }
      // console.log('shouldComponentUpdate, TRUE');
      return true
    }
    render() {
      const { query, data, lastError, children } = this.props
      const { queryLoadingIndicator } = this.context
      if (lastError) {
        // TODO: should be a configurable component to display errors
        return (
Unable to query API: { lastError.message || 'Error while fetching' }
)
      }
      return data
        ? <Comp {...this.props} {...data} />
        : queryLoadingIndicator
    }
  }
  DataHolder.contextTypes = contextTypes

  function getIndex(state, query, variables) {
    const key = JSON.stringify({ q: query, v: variables })
    const ix = state.keys.indexOf(key)
    return ix
  }

  function getQueryState(state, query, variables) {
    const ix = getIndex(state, query, variables)
    if (ix === -1) return undefined;
    return state.values[ix]
  }

  function getResult(state, query, variables) {
    const queryState = getQueryState(state, query, variables);
    if (!queryState) return undefined;
    return queryState.data
  }

  const mustFetch = R.curry((state, query, variables, ttl, retryInterval) => {
    const ix = getIndex(state, query, variables)
    const queryState = getQueryState(state, query, variables)
    if (!queryState) {
      console.log('must fetch, because no result cached yet, ix=' + ix)
      return true
    }
    if (queryState.fetching) {
      if (queryState.startFetchingAt
        && queryState.startFetchingAt + retryInterval < new Date().getTime()) {
        console.log('fetching already, but retrying, ix=' + ix)
        return true
      } else {
        console.log('not fetching, fetching already, ix=' + ix)
        return false
      }
    }
    if (!queryState.at || queryState.at + ttl < new Date().getTime()) {
      console.log('must fetch, as cache expired, ix=' + ix)
      return true
    }
    console.log('not fetching, result still cached, ix=' + ix)
    return false
  })

  const DataHolderContainer = connect((state, ownProps) => {
    const params = (typeof queryParams === 'function') ? queryParams(state, ownProps) : queryParams
    return {
      data: getResult(state.queries, query, params),
      mustFetch: (ttl, retryInterval) => mustFetch(state.queries, query, params, ttl, retryInterval),
      params
    }
  }, (dispatch, ownProps) => ({
    fetch: (fetcher, query, variables) => {
      dispatch({ type: FETCH, query, values: variables, at: new Date().getTime() })
      fetcher(dispatch, query, variables, ownProps)
    }
  }))(DataHolder)

  return <DataHolderContainer {...componentProps} />
}
<commit_msg>additional logging<commit_after>//@flow
import React from 'react'
import { connect } from 'react-redux'
import * as R from 'ramda'

import contextTypes from './context-types'

import { FETCH } from './query-reducer'

export default (
  query: string,
  queryParams: any
) => (Comp : ReactClass<any>) => (componentProps: Object) => {

  class DataHolder extends React.Component {
    constructor(props) {
      super(props)
    }
    componentWillMount() {
      const { fetch, mustFetch, params } = this.props
      const { fetcher, ttl, retryInterval } = this.context
      if (mustFetch(ttl, retryInterval)) {
        fetch(fetcher, query, params)
      }
    }
    componentWillReceiveProps(nextProps) {
      // TODO: repeats what we do in componentWillMount, but with `nextProps`
      // which causes fetching double (in some cases)
      const { fetch, mustFetch, params } = nextProps
      const { fetcher, ttl, retryInterval } = this.context
      // console.log('componentWillReceiveProps', nextProps)
      if (mustFetch(ttl, retryInterval)) {
        fetch(fetcher, query, params)
      }
    }
    shouldComponentUpdate(nextProps, nextState) {
      // console.log('shouldComponentUpdate, this.props, nextProps', this.props, nextProps)
      const { data, params, lastError } = this.props
      if (R.equals(nextProps.data, data)
      && R.equals(nextProps.params, params)
      && R.equals(nextProps.lastError, lastError)) {
        // console.log('shouldComponentUpdate, FALSE');
        return false
      }
      // console.log('shouldComponentUpdate, TRUE');
      return true
    }
    render() {
      const { query, data, lastError, children } = this.props
      const { queryLoadingIndicator } = this.context
      if (lastError) {
        // TODO: should be a configurable component to display errors
        return (
Unable to query API: { lastError.message || 'Error while fetching' }
)
      }
      return data
        ? <Comp {...this.props} {...data} />
        : queryLoadingIndicator
    }
  }
  DataHolder.contextTypes = contextTypes

  function getIndex(state, query, variables) {
    const key = JSON.stringify({ q: query, v: variables })
    const ix = state.keys.indexOf(key)
    return ix
  }

  function getQueryState(state, query, variables) {
    const ix = getIndex(state, query, variables)
    if (ix === -1) return undefined;
    return state.values[ix]
  }

  function getResult(state, query, variables) {
    const queryState = getQueryState(state, query, variables);
    if (!queryState) return undefined;
    return queryState.data
  }

  const mustFetch = R.curry((state, query, variables, ttl, retryInterval) => {
    const ix = getIndex(state, query, variables)
    const queryState = getQueryState(state, query, variables)
    if (!queryState) {
      console.log('must fetch, because no result cached yet, ix=' + ix)
      return true
    }
    if (queryState.fetching) {
      if (queryState.startFetchingAt
        && queryState.startFetchingAt + retryInterval < new Date().getTime()) {
        console.log('fetching already, but retrying, ix=' + ix)
        return true
      } else {
        console.log('not fetching, fetching already, ix=' + ix)
        return false
      }
    }
    if (!queryState.at || queryState.at + ttl < new Date().getTime()) {
      console.log('must fetch, as cache expired, ix=' + ix)
      return true
    }
    console.log('not fetching, result still cached, ix=' + ix)
    return false
  })

  const DataHolderContainer = connect((state, ownProps) => {
    const params = (typeof queryParams === 'function') ? queryParams(state, ownProps) : queryParams
    const data = getResult(state.queries, query, params)
    console.log('DataHolderContainer, data', data, query, params)
    if (!data) {
      console.log('DataHolderContainer, no data')
    }
    return {
      data: getResult(state.queries, query, params),
      mustFetch: (ttl, retryInterval) => mustFetch(state.queries, query, params, ttl, retryInterval),
      params
    }
  }, (dispatch, ownProps) => ({
    fetch: (fetcher, query, variables) => {
      dispatch({ type: FETCH, query, values: variables, at: new Date().getTime() })
      fetcher(dispatch, query, variables, ownProps)
    }
  }))(DataHolder)

  return <DataHolderContainer {...componentProps} />
}
<|endoftext|>