<commit_before>/*
 *   Copyright 2012 OSBI Ltd
 *
 *   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.
 */

var account = 'UA-16172251-20';

if (window.location.hostname && window.location.hostname == "dev.analytical-labs.com" ) {
    account = 'UA-16172251-12';
} else if (window.location.hostname && window.location.hostname == "demo.analytical-labs.com" ) {
	account = 'UA-16172251-5';
}

  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  ga('create', account, 'analytical-labs.com');
  ga('send', 'pageview');
<commit_msg>remove GA<commit_after>/*
 *   Copyright 2012 OSBI Ltd
 *
 *   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.
 */

var account = 'UA-XXXXX';
/*

  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  ga('create', account, 'analytical-labs.com');
  ga('send', 'pageview');
*/<|endoftext|>


<commit_before>define( ['dojo/_base/array',
         'JBrowse/Util',
         './Util',
         'JBrowse/Model/SimpleFeature'
        ],
        function( array, Util, BAMUtil, SimpleFeature ) {

var SEQRET_DECODER = ['=', 'A', 'C', 'x', 'G', 'x', 'x', 'x', 'T', 'x', 'x', 'x', 'x', 'x', 'x', 'N'];
var CIGAR_DECODER  = ['M', 'I', 'D', 'N', 'S', 'H', 'P', '=', 'X', '?', '?', '?', '?', '?', '?', '?'];

var readInt   = BAMUtil.readInt;
var readShort = BAMUtil.readShort;
var readFloat = BAMUtil.readFloat;

var counter = 0;

var Feature = Util.fastDeclare(
{
    constructor: function( args ) {
        this.store = args.store;
        this.file  = args.file;
        this.data  = {
            type: 'match',
            source: args.store.source
        };
        this.bytes = {
            start: args.bytes.start,
            end: args.bytes.end,
            byteArray: args.bytes.byteArray
        };

        this._coreParse();
    },

    get: function( field ) {
        field = field.toLowerCase();
        return field in this.data ? this.data[field] : // maybe already parsed
            function() {
                var v = this.data[field] = this[field] ? this[field]()                       : // maybe we have a special parser for it
                                           this._flagMasks[field] ? this._parseFlag( field ) : // or is it a flag?
                                           this._parseTag( field );                            // otherwise, must be a tag
                return v;
            }.call(this);
    },

    tags: function() {
        return this.get('_tags');
    },

    _tags: function() {
        this._parseAllTags();

        var tags = [ 'seq', 'seq_reverse_complemented', 'unmapped' ];
        if( ! this.get('unmapped') )
            tags.push( 'start', 'end', 'strand', 'score', 'qual', 'MQ', 'CIGAR', 'length_on_ref' );
        if( this.get('multi_segment_template') ) {
            tags.push( 'multi_segment_all_aligned',
                       'multi_segment_next_segment_unmapped',
                       'multi_segment_next_segment_reversed',
                       'multi_segment_first',
                       'multi_segment_last',
                       'secondary_alignment',
                       'qc_failed',
                       'duplicate',
                       'next_segment_position'
                     );
        }

        var d = this.data;
        for( var k in d ) {
            if( d.hasOwnProperty( k ) && k[0] != '_' )
                tags.push( k );
        }

        var seen = {};
        tags = array.filter( tags, function(t) {
            if( t in this.data && this.data[t] === undefined )
                return false;

            var lt = t.toLowerCase();
            var s = seen[lt];
            seen[lt] = true;
            return ! s;
        },this);

        return tags;
    },

    parent: function() {
        return undefined;
    },

    children: function() {
        return this.get('subfeatures');
    },

    id: function() {
        return this.get('name')+'/'+this.get('MD')+'/'+this.get('CIGAR')+'/'+this.get('start');
    },

    // special parsers
    /**
     * Mapping quality score.
     */
    mq: function() {
        var mq = (this.get('_bin_mq_nl') & 0xff00) >> 8;
        return mq == 255 ? undefined : mq;
    },
    score: function() {
        return this.get('MQ');
    },
    qual: function() {
        if( this.get('unmapped') )
            return undefined;

        var qseq = [];
        var byteArray = this.bytes.byteArray;
        var p = this.bytes.start + 36 + this.get('_l_read_name') + this.get('_n_cigar_op')*4 + this.get('_seq_bytes');
        var lseq = this.get('seq_length');
        for (var j = 0; j < lseq; ++j) {
            qseq.push( byteArray[p + j] );
        }
        return qseq.join(' ');
    },
    strand: function() {
        // var xs = this.get('XS');
        // return xs ? ( xs == '-' ? -1 : 1 ) :
        return this.get('seq_reverse_complemented') ? -1 :  1;
    },
    /**
     * Length in characters of the read name.
     */
    _l_read_name: function() {
        return this.get('_bin_mq_nl') & 0xff;
    },
    /**
     * number of bytes in the sequence field
     */
    _seq_bytes: function() {
        return (this.get('seq_length') + 1) >> 1;
    },
    seq: function() {
        var seq = '';
        var byteArray = this.bytes.byteArray;
        var p = this.bytes.start + 36 + this.get('_l_read_name') + this.get('_n_cigar_op')*4;
        var seqBytes = this.get('_seq_bytes');
        for (var j = 0; j < seqBytes; ++j) {
            var sb = byteArray[p + j];
            seq += SEQRET_DECODER[(sb & 0xf0) >> 4];
            seq += SEQRET_DECODER[(sb & 0x0f)];
        }
        return seq;
    },
    name: function() {
        return this.get('read_name');
    },
    read_name: function() {
        var byteArray = this.bytes.byteArray;
        var readName = '';
        var nl = this.get('_l_read_name');
        var p = this.bytes.start + 36;
        for (var j = 0; j < nl-1; ++j) {
            readName += String.fromCharCode(byteArray[p+j]);
        }
        return readName;
    },
    _n_cigar_op: function() {
        return this.get('_flag_nc') & 0xffff;
    },
    cigar: function() {
        if( this.get('unmapped') )
            return undefined;

        var byteArray   = this.bytes.byteArray;
        var numCigarOps = this.get('_n_cigar_op');
        var p = this.bytes.start + 36 + this.get('_l_read_name');
        var cigar = '';
        var lref = 0;
        for (var c = 0; c < numCigarOps; ++c) {
            var cigop = readInt(byteArray, p);
            var lop = cigop >> 4;
            var op = CIGAR_DECODER[cigop & 0xf];
            cigar = cigar + lop + op;
            p += 4;
        }
        return cigar;
    },
    next_segment_position: function() {
        var nextRefID = this.get('_next_refID');
        var nextSegment = this.file.indexToChr[nextRefID];
        if( nextSegment )
            return nextSegment.name+':'+this.get('_next_pos');
        else
            return undefined;
    },
    subfeatures: function() {
        if( ! this.store.createSubfeatures )
            return undefined;

        var cigar = this.get('CIGAR');
        if( cigar )
            return this._cigarToSubfeats( cigar );

        return undefined;
    },
    length_on_ref: function() {
        if( this.get('unmapped') )
            return undefined;

        var numbers = this.get('CIGAR').match(/\d+(?=[MDN=X])/ig);
        var sum = 0;
        for( var i = 0; i<numbers.length; i++ )
            sum += parseInt( numbers[i] );
        return sum;
    },
    _flags: function() {
        return (this.data._flag_nc & 0xffff0000) >> 16;
    },
    end: function() {
        return this.get('start') + ( this.get('length_on_ref') || this.get('seq_length') || undefined );
    },

    seq_id: function() {
        if( this.get('unmapped') )
            return undefined;

        return ( this.file.indexToChr[ this._refID ] || {} ).name;
    },

    /**
     * parse the core data: start, end, strand, etc
     */
    _coreParse: function() {
        var byteArray = this.bytes.byteArray;
        var blockStart = this.bytes.start;

        var tempBytes = new Uint8Array( 36 );
        for( var i = 0; i<36; i++ ) {
            tempBytes[i] = byteArray[i+blockStart];
        }
        var ints = new Int32Array( tempBytes.buffer );

        this._refID = ints[1];
        var start = this.data.start = ints[2];
        this.data._bin_mq_nl = ints[3];
        this.data._flag_nc   = ints[4];
        this.data.seq_length = ints[5];
        this.data._next_refID = ints[6];
        this.data._next_pos   = ints[7];
        this.data.template_length = ints[8];
    },

    /**
     * Get the value of a tag, parsing the tags as far as necessary.
     * Only called if we have not already parsed that field.
     */
    _parseTag: function( tagName ) {
        var byteArray = this.bytes.byteArray;
        var p = this.bytes.start + 36 + this.get('_l_read_name') + this.get('_n_cigar_op')*4 + this.get('_seq_bytes') + this.get('seq_length');
        var blockEnd = this.bytes.end;
        while( p < blockEnd && !(tag == tagName)) { // really should be blockEnd - 3, but this works too and is faster
            var tag = String.fromCharCode( byteArray[p] ) + String.fromCharCode(byteArray[p + 1] );
            var origType = String.fromCharCode( byteArray[p + 2] );
            var type = origType.toLowerCase();
            p += 3;

            var value;
            if (type == 'a') {
                value = String.fromCharCode( byteArray[p] );
                p += 1;
            } else if (type == 'i' ) {
                value = readInt(byteArray, p );
                p += 4;
            } else if (type == 'c' ) {
                value = byteArray[p];
                p += 1;
            } else if (type == 's' ) {
                value = readShort(byteArray, p);
                p += 2;
            } else if (type == 'f') {
                value = readFloat( byteArray, p );
                p += 4;
            } else if ( type == 'z' || type == 'h' ) {
                value = '';
                while( p <= blockEnd ) {
                    var cc = byteArray[p++];
                    if( cc == 0 ) {
                        break;
                    }
                    else {
                        value += String.fromCharCode(cc);
                    }
                }
            } else {
                console.warn( "Unknown BAM tag type '"+origType
                              +'\', tags for '+(readName||'(unnamed read)')
                              +' may be incomplete'
                            );
                value = undefined;
                p = blockEnd+1; // stop parsing tags
            }
            if( tag == tagName )
                return value;
            else
                this.data[tag] = value;
        }
        return undefined;
    },
    _parseAllTags: function() {
        if( ! this._allTagsParsed )
            this._parseTag(); // calling _parseTag with no arg just parses
                              // all the tags and returns the last one

        this._allTagsParsed = true;
    },

    _flagMasks: {
        multi_segment_template:              0x1,
        multi_segment_all_aligned:           0x2,
        unmapped:                            0x4,
        multi_segment_next_segment_unmapped: 0x8,
        seq_reverse_complemented:            0x10,
        multi_segment_next_segment_reversed: 0x20,
        multi_segment_first:                 0x40,
        multi_segment_last:                  0x80,
        secondary_alignment:                 0x100,
        qc_failed:                           0x200,
        duplicate:                           0x400
    },

    _parseFlag: function( flagName ) {
        return !!( this.get('_flags') & this._flagMasks[flagName] );
    },

    _parseCigar: function( cigar ) {
        return array.map( cigar.match(/\d+\D/g), function( op ) {
           return [ op.match(/\D/)[0].toUpperCase(), parseInt( op ) ];
        });
    },

    /**
     *  take a cigar string, and initial position, return an array of subfeatures
     */
    _cigarToSubfeats: function(cigar)    {
        var subfeats = [];
        var min = this.get('start');
        var max;
        var ops = this._parseCigar( cigar );
        for (var i = 0; i < ops.length; i++)  {
            var lop = ops[i][1];
            var op = ops[i][0];  // operation type
            // converting "=" to "E" to avoid possible problems later with non-alphanumeric type name
            if (op === "=")  { op = "E"; }

            switch (op) {
            case 'M':
            case 'D':
            case 'N':
            case 'E':
            case 'X':
                max = min + lop;
                break;
            case 'I':
                max = min;
                break;
            case 'P':  // not showing padding deletions (possibly change this later -- could treat same as 'I' ?? )
            case 'H':  // not showing hard clipping (since it's unaligned, and offset arg meant to be beginning of aligned part)
            case 'S':  // not showing soft clipping (since it's unaligned, and offset arg meant to be beginning of aligned part)
                break;
                // other possible cases
            }
            if( op !== 'N' ) {
                var subfeat = new SimpleFeature({
                    data: {
                    type: op,
                        start: min,
                        end: max,
                        strand: this.get('strand'),
                        cigar_op: lop+op
                    },
                    parent: this
                });
                subfeats.push(subfeat);
            }
            min = max;
        }
        return subfeats;
    }

});

return Feature;
});<commit_msg>add XS logic back into lazy bam strand<commit_after>define( ['dojo/_base/array',
         'JBrowse/Util',
         './Util',
         'JBrowse/Model/SimpleFeature'
        ],
        function( array, Util, BAMUtil, SimpleFeature ) {

var SEQRET_DECODER = ['=', 'A', 'C', 'x', 'G', 'x', 'x', 'x', 'T', 'x', 'x', 'x', 'x', 'x', 'x', 'N'];
var CIGAR_DECODER  = ['M', 'I', 'D', 'N', 'S', 'H', 'P', '=', 'X', '?', '?', '?', '?', '?', '?', '?'];

var readInt   = BAMUtil.readInt;
var readShort = BAMUtil.readShort;
var readFloat = BAMUtil.readFloat;

var counter = 0;

var Feature = Util.fastDeclare(
{
    constructor: function( args ) {
        this.store = args.store;
        this.file  = args.file;
        this.data  = {
            type: 'match',
            source: args.store.source
        };
        this.bytes = {
            start: args.bytes.start,
            end: args.bytes.end,
            byteArray: args.bytes.byteArray
        };

        this._coreParse();
    },

    get: function( field ) {
        field = field.toLowerCase();
        return field in this.data ? this.data[field] : // maybe already parsed
            function() {
                var v = this.data[field] = this[field] ? this[field]()                       : // maybe we have a special parser for it
                                           this._flagMasks[field] ? this._parseFlag( field ) : // or is it a flag?
                                           this._parseTag( field );                            // otherwise, must be a tag
                return v;
            }.call(this);
    },

    tags: function() {
        return this.get('_tags');
    },

    _tags: function() {
        this._parseAllTags();

        var tags = [ 'seq', 'seq_reverse_complemented', 'unmapped' ];
        if( ! this.get('unmapped') )
            tags.push( 'start', 'end', 'strand', 'score', 'qual', 'MQ', 'CIGAR', 'length_on_ref' );
        if( this.get('multi_segment_template') ) {
            tags.push( 'multi_segment_all_aligned',
                       'multi_segment_next_segment_unmapped',
                       'multi_segment_next_segment_reversed',
                       'multi_segment_first',
                       'multi_segment_last',
                       'secondary_alignment',
                       'qc_failed',
                       'duplicate',
                       'next_segment_position'
                     );
        }

        var d = this.data;
        for( var k in d ) {
            if( d.hasOwnProperty( k ) && k[0] != '_' )
                tags.push( k );
        }

        var seen = {};
        tags = array.filter( tags, function(t) {
            if( t in this.data && this.data[t] === undefined )
                return false;

            var lt = t.toLowerCase();
            var s = seen[lt];
            seen[lt] = true;
            return ! s;
        },this);

        return tags;
    },

    parent: function() {
        return undefined;
    },

    children: function() {
        return this.get('subfeatures');
    },

    id: function() {
        return this.get('name')+'/'+this.get('MD')+'/'+this.get('CIGAR')+'/'+this.get('start');
    },

    // special parsers
    /**
     * Mapping quality score.
     */
    mq: function() {
        var mq = (this.get('_bin_mq_nl') & 0xff00) >> 8;
        return mq == 255 ? undefined : mq;
    },
    score: function() {
        return this.get('MQ');
    },
    qual: function() {
        if( this.get('unmapped') )
            return undefined;

        var qseq = [];
        var byteArray = this.bytes.byteArray;
        var p = this.bytes.start + 36 + this.get('_l_read_name') + this.get('_n_cigar_op')*4 + this.get('_seq_bytes');
        var lseq = this.get('seq_length');
        for (var j = 0; j < lseq; ++j) {
            qseq.push( byteArray[p + j] );
        }
        return qseq.join(' ');
    },
    strand: function() {
        var xs = this.get('XS');
        return xs ? ( xs == '-' ? -1 : 1 ) :
               this.get('seq_reverse_complemented') ? -1 :  1;
    },
    /**
     * Length in characters of the read name.
     */
    _l_read_name: function() {
        return this.get('_bin_mq_nl') & 0xff;
    },
    /**
     * number of bytes in the sequence field
     */
    _seq_bytes: function() {
        return (this.get('seq_length') + 1) >> 1;
    },
    seq: function() {
        var seq = '';
        var byteArray = this.bytes.byteArray;
        var p = this.bytes.start + 36 + this.get('_l_read_name') + this.get('_n_cigar_op')*4;
        var seqBytes = this.get('_seq_bytes');
        for (var j = 0; j < seqBytes; ++j) {
            var sb = byteArray[p + j];
            seq += SEQRET_DECODER[(sb & 0xf0) >> 4];
            seq += SEQRET_DECODER[(sb & 0x0f)];
        }
        return seq;
    },
    name: function() {
        return this.get('read_name');
    },
    read_name: function() {
        var byteArray = this.bytes.byteArray;
        var readName = '';
        var nl = this.get('_l_read_name');
        var p = this.bytes.start + 36;
        for (var j = 0; j < nl-1; ++j) {
            readName += String.fromCharCode(byteArray[p+j]);
        }
        return readName;
    },
    _n_cigar_op: function() {
        return this.get('_flag_nc') & 0xffff;
    },
    cigar: function() {
        if( this.get('unmapped') )
            return undefined;

        var byteArray   = this.bytes.byteArray;
        var numCigarOps = this.get('_n_cigar_op');
        var p = this.bytes.start + 36 + this.get('_l_read_name');
        var cigar = '';
        var lref = 0;
        for (var c = 0; c < numCigarOps; ++c) {
            var cigop = readInt(byteArray, p);
            var lop = cigop >> 4;
            var op = CIGAR_DECODER[cigop & 0xf];
            cigar = cigar + lop + op;
            p += 4;
        }
        return cigar;
    },
    next_segment_position: function() {
        var nextRefID = this.get('_next_refID');
        var nextSegment = this.file.indexToChr[nextRefID];
        if( nextSegment )
            return nextSegment.name+':'+this.get('_next_pos');
        else
            return undefined;
    },
    subfeatures: function() {
        if( ! this.store.createSubfeatures )
            return undefined;

        var cigar = this.get('CIGAR');
        if( cigar )
            return this._cigarToSubfeats( cigar );

        return undefined;
    },
    length_on_ref: function() {
        if( this.get('unmapped') )
            return undefined;

        var numbers = this.get('CIGAR').match(/\d+(?=[MDN=X])/ig);
        var sum = 0;
        for( var i = 0; i<numbers.length; i++ )
            sum += parseInt( numbers[i] );
        return sum;
    },
    _flags: function() {
        return (this.data._flag_nc & 0xffff0000) >> 16;
    },
    end: function() {
        return this.get('start') + ( this.get('length_on_ref') || this.get('seq_length') || undefined );
    },

    seq_id: function() {
        if( this.get('unmapped') )
            return undefined;

        return ( this.file.indexToChr[ this._refID ] || {} ).name;
    },

    /**
     * parse the core data: start, end, strand, etc
     */
    _coreParse: function() {
        var byteArray = this.bytes.byteArray;
        var blockStart = this.bytes.start;

        var tempBytes = new Uint8Array( 36 );
        for( var i = 0; i<36; i++ ) {
            tempBytes[i] = byteArray[i+blockStart];
        }
        var ints = new Int32Array( tempBytes.buffer );

        this._refID = ints[1];
        var start = this.data.start = ints[2];
        this.data._bin_mq_nl = ints[3];
        this.data._flag_nc   = ints[4];
        this.data.seq_length = ints[5];
        this.data._next_refID = ints[6];
        this.data._next_pos   = ints[7];
        this.data.template_length = ints[8];
    },

    /**
     * Get the value of a tag, parsing the tags as far as necessary.
     * Only called if we have not already parsed that field.
     */
    _parseTag: function( tagName ) {
        var byteArray = this.bytes.byteArray;
        var p = this.bytes.start + 36 + this.get('_l_read_name') + this.get('_n_cigar_op')*4 + this.get('_seq_bytes') + this.get('seq_length');
        var blockEnd = this.bytes.end;
        while( p < blockEnd && !(tag == tagName)) { // really should be blockEnd - 3, but this works too and is faster
            var tag = String.fromCharCode( byteArray[p] ) + String.fromCharCode(byteArray[p + 1] );
            var origType = String.fromCharCode( byteArray[p + 2] );
            var type = origType.toLowerCase();
            p += 3;

            var value;
            if (type == 'a') {
                value = String.fromCharCode( byteArray[p] );
                p += 1;
            } else if (type == 'i' ) {
                value = readInt(byteArray, p );
                p += 4;
            } else if (type == 'c' ) {
                value = byteArray[p];
                p += 1;
            } else if (type == 's' ) {
                value = readShort(byteArray, p);
                p += 2;
            } else if (type == 'f') {
                value = readFloat( byteArray, p );
                p += 4;
            } else if ( type == 'z' || type == 'h' ) {
                value = '';
                while( p <= blockEnd ) {
                    var cc = byteArray[p++];
                    if( cc == 0 ) {
                        break;
                    }
                    else {
                        value += String.fromCharCode(cc);
                    }
                }
            } else {
                console.warn( "Unknown BAM tag type '"+origType
                              +'\', tags for '+(readName||'(unnamed read)')
                              +' may be incomplete'
                            );
                value = undefined;
                p = blockEnd+1; // stop parsing tags
            }
            if( tag == tagName )
                return value;
            else
                this.data[tag] = value;
        }
        return undefined;
    },
    _parseAllTags: function() {
        if( ! this._allTagsParsed )
            this._parseTag(); // calling _parseTag with no arg just parses
                              // all the tags and returns the last one

        this._allTagsParsed = true;
    },

    _flagMasks: {
        multi_segment_template:              0x1,
        multi_segment_all_aligned:           0x2,
        unmapped:                            0x4,
        multi_segment_next_segment_unmapped: 0x8,
        seq_reverse_complemented:            0x10,
        multi_segment_next_segment_reversed: 0x20,
        multi_segment_first:                 0x40,
        multi_segment_last:                  0x80,
        secondary_alignment:                 0x100,
        qc_failed:                           0x200,
        duplicate:                           0x400
    },

    _parseFlag: function( flagName ) {
        return !!( this.get('_flags') & this._flagMasks[flagName] );
    },

    _parseCigar: function( cigar ) {
        return array.map( cigar.match(/\d+\D/g), function( op ) {
           return [ op.match(/\D/)[0].toUpperCase(), parseInt( op ) ];
        });
    },

    /**
     *  take a cigar string, and initial position, return an array of subfeatures
     */
    _cigarToSubfeats: function(cigar)    {
        var subfeats = [];
        var min = this.get('start');
        var max;
        var ops = this._parseCigar( cigar );
        for (var i = 0; i < ops.length; i++)  {
            var lop = ops[i][1];
            var op = ops[i][0];  // operation type
            // converting "=" to "E" to avoid possible problems later with non-alphanumeric type name
            if (op === "=")  { op = "E"; }

            switch (op) {
            case 'M':
            case 'D':
            case 'N':
            case 'E':
            case 'X':
                max = min + lop;
                break;
            case 'I':
                max = min;
                break;
            case 'P':  // not showing padding deletions (possibly change this later -- could treat same as 'I' ?? )
            case 'H':  // not showing hard clipping (since it's unaligned, and offset arg meant to be beginning of aligned part)
            case 'S':  // not showing soft clipping (since it's unaligned, and offset arg meant to be beginning of aligned part)
                break;
                // other possible cases
            }
            if( op !== 'N' ) {
                var subfeat = new SimpleFeature({
                    data: {
                    type: op,
                        start: min,
                        end: max,
                        strand: this.get('strand'),
                        cigar_op: lop+op
                    },
                    parent: this
                });
                subfeats.push(subfeat);
            }
            min = max;
        }
        return subfeats;
    }

});

return Feature;
});<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2017 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.
 */

foam.CLASS({
  package: 'foam.dao',
  name: 'ManyToManyRelationshipDAO',
  extends: 'foam.dao.RelationshipDAO',

  implements: [ 'foam.mlang.Expressions' ],

  documentation: 'Adapts a DAO based on a *:* Relationship.',

  properties: [
    'junctionProperty',
    'joinDAOKey',
    'targetProperty',
    'junctionCls',
    {
      name: 'joinDAO',
      getter: function() {
        return this.__context__[this.joinDAOKey];
      }
    },
    {
      name: 'predicate',
      documentation: `ManyToMany filtered querys are always "backward" to
        match inverse-name property and source object's id.`,
      getter: function() {
        return this.EQ(
            this.junctionCls[
              foam.String.constantize(this.relationship.inverseName)],
            this.obj.id);
      }
    }
  ],

  methods: [
    function put(obj) {
      var self = this;
      return self.__context__[foam.String.daoize(self.of.name)].put(obj)
          .then(function(obj) {
            return self.delegate.put(
                self.relationship.adaptTarget(self.obj, obj));
          });
    },
    function find(key) {
      var id = this.of.isInstance(key) ? key.id : key;
      var dao = this.joinDAO;

      // Find junction record associated with this source-id and
      // target-id = "id". Doing this rather than calling select() directly
      // avoids need to accumulate all "junctionProperty" values associated
      // with this source-id and pass them to a joinDAO query.
      return this.selectFromJunction(
          this.COUNT(), undefined, 1, undefined,
          this.EQ(this.junctionProperty, id))
              .then(function(count) {
                // Return joinDAO's .find() value
                return count.value > 0 ? dao.find(id) : null;
              });
    },
    function select(sink, skip, limit, order, predicate) {
      var self = this;

      return self.selectFromJunction(self.MAP(self.junctionProperty))
          .then(function(map) {
            return self.joinDAO.select(sink, skip, limit, order, self.AND(
                predicate || self.TRUE,
                self.IN(self.targetProperty, map.delegate.a)));
          });
    },
    function selectFromJunction(sink, skip, limit, order, predicate) {
      return this.delegate.select(
        sink, skip, limit, order,
        predicate ?
          this.And.create({ args: [this.predicate, predicate] }) :
          this.predicate);
    }
  ]
});
<commit_msg>ManyToManyRelationshipDAO.find(): Simplify logic and de-duplicate FilteredDAO.select()<commit_after>/**
 * @license
 * Copyright 2017 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.
 */

foam.CLASS({
  package: 'foam.dao',
  name: 'ManyToManyRelationshipDAO',
  extends: 'foam.dao.RelationshipDAO',

  implements: [ 'foam.mlang.Expressions' ],

  documentation: 'Adapts a DAO based on a *:* Relationship.',

  properties: [
    'junctionProperty',
    'joinDAOKey',
    'targetProperty',
    'junctionCls',
    {
      name: 'joinDAO',
      getter: function() {
        return this.__context__[this.joinDAOKey];
      }
    },
    {
      name: 'predicate',
      documentation: `ManyToMany filtered querys are always "backward" to
        match inverse-name property and source object's id.`,
      getter: function() {
        return this.EQ(
            this.junctionCls[
              foam.String.constantize(this.relationship.inverseName)],
            this.obj.id);
      }
    }
  ],

  methods: [
    function put(obj) {
      var self = this;
      return self.__context__[foam.String.daoize(self.of.name)].put(obj)
          .then(function(obj) {
            return self.delegate.put(
                self.relationship.adaptTarget(self.obj, obj));
          });
    },
    function find(key) {
      var id = this.of.isInstance(key) ? key.id : key;
      var dao = this.joinDAO;
      return this.delegate.find([this.obj.id, id]).then(function(obj) {
        return obj !== null ? dao.find(id) : null;
      });
    },
    function select(sink, skip, limit, order, predicate) {
      var self = this;

      return self.SUPER(self.MAP(self.junctionProperty))
          .then(function(map) {
            return self.joinDAO.select(sink, skip, limit, order, self.AND(
                predicate || self.TRUE,
                self.IN(self.targetProperty, map.delegate.a)));
          });
    }
  ]
});
<|endoftext|>


<commit_before>/**
 * @constructor
 */
webfont.FontWatcher = function(domHelper, eventDispatcher, fontSizer,
    asyncCall, getTime) {
  this.domHelper_ = domHelper;
  this.eventDispatcher_ = eventDispatcher;
  this.fontSizer_ = fontSizer;
  this.asyncCall_ = asyncCall;
  this.getTime_ = getTime;
  this.currentlyWatched_ = 0;
  this.last_ = false;
  this.success_ = false;
  this.nameHelper_ = new webfont.CssFontFamilyName();
  this.fvd_ = new webfont.FontVariationDescription();
};

/**
 * @type {string}
 * @const
 */
webfont.FontWatcher.DEFAULT_FONTS_A = 'arial,sans-serif';

/**
 * @type {string}
 * @const
 */
webfont.FontWatcher.DEFAULT_FONTS_B = 'Georgia,"Century Schoolbook L",serif';

/**
 * @type {string}
 * @const
 */
webfont.FontWatcher.DEFAULT_VARIATION = 'n4';

/**
 * Watches a set of font families.
 * @param {Array.<string>} fontFamilies The font family names to watch.
 * @param {Object.<string, Array.<string>>} fontDescriptions The font variations
 *     of each family to watch. Described with FVD.
 * @param {Object.<string, string>} fontTestStrings The font test strings for
 *     each family.
 * @param {boolean} last True if this is the last set of families to watch.
 */
webfont.FontWatcher.prototype.watch = function(fontFamilies, fontDescriptions,
    fontTestStrings, last) {
  var length = fontFamilies.length;

  for (var i = 0; i < length; i++) {
    var fontFamily = fontFamilies[i];
    if (!fontDescriptions[fontFamily]) {
      fontDescriptions[fontFamily] = [webfont.FontWatcher.DEFAULT_VARIATION];
    }
    this.currentlyWatched_ += fontDescriptions[fontFamily].length;
  }

  if (last) {
    this.last_ = last;
  }

  for (var i = 0; i < length; i++) {
    var fontFamily = fontFamilies[i];
    var descriptions = fontDescriptions[fontFamily];
    var fontTestString  = fontTestStrings[fontFamily] || 'Mm';

    for (var j = 0, len = descriptions.length; j < len; j++) {
      var fontDescription = descriptions[j];

      var defaultFontsA = webfont.FontWatcher.DEFAULT_FONTS_A;
      var originalSizeA = this.getDefaultFontSize_(defaultFontsA,
          fontDescription, fontTestString);

      var defaultFontsB = webfont.FontWatcher.DEFAULT_FONTS_B;
      var originalSizeB = this.getDefaultFontSize_(defaultFontsB,
          fontDescription, fontTestString);;

      this.watch_(fontFamily, fontDescription, fontTestString,
          originalSizeA, defaultFontsA, originalSizeB, defaultFontsB);
    }
  }
};

/**
 * @private
 */
webfont.FontWatcher.prototype.watch_ = function(fontFamily, fontDescription,
    fontTestString, originalSizeA, defaultFontsA, originalSizeB, defaultFontsB) {
  this.eventDispatcher_.dispatchFontLoading(fontFamily, fontDescription);
  var requestedFontA = this.createHiddenElementWithFont_(this.nameHelper_.quote(fontFamily),
      defaultFontsA, fontDescription, fontTestString);
  var sizeA = this.fontSizer_.getWidth(requestedFontA);

  var requestedFontB = this.createHiddenElementWithFont_(this.nameHelper_.quote(fontFamily),
      defaultFontsB, fontDescription, fontTestString);
  var sizeB = this.fontSizer_.getWidth(requestedFontB);

  if (originalSizeA != sizeA || originalSizeB != sizeB) {
    this.domHelper_.removeElement(requestedFontA);
    this.domHelper_.removeElement(requestedFontB);
    this.eventDispatcher_.dispatchFontActive(fontFamily, fontDescription);
    this.success_ = true;
    this.decreaseCurrentlyWatched_();
  } else {
    this.asyncCheck_(this.getTime_(), fontFamily, fontDescription,
        originalSizeA, requestedFontA, originalSizeB, requestedFontB);
  }
};

/**
 * @private
 */
webfont.FontWatcher.prototype.decreaseCurrentlyWatched_ = function() {
  if (--this.currentlyWatched_ == 0 && this.last_) {
    if (this.success_) {
      this.eventDispatcher_.dispatchActive();
    } else {
      this.eventDispatcher_.dispatchInactive();
    }
  }
};

/**
 * @private
 */
webfont.FontWatcher.prototype.check_ = function(started, fontFamily,
    fontDescription, originalSizeA, requestedFontA, originalSizeB, requestedFontB) {
  var sizeA = this.fontSizer_.getWidth(requestedFontA);
  var sizeB = this.fontSizer_.getWidth(requestedFontB);

  if (originalSizeA != sizeA || originalSizeB != sizeB) {
    this.domHelper_.removeElement(requestedFontA);
    this.domHelper_.removeElement(requestedFontB);
    this.eventDispatcher_.dispatchFontActive(fontFamily, fontDescription);
    this.success_ = true;
    this.decreaseCurrentlyWatched_();
  } else if ((this.getTime_() - started) < 5000) {
    this.asyncCheck_(started, fontFamily, fontDescription, originalSizeA,
        requestedFontA, originalSizeB, requestedFontB);
  } else {
    this.domHelper_.removeElement(requestedFontA);
    this.domHelper_.removeElement(requestedFontB);
    this.eventDispatcher_.dispatchFontInactive(fontFamily, fontDescription);
    this.decreaseCurrentlyWatched_();
  }
};

/**
 * @private
 */
webfont.FontWatcher.prototype.asyncCheck_ = function(started, fontFamily,
    fontDescription, originalSizeA, requestedFontA, originalSizeB, requestedFontB) {
  this.asyncCall_(function(context, func) {
    return function() {
      func.call(context, started, fontFamily, fontDescription, originalSizeA,
          requestedFontA, originalSizeB, requestedFontB);
    }
  }(this, this.check_), 50);
};

/**
 * @private
 */
webfont.FontWatcher.prototype.getDefaultFontSize_ = function(defaultFonts,
    fontDescription, fontTestString) {
  var defaultFont = this.createHiddenElementWithFont_(
      null, defaultFonts, fontDescription, fontTestString);
  var size = this.fontSizer_.getWidth(defaultFont);

  this.domHelper_.removeElement(defaultFont);
  return size;
};

/**
 * @private
 */
webfont.FontWatcher.prototype.createHiddenElementWithFont_ = function(
    fontFamily, defaultFonts, fontDescription, fontTestString) {
  fontFamily = fontFamily ? fontFamily + "," : "";
  var variationCss = this.fvd_.expand(fontDescription);
  var styleString = "position:absolute;top:-999px;font-size:300px;font-family:" +
      fontFamily + defaultFonts + ";" + variationCss;
  var span = this.domHelper_.createElement('span', { 'style': styleString },
      fontTestString);

  this.domHelper_.insertInto('body', span);
  return span;
};
<commit_msg>Removed an extra semicolon.<commit_after>/**
 * @constructor
 */
webfont.FontWatcher = function(domHelper, eventDispatcher, fontSizer,
    asyncCall, getTime) {
  this.domHelper_ = domHelper;
  this.eventDispatcher_ = eventDispatcher;
  this.fontSizer_ = fontSizer;
  this.asyncCall_ = asyncCall;
  this.getTime_ = getTime;
  this.currentlyWatched_ = 0;
  this.last_ = false;
  this.success_ = false;
  this.nameHelper_ = new webfont.CssFontFamilyName();
  this.fvd_ = new webfont.FontVariationDescription();
};

/**
 * @type {string}
 * @const
 */
webfont.FontWatcher.DEFAULT_FONTS_A = 'arial,sans-serif';

/**
 * @type {string}
 * @const
 */
webfont.FontWatcher.DEFAULT_FONTS_B = 'Georgia,"Century Schoolbook L",serif';

/**
 * @type {string}
 * @const
 */
webfont.FontWatcher.DEFAULT_VARIATION = 'n4';

/**
 * Watches a set of font families.
 * @param {Array.<string>} fontFamilies The font family names to watch.
 * @param {Object.<string, Array.<string>>} fontDescriptions The font variations
 *     of each family to watch. Described with FVD.
 * @param {Object.<string, string>} fontTestStrings The font test strings for
 *     each family.
 * @param {boolean} last True if this is the last set of families to watch.
 */
webfont.FontWatcher.prototype.watch = function(fontFamilies, fontDescriptions,
    fontTestStrings, last) {
  var length = fontFamilies.length;

  for (var i = 0; i < length; i++) {
    var fontFamily = fontFamilies[i];
    if (!fontDescriptions[fontFamily]) {
      fontDescriptions[fontFamily] = [webfont.FontWatcher.DEFAULT_VARIATION];
    }
    this.currentlyWatched_ += fontDescriptions[fontFamily].length;
  }

  if (last) {
    this.last_ = last;
  }

  for (var i = 0; i < length; i++) {
    var fontFamily = fontFamilies[i];
    var descriptions = fontDescriptions[fontFamily];
    var fontTestString  = fontTestStrings[fontFamily] || 'Mm';

    for (var j = 0, len = descriptions.length; j < len; j++) {
      var fontDescription = descriptions[j];

      var defaultFontsA = webfont.FontWatcher.DEFAULT_FONTS_A;
      var originalSizeA = this.getDefaultFontSize_(defaultFontsA,
          fontDescription, fontTestString);

      var defaultFontsB = webfont.FontWatcher.DEFAULT_FONTS_B;
      var originalSizeB = this.getDefaultFontSize_(defaultFontsB,
          fontDescription, fontTestString);

      this.watch_(fontFamily, fontDescription, fontTestString,
          originalSizeA, defaultFontsA, originalSizeB, defaultFontsB);
    }
  }
};

/**
 * @private
 */
webfont.FontWatcher.prototype.watch_ = function(fontFamily, fontDescription,
    fontTestString, originalSizeA, defaultFontsA, originalSizeB, defaultFontsB) {
  this.eventDispatcher_.dispatchFontLoading(fontFamily, fontDescription);
  var requestedFontA = this.createHiddenElementWithFont_(this.nameHelper_.quote(fontFamily),
      defaultFontsA, fontDescription, fontTestString);
  var sizeA = this.fontSizer_.getWidth(requestedFontA);

  var requestedFontB = this.createHiddenElementWithFont_(this.nameHelper_.quote(fontFamily),
      defaultFontsB, fontDescription, fontTestString);
  var sizeB = this.fontSizer_.getWidth(requestedFontB);

  if (originalSizeA != sizeA || originalSizeB != sizeB) {
    this.domHelper_.removeElement(requestedFontA);
    this.domHelper_.removeElement(requestedFontB);
    this.eventDispatcher_.dispatchFontActive(fontFamily, fontDescription);
    this.success_ = true;
    this.decreaseCurrentlyWatched_();
  } else {
    this.asyncCheck_(this.getTime_(), fontFamily, fontDescription,
        originalSizeA, requestedFontA, originalSizeB, requestedFontB);
  }
};

/**
 * @private
 */
webfont.FontWatcher.prototype.decreaseCurrentlyWatched_ = function() {
  if (--this.currentlyWatched_ == 0 && this.last_) {
    if (this.success_) {
      this.eventDispatcher_.dispatchActive();
    } else {
      this.eventDispatcher_.dispatchInactive();
    }
  }
};

/**
 * @private
 */
webfont.FontWatcher.prototype.check_ = function(started, fontFamily,
    fontDescription, originalSizeA, requestedFontA, originalSizeB, requestedFontB) {
  var sizeA = this.fontSizer_.getWidth(requestedFontA);
  var sizeB = this.fontSizer_.getWidth(requestedFontB);

  if (originalSizeA != sizeA || originalSizeB != sizeB) {
    this.domHelper_.removeElement(requestedFontA);
    this.domHelper_.removeElement(requestedFontB);
    this.eventDispatcher_.dispatchFontActive(fontFamily, fontDescription);
    this.success_ = true;
    this.decreaseCurrentlyWatched_();
  } else if ((this.getTime_() - started) < 5000) {
    this.asyncCheck_(started, fontFamily, fontDescription, originalSizeA,
        requestedFontA, originalSizeB, requestedFontB);
  } else {
    this.domHelper_.removeElement(requestedFontA);
    this.domHelper_.removeElement(requestedFontB);
    this.eventDispatcher_.dispatchFontInactive(fontFamily, fontDescription);
    this.decreaseCurrentlyWatched_();
  }
};

/**
 * @private
 */
webfont.FontWatcher.prototype.asyncCheck_ = function(started, fontFamily,
    fontDescription, originalSizeA, requestedFontA, originalSizeB, requestedFontB) {
  this.asyncCall_(function(context, func) {
    return function() {
      func.call(context, started, fontFamily, fontDescription, originalSizeA,
          requestedFontA, originalSizeB, requestedFontB);
    }
  }(this, this.check_), 50);
};

/**
 * @private
 */
webfont.FontWatcher.prototype.getDefaultFontSize_ = function(defaultFonts,
    fontDescription, fontTestString) {
  var defaultFont = this.createHiddenElementWithFont_(
      null, defaultFonts, fontDescription, fontTestString);
  var size = this.fontSizer_.getWidth(defaultFont);

  this.domHelper_.removeElement(defaultFont);
  return size;
};

/**
 * @private
 */
webfont.FontWatcher.prototype.createHiddenElementWithFont_ = function(
    fontFamily, defaultFonts, fontDescription, fontTestString) {
  fontFamily = fontFamily ? fontFamily + "," : "";
  var variationCss = this.fvd_.expand(fontDescription);
  var styleString = "position:absolute;top:-999px;font-size:300px;font-family:" +
      fontFamily + defaultFonts + ";" + variationCss;
  var span = this.domHelper_.createElement('span', { 'style': styleString },
      fontTestString);

  this.domHelper_.insertInto('body', span);
  return span;
};
<|endoftext|>


<commit_before>var eventbus;
var tableResponsiveComponent;
var tableComponent;
var tableFooterComponent;
var component;
var eventbus;
var sub;

var App = (function () {
    function App() {

    }

    App.prototype.singleComponent = function () {
        eventbus = new Eventbus();

        var pub = new Publisher({
            event: 'event1',
            state: [
                {name: 'Sumer'},
                {name: 'Hitit'},
                {name: 'Hatti'},
                {name: 'Hurri'},
                {name: 'İskit'}
            ]
        });

        eventbus.publisher().register(pub);

        var component = new Component({
            name: 'myComponent',
            event: 'event1',
            render: function (state) {
                state = state.event1;

                var ce = Component.createElement;

                var ul = ce("ul", {class: "list-group"});

                for (var i in state) {
                    if (state.hasOwnProperty(i)) {
                        var li = Component.createElement("li", {class: "list-group-item", text: state[i].name});
                        ul.append(li);
                    }
                }

                return ul;
            },
            methods: { // or Component.createElement 's on function
                'li': { // all selectors; (.), (#), (tag name)
                    click: function (e) {
                        console.log(this.targetElement);
                    }
                },
                '#myLi': {
                    mouseenter: function (e) {
                        console.log("mouse enter");
                    }
                }
            }
        });

        //Routing
        var anatolia = new Anatolia({
            name: 'AnatoliaApp',
            eventbus: eventbus,
            components: {
                '#component_1': component
            }
        });

        anatolia.render();

        // anatolia.listen();
    };

    App.prototype.parentChildDataTransfer = function () {
        eventbus = new Eventbus();

        var pub = new Publisher({
            event: "e1",
            state: {text: "Hello Anatolia!"}
        });

        eventbus.publisher().register(pub);

        var myParagraph = new Component({
            name: "myParagraph",
            render: function (state) {
                var myParagraph = document.createElement("p");
                var myParagraphText = document.createTextNode(state.text);

                myParagraph.append(myParagraphText);

                return myParagraph;
            }
        });

        var component = new Component({
            name: "myDiv",
            event: "e1",
            render: function (state) {
                var myDiv = document.createElement("div");

                var myP = myParagraph.setState(state.e1).render();

                myDiv.append(myP);

                return myDiv;
            }
        });

        //Routing
        var anatolia = new Anatolia({
            name: 'AnatoliaApp',
            eventbus: eventbus,
            components: {
                '#component_1': component
            }
        });

        anatolia.render();

    };

    App.prototype.nestedComponents = function () {
        eventbus = new Eventbus();

        var pub = new Publisher({
            event: 'event1',
            state: [
                {order: 1, name: 'Sumer'},
                {order: 2, name: 'Hitit'},
                {order: 3, name: 'Hatti'},
                {order: 4, name: 'Hurri'},
                {order: 5, name: 'İskit'}
            ]
        });

        var stylePub = new Publisher({
            event: 'styleEvent',
            state: {
                class: "table table-condensed table-striped table-hover",
                header: [
                    {text: "Order"},
                    {text: "Name"}
                ],
                propA: "my",
                propB: "Class"
            }
        });

        var tableResponsivePub = new Publisher({
            event: 'tableResponsive',
            state: {
                class: "table-responsive"
            }
        });

        eventbus.publisher().register(pub, stylePub, tableResponsivePub);

        tableResponsiveComponent = new Component({
            name: "tableResponsive",
            render: function (state) {
                var responsiveContainer = document.createElement("template");
                responsiveContainer.innerHTML = "

";

                return responsiveContainer.content.firstChild;
            }
        }).setEventbus(eventbus).setEvent('tableResponsive');

        tableComponent = new Component({
            name: 'table',
            render: function (state) {
                var styles = state.styleEvent;
                var data = state.event1;

                var $$ = Component.createElement;

                var table = $$("table", {class: styles.class});
                var thead = $$("thead");
                var tbody = $$("tbody");
                var theadRow = $$("tr");

                thead.append(theadRow);
                table.append(thead);
                table.append(tbody);

                for (var i in styles.header) {
                    if (styles.header.hasOwnProperty(i)) {
                        var headerCell = $$("th", {text: styles.header[i].text});
                        theadRow.append(headerCell);
                    }
                }

                // console.log(styles.header);

                for (var i in data) {
                    if (data.hasOwnProperty(i)) {
                        var tr = $$("tr");

                        var orderCell = $$("td", {text: data[i].order});
                        var nameCell = $$("td", {text: data[i].name});

                        tr.append(orderCell, nameCell);
                        table.children[1].append(tr);
                    }
                }

                return table;
            }
        });

        tableFooterComponent = new Component({
            name: "tfoot",
            render: function () {
                var el = Component.createElementFromObject({
                    tagName: "tfoot",
                    class: "tfoot",
                    id: "tfoot",
                    myAttribute: "tfoot",
                    child: [
                        {
                            tagName: "tr",
                            child: [
                                {
                                    tagName: "td",
                                    text: "Hello"
                                },
                                {
                                    tagName: "td",
                                    text: "World"
                                }
                            ]
                        }
                    ]
                });

                return el;
            },
            methods: {
                'td': {
                    click: function (e) {
                        console.log(this);
                    }
                }
            }
        });

        component = new Component({
            name: 'myComponent',
            render: function () {
                var tableResponsiveContainer = tableResponsiveComponent.render(this);
                var table = tableComponent.setEventbus(eventbus).setEvent(["styleEvent", "event1"]).render(this);
                var tableFooter = tableFooterComponent.render(this);

                table.append(tableFooter);
                tableResponsiveContainer.append(table);

                return tableResponsiveContainer;
            },
            methods: { // or Component.createElement 's on function
                // 'tr': { // all selectors; (.), (#), (tag name)
                //     click: function (e) {
                //         console.log(this.targetElement);
                //     }
                // }
            }
        });

        //Routing
        var anatolia = new Anatolia({
            name: 'AnatoliaApp',
            eventbus: eventbus,
            components: {
                '#component_1': component
            }
        });

        anatolia.render();
    };

    return App;
})();<commit_msg>single component, nested components, parent - child data transfer<commit_after>var eventbus;
var tableResponsiveComponent;
var tableComponent;
var tableFooterComponent;
var component;
var eventbus;
var sub;

var App = (function () {
    function App() {

    }

    App.prototype.singleComponent = function () {
        eventbus = new Eventbus();

        var pub = new Publisher({
            event: 'event1',
            state: [
                {name: 'Sumer'},
                {name: 'Hitit'},
                {name: 'Hatti'},
                {name: 'Hurri'},
                {name: 'İskit'}
            ]
        });

        eventbus.publisher().register(pub);

        var component = new Component({
            name: 'myComponent',
            event: 'event1',
            render: function (state) {
                state = state.event1;

                var ce = Component.createElement;

                var ul = ce("ul", {class: "list-group"});

                for (var i in state) {
                    if (state.hasOwnProperty(i)) {
                        var li = Component.createElement("li", {class: "list-group-item", text: state[i].name});
                        ul.append(li);
                    }
                }

                return ul;
            },
            methods: { // or Component.createElement 's on function
                'li': { // all selectors; (.), (#), (tag name)
                    click: function (e) {
                        console.log(this.targetElement);
                    }
                },
                '#myLi': {
                    mouseenter: function (e) {
                        console.log("mouse enter");
                    }
                }
            }
        });

        //Routing
        var anatolia = new Anatolia({
            name: 'AnatoliaApp',
            eventbus: eventbus,
            components: {
                '#component_1': component
            }
        });

        anatolia.render();

        // anatolia.listen();
    };

    App.prototype.parentChildDataTransfer = function () {
        eventbus = new Eventbus();

        var pub = new Publisher({
            event: "e1",
            state: {text: "Hello Anatolia!"}
        });

        eventbus.publisher().register(pub);

        var myParagraph = new Component({
            name: "myParagraph",
            render: function (state) {
                var myParagraph = document.createElement("p");
                var myParagraphText = document.createTextNode(state.text);

                myParagraph.append(myParagraphText);

                return myParagraph;
            }
        });

        var component = new Component({
            name: "myDiv",
            event: "e1",
            render: function (state) {
                var myDiv = document.createElement("div");

                var myP = myParagraph.setState(state.e1).render();

                myDiv.append(myP);

                return myDiv;
            }
        });

        //Routing
        var anatolia = new Anatolia({
            name: 'AnatoliaApp',
            eventbus: eventbus,
            components: {
                '#component_1': component
            }
        });

        anatolia.render();

    };

    App.prototype.nestedComponentsWithPublishers = function () {
        eventbus = new Eventbus();

        var pub = new Publisher({
            event: 'event1',
            state: [
                {order: 1, name: 'Sumer'},
                {order: 2, name: 'Hitit'},
                {order: 3, name: 'Hatti'},
                {order: 4, name: 'Hurri'},
                {order: 5, name: 'İskit'}
            ]
        });

        var stylePub = new Publisher({
            event: 'styleEvent',
            state: {
                class: "table table-condensed table-striped table-hover",
                header: [
                    {text: "Order"},
                    {text: "Name"}
                ],
                propA: "my",
                propB: "Class"
            }
        });

        var tableResponsivePub = new Publisher({
            event: 'tableResponsive',
            state: {
                class: "table-responsive"
            }
        });

        eventbus.publisher().register(pub, stylePub, tableResponsivePub);

        tableResponsiveComponent = new Component({
            name: "tableResponsive",
            render: function (state) {
                var responsiveContainer = document.createElement("template");
                responsiveContainer.innerHTML = "

";

                return responsiveContainer.content.firstChild;
            }
        }).setEventbus(eventbus).setEvent('tableResponsive');

        tableComponent = new Component({
            name: 'table',
            render: function (state) {
                var styles = state.styleEvent;
                var data = state.event1;

                var $$ = Component.createElement;

                var table = $$("table", {class: styles.class});
                var thead = $$("thead");
                var tbody = $$("tbody");
                var theadRow = $$("tr");

                thead.append(theadRow);
                table.append(thead);
                table.append(tbody);

                for (var i in styles.header) {
                    if (styles.header.hasOwnProperty(i)) {
                        var headerCell = $$("th", {text: styles.header[i].text});
                        theadRow.append(headerCell);
                    }
                }

                // console.log(styles.header);

                for (var i in data) {
                    if (data.hasOwnProperty(i)) {
                        var tr = $$("tr");

                        var orderCell = $$("td", {text: data[i].order});
                        var nameCell = $$("td", {text: data[i].name});

                        tr.append(orderCell, nameCell);
                        table.children[1].append(tr);
                    }
                }

                return table;
            }
        });

        tableFooterComponent = new Component({
            name: "tfoot",
            render: function () {
                var el = Component.createElementFromObject({
                    tagName: "tfoot",
                    class: "tfoot",
                    id: "tfoot",
                    myAttribute: "tfoot",
                    child: [
                        {
                            tagName: "tr",
                            child: [
                                {
                                    tagName: "td",
                                    text: "Hello"
                                },
                                {
                                    tagName: "td",
                                    text: "World"
                                }
                            ]
                        }
                    ]
                });

                return el;
            },
            methods: {
                'td': {
                    click: function (e) {
                        console.log(this);
                    }
                }
            }
        });

        component = new Component({
            name: 'myComponent',
            render: function () {
                var tableResponsiveContainer = tableResponsiveComponent.render(this);
                var table = tableComponent.setEventbus(eventbus).setEvent(["styleEvent", "event1"]).render(this);
                var tableFooter = tableFooterComponent.render(this);

                table.append(tableFooter);
                tableResponsiveContainer.append(table);

                return tableResponsiveContainer;
            },
            methods: { // or Component.createElement 's on function
                // 'tr': { // all selectors; (.), (#), (tag name)
                //     click: function (e) {
                //         console.log(this.targetElement);
                //     }
                // }
            }
        });

        //Routing
        var anatolia = new Anatolia({
            name: 'AnatoliaApp',
            eventbus: eventbus,
            components: {
                '#component_1': component
            }
        });

        anatolia.render();
    };

    return App;
})();<|endoftext|>


<commit_before>import Ember from 'ember';
import config from './config/environment';

var Router = Ember.Router.extend({
  location: config.locationType
});

Router.map(function() {
  this.resource('plot-points', function(){
      this.route('add');
      this.resource('plot-point', { path: '/:id' }, function() {
          this.route('edit');
          this.resource('characters', function(){
            this.route('add');
          });
      });
  });

  this.resource("stories", function() {
    this.route("add");
    this.resource('story', { path: '/:id'}, function(){
      this.route('edit');
    });
  });

  this.resource("standard-archetypes", function() {
      this.route("add");
      this.resource('standard-archetype', { path: '/:id'}, function(){
          this.route('edit');
      });
	});
  this.resource("standard-beasts", function() {
      this.route("add");
      this.resource('standard-beast', { path: '/:id'}, function(){
          this.route('edit');
      });
	});
  this.resource("standard-characters", function() {
      this.route("add");
      this.resource('standard-character', { path: '/:id'}, function(){
          this.route('edit');
      });
	});
  this.resource("standard-edges", function() {
      this.route("add");
      this.resource('standard-edge', { path: '/:id'}, function(){
          this.route('edit');
      });
	});
  this.resource("standard-extras", function() {
      this.route("add");
      this.resource('standard-extra', { path: '/:id'}, function(){
          this.route('edit');
      });
	});
  this.resource("standard-gears", function() {
      this.route("add");
      this.resource('standard-gear', { path: '/:id'}, function(){
          this.route('edit');
      });
	});
  this.resource("standard-hindrances", function() {
      this.route("add");
      this.resource('standard-hindrance', { path: '/:id'}, function(){
          this.route('edit');
      });
	});
  this.resource("standard-places", function() {
      this.route("add");
      this.resource('standard-place', { path: '/:id'}, function(){
          this.route('edit');
      });
	});
  this.resource("standard-powers", function() {
      this.route("add");
      this.resource('standard-power', { path: '/:id'}, function(){
          this.route('edit');
      });
	});
  this.resource("standard-races", function() {
      this.route("add");
      this.resource('standard-race', { path: '/:id'}, function(){
          this.route('edit');
      });
	});
  this.resource("standard-skills", function() {
      this.route("add");
      this.resource('standard-skill', { path: '/:id'}, function(){
          this.route('edit');
      });
	});
});

export default Router;
<commit_msg>Needed this after all<commit_after>import Ember from 'ember';
import config from './config/environment';

var Router = Ember.Router.extend({
  location: config.locationType
});

Router.map(function() {
  this.resource('plot-points', function(){
      this.route('add');
      this.resource('plot-point', { path: '/:id' }, function() {
          this.route('edit');
          this.resource('characters', function(){
            this.route('add');
          });
      });
  });
  this.resource("characters", function() {
    this.resource('character', { path: '/:characterId'}, function(){
      this.route('edit');
    });
  });

  this.resource("stories", function() {
    this.route("add");
    this.resource('story', { path: '/:id'}, function(){
      this.route('edit');
    });
  });

  this.resource("standard-archetypes", function() {
      this.route("add");
      this.resource('standard-archetype', { path: '/:id'}, function(){
          this.route('edit');
      });
	});
  this.resource("standard-beasts", function() {
      this.route("add");
      this.resource('standard-beast', { path: '/:id'}, function(){
          this.route('edit');
      });
	});
  this.resource("standard-characters", function() {
      this.route("add");
      this.resource('standard-character', { path: '/:id'}, function(){
          this.route('edit');
      });
	});
  this.resource("standard-edges", function() {
      this.route("add");
      this.resource('standard-edge', { path: '/:id'}, function(){
          this.route('edit');
      });
	});
  this.resource("standard-extras", function() {
      this.route("add");
      this.resource('standard-extra', { path: '/:id'}, function(){
          this.route('edit');
      });
	});
  this.resource("standard-gears", function() {
      this.route("add");
      this.resource('standard-gear', { path: '/:id'}, function(){
          this.route('edit');
      });
	});
  this.resource("standard-hindrances", function() {
      this.route("add");
      this.resource('standard-hindrance', { path: '/:id'}, function(){
          this.route('edit');
      });
	});
  this.resource("standard-places", function() {
      this.route("add");
      this.resource('standard-place', { path: '/:id'}, function(){
          this.route('edit');
      });
	});
  this.resource("standard-powers", function() {
      this.route("add");
      this.resource('standard-power', { path: '/:id'}, function(){
          this.route('edit');
      });
	});
  this.resource("standard-races", function() {
      this.route("add");
      this.resource('standard-race', { path: '/:id'}, function(){
          this.route('edit');
      });
	});
  this.resource("standard-skills", function() {
      this.route("add");
      this.resource('standard-skill', { path: '/:id'}, function(){
          this.route('edit');
      });
	});
});

export default Router;
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2020 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.medusa',
  name: 'MedusaAdapterDAO',
  extends: 'foam.dao.ProxyDAO',

  documentation: `Entry point into the Medusa system.
This DAO intercepts MDAO 'put' operations and creates a medusa entry for argument model.
It then marshalls it to the primary mediator, and waits on a response.`,

  javaImports: [
    'foam.core.FObject',
    'foam.core.X',
    'foam.dao.ArraySink',
    'foam.dao.DAO',
    'foam.dao.DOP',
    'foam.dao.MDAO',
    'foam.dao.RemoveSink',
    'foam.lib.formatter.FObjectFormatter',
    'foam.lib.formatter.JSONFObjectFormatter',
    'static foam.mlang.MLang.AND',
    'static foam.mlang.MLang.EQ',
    'foam.log.LogLevel',
    'foam.nanos.alarming.Alarm',
    'foam.nanos.logger.PrefixLogger',
    'foam.nanos.logger.Logger',
    'foam.nanos.pm.PM',
    'java.util.HashMap',
    'java.util.Map',
    'java.util.List',
    'foam.util.SafetyUtil',
    'java.util.concurrent.ConcurrentLinkedDeque',
  ],

  classes: [
    {
      name: 'WriteLock',
      properties: [
        {
          name: 'complete',
          class: 'Boolean'
        }
      ]
    }
  ],

  properties: [
    {
      name: 'nSpec',
      class: 'FObjectProperty',
      of: 'foam.nanos.boot.NSpec'
    },
    {
      name: 'medusaEntryDAO',
      class: 'FObjectProperty',
      of: 'foam.dao.DAO',
      javaFactory: 'return (foam.dao.DAO) getX().get("localMedusaEntryDAO");'
    },
    {
      name: 'clientDAO',
      class: 'FObjectProperty',
      of: 'foam.dao.DAO',
      javaFactory: `
      return new foam.nanos.medusa.ClusterClientDAO.Builder(getX())
        .setNSpec(getNSpec())
        .setDelegate(new foam.dao.NullDAO(getX(), getDelegate().getOf()))
        .build();
      `
    },
    {
      name: 'logger',
      class: 'FObjectProperty',
      of: 'foam.nanos.logger.Logger',
      visibility: 'HIDDEN',
      javaFactory: `
        return new PrefixLogger(new Object[] {
          this.getClass().getSimpleName(),
          getNSpec().getName()
        }, (Logger) getX().get("logger"));
      `
    }
  ],

  axioms: [
    {
      name: 'javaExtras',
      buildJavaClass: function(cls) {
        cls.extras.push(foam.java.Code.create({
          data: `
  protected static final ThreadLocal<FObjectFormatter> formatter_ = new ThreadLocal<FObjectFormatter>() {
    @Override
    protected JSONFObjectFormatter initialValue() {
      JSONFObjectFormatter formatter = new JSONFObjectFormatter();
      formatter.setQuoteKeys(false); // default
      formatter.setOutputShortNames(true); // default
      formatter.setOutputDefaultValues(true);
      formatter.setOutputClassNames(true); // default
      formatter.setOutputDefaultClassNames(true); // default
      formatter.setOutputReadableDates(false);
      formatter.setPropertyPredicate(new foam.lib.StoragePropertyPredicate());
      return formatter;
    }

    @Override
    public FObjectFormatter get() {
      FObjectFormatter formatter = super.get();
      formatter.reset();
      return formatter;
    }
  };

  // TODO: pool of reusable WriteLocks
  protected ConcurrentLinkedDeque writeLocks_ = new ConcurrentLinkedDeque();
          `
        }));
      }
    }
  ],

  methods: [
    {
      name: 'waitOnWrite',
      args: [
        {
          name: 'x',
          type: 'Context'
        }
      ],
      javaCode: `
      WriteLock lock = (WriteLock) writeLocks_.peekLast();
      if ( lock != null ) {
        synchronized ( lock ) {
          while ( ! lock.getComplete() &&
                  writeLocks_.contains( lock ) ) {
            try {
              // getLogger().debug("waitOnWrite_", "wait");
              lock.wait(1000L);
              // getLogger().debug("waitOnWrite_", "unwait");
              // getLogger().debug("waitOnWrite_", "complete", lock.getComplete(), "contains", writeLocks_.contains( lock ), "size", writeLocks_.size());
            } catch (InterruptedException e) {
              break;
            }
          }
        }
      }
      `
    },
    {
      name: 'find_',
      javaCode: `
      // FIXME: this is causing threadwait on primary - related to registry
      // waitOnWrite(x);
      return getDelegate().find_(x, id);
      `
    },
    {
      name: 'select_',
      javaCode: `
      waitOnWrite(x);
      return getDelegate().select_(x, sink, skip, limit, order, predicate);
      `
    },
    {
      name: 'put_',
      javaCode: `
      return update(x, obj, DOP.PUT);
      `
    },
    {
      name: 'remove_',
      javaCode: `
      return update(x, obj, DOP.REMOVE);
      `
    },
    {
      name: 'removeAll_',
      javaCode: `
      getDelegate().select_(x, new RemoveSink(x, this), skip, limit, order, predicate);
      `
    },
    {
      documentation: 'Route DAO operation to primary mediator',
      name: 'update',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'obj',
          type: 'foam.core.FObject'
        },
        {
          name: 'dop',
          type: 'foam.dao.DOP'
        }
      ],
      javaType: 'foam.core.FObject',
      javaCode: `
      if ( ! ( DOP.PUT == dop ||
               DOP.REMOVE == dop ) ) {
        getLogger().warning("update", "Unsupported operation", dop.getLabel());
        throw new UnsupportedOperationException(dop.getLabel());
      }

      if ( obj instanceof Clusterable &&
           ! ((Clusterable) obj).getClusterable() ) {
        getLogger().debug("update", dop.getLabel(), "not clusterable", obj.getClass().getSimpleName(), obj.getProperty("id"));
        if ( DOP.PUT == dop ) return getDelegate().put_(x, obj);
        if ( DOP.REMOVE == dop ) return getDelegate().remove_(x, obj);
      }

      ClusterConfigSupport support = (ClusterConfigSupport) x.get("clusterConfigSupport");
      ClusterConfig config = support.getConfig(x, support.getConfigId());

      if ( config.getIsPrimary() ) {
        return updatePrimary(x, obj, dop);
      }
      return updateSecondary(x, obj, dop);
    `
    },
    {
      documentation: `Secondary flow: proxy to next server, wait on own mdao update.`,
      name: 'updateSecondary',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'obj',
          type: 'foam.core.FObject'
        },
        {
          name: 'dop',
          type: 'foam.dao.DOP'
        }
      ],
      javaType: 'foam.core.FObject',
      javaCode: `
      ClusterCommand cmd = new ClusterCommand(x, getNSpec().getName(), dop, obj);
      getLogger().debug("update", "secondary", dop, obj.getProperty("id"), "send");
      PM pm = PM.create(x, this.getClass().getSimpleName(), "secondary", "cmd");
      // PM pm = new PM(this.getClass().getSimpleName(), "secondary", "cmd");
      cmd = (ClusterCommand) getClientDAO().cmd_(x, cmd);
      pm.log(x);
      cmd.logHops(x);
      getLogger().debug("update", "secondary", dop, obj.getProperty("id"), "receive", cmd.getMedusaEntryId());
      if ( cmd.getMedusaEntryId() > 0 ) {
        pm = PM.create(x, this.getClass().getSimpleName(), "secondary", "wait");
        MedusaRegistry registry = (MedusaRegistry) x.get("medusaRegistry");
        registry.wait(x, (Long) cmd.getMedusaEntryId());
        pm.log(x);
      }
      FObject result = cmd.getData();
      if ( DOP.PUT == dop ) {
        if ( result != null ) {
          FObject nu = getDelegate().find_(x, result.getProperty("id"));
          if ( nu == null ) {
            getLogger().error("update", "secondary", dop, obj.getProperty("id"), "delegate", cmd.getMedusaEntryId(), "find", result.getProperty("id"), "null");
            Alarm alarm = new Alarm();
            alarm.setClusterable(false);
            alarm.setSeverity(LogLevel.ERROR);
            alarm.setName("MedusaAdapter secondary find failed");
            alarm.setNote(obj.getClass().getName()+" "+obj.getProperty("id"));
            alarm = (Alarm) ((DAO) x.get("alarmDAO")).put(alarm);
          }
          // put again to update storageTransient properties
          return getDelegate().put_(x, result);
        }
        // TODO/REVIEW
        getLogger().error("update", "secondary", dop, obj.getProperty("id"), "result,null");
        Alarm alarm = new Alarm();
        alarm.setClusterable(false);
        alarm.setSeverity(LogLevel.ERROR);
        alarm.setName("MedusaAdapter secondary cmd failed");
        alarm.setNote(obj.getClass().getName()+" "+obj.getProperty("id"));
        alarm = (Alarm) ((DAO) x.get("alarmDAO")).put(alarm);
      } else if ( DOP.REMOVE == dop ) {
        if ( getDelegate().find_(x, obj.getProperty("id")) != null ) {
          getLogger().error("update", "secondary", dop, obj.getProperty("id"), "delegate", "not deleted");
          Alarm alarm = new Alarm();
          alarm.setClusterable(false);
          alarm.setSeverity(LogLevel.ERROR);
          alarm.setName("MedusaAdapter secondary remove failed");
          alarm.setNote(obj.getClass().getName()+" "+obj.getProperty("id"));
          alarm = (Alarm) ((DAO) x.get("alarmDAO")).put(alarm);
          return getDelegate().remove_(x, obj);
        }
      }
      return result;
      `
    },
    {
      documentation: 'Primary flow: put to delegate/MDAO, then submit to nodes, wait on MedusaEntry consensus.',
      name: 'updatePrimary',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'obj',
          type: 'foam.core.FObject'
        },
        {
          name: 'dop',
          type: 'foam.dao.DOP'
        }
      ],
      javaType: 'foam.core.FObject',
      javaCode: `
      PM pm = PM.create(x, this.getClass().getSimpleName(), "primary");
      ClusterCommand cmd = null;
      if ( obj instanceof ClusterCommand ) {
        cmd = (ClusterCommand) obj;
        obj = cmd.getData();
      }

      // TODO: create a pool of WriteLock to reuse
      WriteLock writeLock = new WriteLock();
      writeLocks_.add(writeLock);
      // getLogger().debug("updatePrimary", "writeLock", "add", writeLocks_.size());
      try {
        if ( DOP.PUT == dop ) {
          Object id = obj.getProperty("id");
          FObject old = getDelegate().find_(x, id);
          FObject nu = getDelegate().put_(x, obj);
          String data = data(x, nu, old, dop);
          // data will be empty if only changes are storageTransient.
          if ( ! SafetyUtil.isEmpty(data) ) {
            MedusaEntry entry = (MedusaEntry) submit(x, data, dop);
            getLogger().debug("updatePrimary", "primary", dop, nu.getProperty("id"), "entry", entry.toSummary());
            if ( cmd != null ) {
              cmd.setMedusaEntryId((Long) entry.getId());
            }
          }
          if ( cmd != null ) {
            cmd.setData(nu);
            return cmd;
          }
          return nu;
        }
        // DOP.REMOVE
        FObject result = getDelegate().remove_(x, obj);
        String data = data(x, obj, null, dop);
        MedusaEntry entry = (MedusaEntry) submit(x, data, dop);
        if ( cmd != null ) {
          cmd.setMedusaEntryId((Long) entry.getId());
          cmd.setData(result);
          return cmd;
        }
        return result;
      } finally {
        synchronized ( writeLock ) {
          writeLock.setComplete(true);
          writeLock.notifyAll();
          writeLocks_.remove(writeLock);
          // getLogger().debug("updatePrimary", "writeLock", "complete", writeLocks_.size());
        }
        pm.log(x);
      }
      `
    },
    {
      name: 'cmd_',
      javaCode: `
      if ( foam.dao.DAO.LAST_CMD.equals(obj) ) {
        return getDelegate();
      }
      if ( obj instanceof ClusterCommand ) {
        ClusterConfigSupport support = (ClusterConfigSupport) x.get("clusterConfigSupport");
        ClusterConfig config = support.getConfig(x, support.getConfigId());
        ClusterCommand cmd = (ClusterCommand) obj;

        if ( config.getIsPrimary() ) {
          if ( DOP.PUT == cmd.getDop() ) {
            return put_(x, cmd);
          }
          if ( DOP.REMOVE == cmd.getDop() ) {
            return remove_(x, cmd);
          }
          getLogger().warning("Unsupported operation", cmd.getDop().getLabel());
          throw new UnsupportedOperationException(cmd.getDop().getLabel());
        }

        cmd = (ClusterCommand) getClientDAO().cmd_(x, obj);

        if ( config.getType() == MedusaType.MEDIATOR ) {
          Long id = cmd.getMedusaEntryId();
          if ( id != null &&
               id > 0L ) {
            MedusaRegistry registry = (MedusaRegistry) x.get("medusaRegistry");
            registry.wait(x, id);
          }
        }
        return cmd;
      }
      return getClientDAO().cmd_(x, obj);
      `
    },
    {
      name: 'data',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'obj',
          type: 'FObject'
        },
        {
          name: 'old',
          type: 'FObject'
        },
        {
          name: 'dop',
          type: 'foam.dao.DOP'
        }
      ],
      type: 'String',
      javaCode: `
      PM pm = PM.create(x, this.getClass().getSimpleName(), "data");
      String data = null;
      try {
        FObjectFormatter formatter = formatter_.get();
        if ( old != null ) {
          formatter.maybeOutputDelta(old, obj);
        } else {
          formatter.output(obj);
        }
        data = formatter.builder().toString();
      } finally {
        pm.log(x);
      }
      return data;
      `
    },
    {
      name: 'submit',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'data',
          type: 'String'
        },
        {
          name: 'dop',
          type: 'foam.dao.DOP'
        }
      ],
      type: 'FObject',
      javaCode: `
      PM pm = PM.create(x, this.getClass().getSimpleName(), "submit");
      MedusaEntry entry = x.create(MedusaEntry.class);
      try {
        PM pmLink = new PM(this.getClass().getSimpleName(), "submit", "link");
        DaggerService dagger = (DaggerService) x.get("daggerService");
        entry = dagger.link(x, entry);
        entry.setNSpecName(getNSpec().getName());
        entry.setDop(dop);
        entry.setData(data);
        pmLink.log(x);

        getLogger().debug("submit", entry.getId());

        MedusaRegistry registry = (MedusaRegistry) x.get("medusaRegistry");
        registry.register(x, (Long) entry.getId());
        PM pmPut = new PM(this.getClass().getSimpleName(), "submit", "put");
        entry = (MedusaEntry) getMedusaEntryDAO().put_(x, entry);
        pmPut.log(x);
        PM pmWait = new PM(this.getClass().getSimpleName(), "submit", "wait");
        registry.wait(x, (Long) entry.getId());
        pmWait.log(x);
        return entry;
      } catch (Throwable t) {
        pm.error(x, entry.toSummary(), t);
        getLogger().error("submit", entry.toSummary(), t.getMessage(), t);
        throw t;
      } finally {
        pm.log(x);
      }
      `
    }
  ]
});
<commit_msg>remove all find/put/select locking (#4787)<commit_after>/**
 * @license
 * Copyright 2020 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.medusa',
  name: 'MedusaAdapterDAO',
  extends: 'foam.dao.ProxyDAO',

  documentation: `Entry point into the Medusa system.
This DAO intercepts MDAO 'put' operations and creates a medusa entry for argument model.
It then marshalls it to the primary mediator, and waits on a response.`,

  javaImports: [
    'foam.core.FObject',
    'foam.core.X',
    'foam.dao.ArraySink',
    'foam.dao.DAO',
    'foam.dao.DOP',
    'foam.dao.MDAO',
    'foam.dao.RemoveSink',
    'foam.lib.formatter.FObjectFormatter',
    'foam.lib.formatter.JSONFObjectFormatter',
    'static foam.mlang.MLang.AND',
    'static foam.mlang.MLang.EQ',
    'foam.log.LogLevel',
    'foam.nanos.alarming.Alarm',
    'foam.nanos.logger.PrefixLogger',
    'foam.nanos.logger.Logger',
    'foam.nanos.pm.PM',
    'java.util.HashMap',
    'java.util.Map',
    'java.util.List',
    'foam.util.SafetyUtil',
    'java.util.concurrent.ConcurrentLinkedDeque',
  ],

  classes: [
    {
      name: 'WriteLock',
      properties: [
        {
          name: 'complete',
          class: 'Boolean'
        }
      ]
    }
  ],

  properties: [
    {
      name: 'nSpec',
      class: 'FObjectProperty',
      of: 'foam.nanos.boot.NSpec'
    },
    {
      name: 'medusaEntryDAO',
      class: 'FObjectProperty',
      of: 'foam.dao.DAO',
      javaFactory: 'return (foam.dao.DAO) getX().get("localMedusaEntryDAO");'
    },
    {
      name: 'clientDAO',
      class: 'FObjectProperty',
      of: 'foam.dao.DAO',
      javaFactory: `
      return new foam.nanos.medusa.ClusterClientDAO.Builder(getX())
        .setNSpec(getNSpec())
        .setDelegate(new foam.dao.NullDAO(getX(), getDelegate().getOf()))
        .build();
      `
    },
    {
      name: 'logger',
      class: 'FObjectProperty',
      of: 'foam.nanos.logger.Logger',
      visibility: 'HIDDEN',
      javaFactory: `
        return new PrefixLogger(new Object[] {
          this.getClass().getSimpleName(),
          getNSpec().getName()
        }, (Logger) getX().get("logger"));
      `
    }
  ],

  axioms: [
    {
      name: 'javaExtras',
      buildJavaClass: function(cls) {
        cls.extras.push(foam.java.Code.create({
          data: `
  protected static final ThreadLocal<FObjectFormatter> formatter_ = new ThreadLocal<FObjectFormatter>() {
    @Override
    protected JSONFObjectFormatter initialValue() {
      JSONFObjectFormatter formatter = new JSONFObjectFormatter();
      formatter.setQuoteKeys(false); // default
      formatter.setOutputShortNames(true); // default
      formatter.setOutputDefaultValues(true);
      formatter.setOutputClassNames(true); // default
      formatter.setOutputDefaultClassNames(true); // default
      formatter.setOutputReadableDates(false);
      formatter.setPropertyPredicate(new foam.lib.StoragePropertyPredicate());
      return formatter;
    }

    @Override
    public FObjectFormatter get() {
      FObjectFormatter formatter = super.get();
      formatter.reset();
      return formatter;
    }
  };

  // TODO: pool of reusable WriteLocks
  protected ConcurrentLinkedDeque writeLocks_ = new ConcurrentLinkedDeque();
          `
        }));
      }
    }
  ],

  methods: [
    {
      name: 'waitOnWrite',
      args: [
        {
          name: 'x',
          type: 'Context'
        }
      ],
      javaCode: `
      WriteLock lock = (WriteLock) writeLocks_.peekLast();
      if ( lock != null ) {
        synchronized ( lock ) {
          while ( ! lock.getComplete() &&
                  writeLocks_.contains( lock ) ) {
            try {
              // getLogger().debug("waitOnWrite_", "wait");
              lock.wait(10000L);
              // getLogger().debug("waitOnWrite_", "unwait");
              getLogger().debug("waitOnWrite_", "complete", lock.getComplete(), "contains", writeLocks_.contains( lock ), "size", writeLocks_.size());
            } catch (InterruptedException e) {
              break;
            }
          }
        }
      }
      `
    },
    {
      name: 'find_',
      javaCode: `
      // FIXME: this is causing threadwait on primary - related to registry
      // waitOnWrite(x);
      return getDelegate().find_(x, id);
      `
    },
    {
      name: 'select_',
      javaCode: `
      // waitOnWrite(x);
      return getDelegate().select_(x, sink, skip, limit, order, predicate);
      `
    },
    {
      name: 'put_',
      javaCode: `
      return update(x, obj, DOP.PUT);
      `
    },
    {
      name: 'remove_',
      javaCode: `
      return update(x, obj, DOP.REMOVE);
      `
    },
    {
      name: 'removeAll_',
      javaCode: `
      getDelegate().select_(x, new RemoveSink(x, this), skip, limit, order, predicate);
      `
    },
    {
      documentation: 'Route DAO operation to primary mediator',
      name: 'update',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'obj',
          type: 'foam.core.FObject'
        },
        {
          name: 'dop',
          type: 'foam.dao.DOP'
        }
      ],
      javaType: 'foam.core.FObject',
      javaCode: `
      if ( ! ( DOP.PUT == dop ||
               DOP.REMOVE == dop ) ) {
        getLogger().warning("update", "Unsupported operation", dop.getLabel());
        throw new UnsupportedOperationException(dop.getLabel());
      }

      if ( obj instanceof Clusterable &&
           ! ((Clusterable) obj).getClusterable() ) {
        getLogger().debug("update", dop.getLabel(), "not clusterable", obj.getClass().getSimpleName(), obj.getProperty("id"));
        if ( DOP.PUT == dop ) return getDelegate().put_(x, obj);
        if ( DOP.REMOVE == dop ) return getDelegate().remove_(x, obj);
      }

      ClusterConfigSupport support = (ClusterConfigSupport) x.get("clusterConfigSupport");
      ClusterConfig config = support.getConfig(x, support.getConfigId());

      if ( config.getIsPrimary() ) {
        return updatePrimary(x, obj, dop);
      }
      return updateSecondary(x, obj, dop);
    `
    },
    {
      documentation: `Secondary flow: proxy to next server, wait on own mdao update.`,
      name: 'updateSecondary',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'obj',
          type: 'foam.core.FObject'
        },
        {
          name: 'dop',
          type: 'foam.dao.DOP'
        }
      ],
      javaType: 'foam.core.FObject',
      javaCode: `
      ClusterCommand cmd = new ClusterCommand(x, getNSpec().getName(), dop, obj);
      getLogger().debug("update", "secondary", dop, obj.getProperty("id"), "send");
      PM pm = PM.create(x, this.getClass().getSimpleName(), "secondary", "cmd");
      // PM pm = new PM(this.getClass().getSimpleName(), "secondary", "cmd");
      cmd = (ClusterCommand) getClientDAO().cmd_(x, cmd);
      pm.log(x);
      cmd.logHops(x);
      getLogger().debug("update", "secondary", dop, obj.getProperty("id"), "receive", cmd.getMedusaEntryId());
      if ( cmd.getMedusaEntryId() > 0 ) {
        pm = PM.create(x, this.getClass().getSimpleName(), "secondary", "wait");
        MedusaRegistry registry = (MedusaRegistry) x.get("medusaRegistry");
        registry.wait(x, (Long) cmd.getMedusaEntryId());
        pm.log(x);
      }
      FObject result = cmd.getData();
      if ( DOP.PUT == dop ) {
        if ( result != null ) {
          FObject nu = getDelegate().find_(x, result.getProperty("id"));
          if ( nu == null ) {
            getLogger().error("update", "secondary", dop, obj.getProperty("id"), "delegate", cmd.getMedusaEntryId(), "find", result.getProperty("id"), "null");
            Alarm alarm = new Alarm();
            alarm.setClusterable(false);
            alarm.setSeverity(LogLevel.ERROR);
            alarm.setName("MedusaAdapter secondary find failed");
            alarm.setNote(obj.getClass().getName()+" "+obj.getProperty("id"));
            alarm = (Alarm) ((DAO) x.get("alarmDAO")).put(alarm);
          }
          // put again to update storageTransient properties
          return getDelegate().put_(x, result);
        }
        // TODO/REVIEW
        getLogger().error("update", "secondary", dop, obj.getProperty("id"), "result,null");
        Alarm alarm = new Alarm();
        alarm.setClusterable(false);
        alarm.setSeverity(LogLevel.ERROR);
        alarm.setName("MedusaAdapter secondary cmd failed");
        alarm.setNote(obj.getClass().getName()+" "+obj.getProperty("id"));
        alarm = (Alarm) ((DAO) x.get("alarmDAO")).put(alarm);
      } else if ( DOP.REMOVE == dop ) {
        if ( getDelegate().find_(x, obj.getProperty("id")) != null ) {
          getLogger().error("update", "secondary", dop, obj.getProperty("id"), "delegate", "not deleted");
          Alarm alarm = new Alarm();
          alarm.setClusterable(false);
          alarm.setSeverity(LogLevel.ERROR);
          alarm.setName("MedusaAdapter secondary remove failed");
          alarm.setNote(obj.getClass().getName()+" "+obj.getProperty("id"));
          alarm = (Alarm) ((DAO) x.get("alarmDAO")).put(alarm);
          return getDelegate().remove_(x, obj);
        }
      }
      return result;
      `
    },
    {
      documentation: 'Primary flow: put to delegate/MDAO, then submit to nodes, wait on MedusaEntry consensus.',
      name: 'updatePrimary',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'obj',
          type: 'foam.core.FObject'
        },
        {
          name: 'dop',
          type: 'foam.dao.DOP'
        }
      ],
      javaType: 'foam.core.FObject',
      javaCode: `
      PM pm = PM.create(x, this.getClass().getSimpleName(), "primary");
      ClusterCommand cmd = null;
      if ( obj instanceof ClusterCommand ) {
        cmd = (ClusterCommand) obj;
        obj = cmd.getData();
      }

      // // TODO: create a pool of WriteLock to reuse
      // WriteLock writeLock = new WriteLock();
      // writeLocks_.add(writeLock);
      // getLogger().debug("updatePrimary", "writeLock", "add", writeLocks_.size());
      try {
        if ( DOP.PUT == dop ) {
          Object id = obj.getProperty("id");
          FObject old = getDelegate().find_(x, id);
          FObject nu = getDelegate().put_(x, obj);
          String data = data(x, nu, old, dop);
          // data will be empty if only changes are storageTransient.
          if ( ! SafetyUtil.isEmpty(data) ) {
            MedusaEntry entry = (MedusaEntry) submit(x, data, dop);
            getLogger().debug("updatePrimary", "primary", dop, nu.getProperty("id"), "entry", entry.toSummary());
            if ( cmd != null ) {
              cmd.setMedusaEntryId((Long) entry.getId());
            }
          }
          if ( cmd != null ) {
            cmd.setData(nu);
            return cmd;
          }
          return nu;
        }
        // DOP.REMOVE
        FObject result = getDelegate().remove_(x, obj);
        String data = data(x, obj, null, dop);
        MedusaEntry entry = (MedusaEntry) submit(x, data, dop);
        if ( cmd != null ) {
          cmd.setMedusaEntryId((Long) entry.getId());
          cmd.setData(result);
          return cmd;
        }
        return result;
      // } finally {
      //   synchronized ( writeLock ) {
      //     writeLock.setComplete(true);
      //     writeLock.notifyAll();
      //     writeLocks_.remove(writeLock);
      //     getLogger().debug("updatePrimary", "writeLock", "complete", writeLocks_.size());
        }
        pm.log(x);
      }
      `
    },
    {
      name: 'cmd_',
      javaCode: `
      if ( foam.dao.DAO.LAST_CMD.equals(obj) ) {
        return getDelegate();
      }
      if ( obj instanceof ClusterCommand ) {
        ClusterConfigSupport support = (ClusterConfigSupport) x.get("clusterConfigSupport");
        ClusterConfig config = support.getConfig(x, support.getConfigId());
        ClusterCommand cmd = (ClusterCommand) obj;

        if ( config.getIsPrimary() ) {
          if ( DOP.PUT == cmd.getDop() ) {
            return put_(x, cmd);
          }
          if ( DOP.REMOVE == cmd.getDop() ) {
            return remove_(x, cmd);
          }
          getLogger().warning("Unsupported operation", cmd.getDop().getLabel());
          throw new UnsupportedOperationException(cmd.getDop().getLabel());
        }

        cmd = (ClusterCommand) getClientDAO().cmd_(x, obj);

        if ( config.getType() == MedusaType.MEDIATOR ) {
          Long id = cmd.getMedusaEntryId();
          if ( id != null &&
               id > 0L ) {
            MedusaRegistry registry = (MedusaRegistry) x.get("medusaRegistry");
            registry.wait(x, id);
          }
        }
        return cmd;
      }
      return getClientDAO().cmd_(x, obj);
      `
    },
    {
      name: 'data',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'obj',
          type: 'FObject'
        },
        {
          name: 'old',
          type: 'FObject'
        },
        {
          name: 'dop',
          type: 'foam.dao.DOP'
        }
      ],
      type: 'String',
      javaCode: `
      PM pm = PM.create(x, this.getClass().getSimpleName(), "data");
      String data = null;
      try {
        FObjectFormatter formatter = formatter_.get();
        if ( old != null ) {
          formatter.maybeOutputDelta(old, obj);
        } else {
          formatter.output(obj);
        }
        data = formatter.builder().toString();
      } finally {
        pm.log(x);
      }
      return data;
      `
    },
    {
      name: 'submit',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'data',
          type: 'String'
        },
        {
          name: 'dop',
          type: 'foam.dao.DOP'
        }
      ],
      type: 'FObject',
      javaCode: `
      PM pm = PM.create(x, this.getClass().getSimpleName(), "submit");
      MedusaEntry entry = x.create(MedusaEntry.class);
      try {
        PM pmLink = new PM(this.getClass().getSimpleName(), "submit", "link");
        DaggerService dagger = (DaggerService) x.get("daggerService");
        entry = dagger.link(x, entry);
        entry.setNSpecName(getNSpec().getName());
        entry.setDop(dop);
        entry.setData(data);
        pmLink.log(x);

        getLogger().debug("submit", entry.getId());

        MedusaRegistry registry = (MedusaRegistry) x.get("medusaRegistry");
        registry.register(x, (Long) entry.getId());
        PM pmPut = new PM(this.getClass().getSimpleName(), "submit", "put");
        entry = (MedusaEntry) getMedusaEntryDAO().put_(x, entry);
        pmPut.log(x);
        PM pmWait = new PM(this.getClass().getSimpleName(), "submit", "wait");
        registry.wait(x, (Long) entry.getId());
        pmWait.log(x);
        return entry;
      } catch (Throwable t) {
        pm.error(x, entry.toSummary(), t);
        getLogger().error("submit", entry.toSummary(), t.getMessage(), t);
        throw t;
      } finally {
        pm.log(x);
      }
      `
    }
  ]
});
<|endoftext|>