<commit_before>define( [
            'dojo/_base/declare',
            'dojo/_base/array',
            'dojo/_base/lang',
            'dojo/dom-construct',
            'dojo/dom-class',
            'dojo/query',
            'JBrowse/View/Track/BlockBased',
            'JBrowse/View/Track/_ExportMixin',
            'JBrowse/CodonTable',
            'JBrowse/Util'
        ],
        function(
            declare,
            array,
            lang,
            dom,
            domClass,
            query,
            BlockBased,
            ExportMixin,
            CodonTable,
            Util
        ) {

return declare( [BlockBased, ExportMixin, CodonTable],
 /**
  * @lends JBrowse.View.Track.Sequence.prototype
  */
{
    /**
     * Track to display the underlying reference sequence, when zoomed in
     * far enough.
     *
     * @constructs
     * @extends JBrowse.View.Track.BlockBased
     */
    constructor: function( args ) {
        this._charMeasurements = {};
        this._codonTable = this.generateCodonTable(lang.mixin(this.defaultCodonTable,this.config.codonTable));
        this._codonStarts = this.config.codonStarts || this.defaultStarts
        this._codonStops = this.config.codonStops || this.defaultStops
    },

    _defaultConfig: function() {
        return {
            maxExportSpan: 500000,
            showForwardStrand: true,
            showReverseStrand: true,
            showTranslation: true,
            showColor: true,
            seqType: 'dna',
            proteinColorScheme: 'taylor'
        };
    },
    _exportFormats: function() {
        return [{name: 'FASTA', label: 'FASTA', fileExt: 'fasta'}];
    },

    endZoom: function(destScale, destBlockBases) {
        this.clear();
    },

    setViewInfo:function(genomeView, heightUpdate, numBlocks,
                         trackDiv,
                         widthPct, widthPx, scale) {
        this.inherited( arguments );
        this.show();
    },

    nbsp: String.fromCharCode(160),

    fillBlock:function( args ) {

        var blockIndex = args.blockIndex;
        var block = args.block;
        var leftBase = args.leftBase;
        var rightBase = args.rightBase;
        var scale = args.scale;

        var leftExtended = leftBase - 2;
        var rightExtended = rightBase + 2;

        var thisB = this;

        var blur = dojo.create(
            'div',
            { className: 'sequence_blur',
              innerHTML: 'Loading'
            }, block.domNode );

        this.heightUpdate( blur.offsetHeight+2*blur.offsetTop, blockIndex );

        // if we are zoomed in far enough to draw bases, then draw them
        if ( scale >= 1.3 ) {
            this.store.getReferenceSequence(
                {
                    ref: this.refSeq.name,
                    start: leftExtended,
                    end: rightExtended
                },
                function( seq ) {
                    if(seq.trim() == ""){
                        blur.innerHTML = 'No sequence available';;
                    }
                    else {
                        dom.empty( block.domNode );
                        thisB._fillSequenceBlock( block, blockIndex, scale, seq );
                    }
                    args.finishCallback();
                },
                function() {
                    args.finishCallback();
                }
            );
        }
        // otherwise, just draw a sort of line (possibly dotted) that
        // suggests there are bases there if you zoom in far enough
        else {
            blur.innerHTML = 'Zoom in to see sequence';
            args.finishCallback();
        }
    },

    _fillSequenceBlock: function( block, blockIndex, scale, seq ) {
        seq = seq.replace(/\s/g,this.nbsp);

        var blockStart = block.startBase;
        var blockEnd = block.endBase;
        var blockSeq = seq.substring( 2, seq.length - 2 );
        var blockLength = blockSeq.length;

        var extStart = blockStart-2;
        var extEnd = blockStart+2;
        var leftover = (seq.length - 2) % 3;
        var extStartSeq = seq.substring( 0, seq.length - 2 );
        var extEndSeq = seq.substring( 2 );

        if( this.config.showForwardStrand && this.config.showTranslation ) {
            var frameDiv = [];
            for( var i = 0; i < 3; i++ ) {
                var transStart = blockStart + i;
                var frame = (transStart % 3 + 3) % 3;
                var translatedDiv = this._renderTranslation( extEndSeq, i, blockStart, blockEnd, blockLength, scale );
                frameDiv[frame] = translatedDiv;
                domClass.add( translatedDiv, "frame" + frame );
            }
            for( var i = 2; i >= 0; i-- ) {
                block.domNode.appendChild( frameDiv[i] );
            }
        }

        // make a table to contain the sequences
        var charSize = this.getCharacterMeasurements('sequence');
        var bigTiles = scale > charSize.w + 4; // whether to add .big styles to the base tiles
        var seqNode;
        if( this.config.showReverseStrand || this.config.showForwardStrand )
            seqNode = dom.create(
                "table", {
                    className: "sequence" + (bigTiles ? ' big' : '') + (this.config.showColor ? '' : ' nocolor'),
                    style: { width: "100%" }
                }, block.domNode);

        // add a table for the forward strand
        if( this.config.showForwardStrand )
            seqNode.appendChild( this._renderSeqTr( blockStart, blockEnd, blockSeq, scale ));

        // and one for the reverse strand
        if( this.config.showReverseStrand ) {
            var comp = this._renderSeqTr( blockStart, blockEnd, Util.complement(blockSeq), scale );
            comp.className = 'revcom';
            seqNode.appendChild( comp );

            if( this.config.showTranslation ) {
                var frameDiv = [];
                for(var i = 0; i < 3; i++) {
                    var transStart = blockStart + 1 - i;
                    var frame = (transStart % 3 + 3 + leftover) % 3;
                    var translatedDiv = this._renderTranslation( extStartSeq, i, blockStart, blockEnd, blockLength, scale, true );
                    frameDiv[frame] = translatedDiv;
                    domClass.add( translatedDiv, "frame" + frame );
                }
                for( var i = 0; i < 3; i++ ) {
                    block.domNode.appendChild( frameDiv[i] );
                }
            }
        }

        var totalHeight = 0;
        array.forEach( block.domNode.childNodes, function( table ) {
                           totalHeight += (table.clientHeight || table.offsetHeight);
                       });
        this.heightUpdate( totalHeight, blockIndex );
    },

    _renderTranslation: function( seq, offset, blockStart, blockEnd, blockLength, scale, reverse ) {
        seq = reverse ? Util.revcom( seq ) : seq;

        var extraBases = (seq.length - offset) % 3;
        var seqSliced = seq.slice( offset, seq.length - extraBases );

        var translated = "";
        for( var i = 0; i < seqSliced.length; i += 3 ) {
            var nextCodon = seqSliced.slice(i, i + 3);
            var aminoAcid = this._codonTable[nextCodon] || this.nbsp;
            translated += aminoAcid;
        }

        translated = reverse ? translated.split("").reverse().join("") : translated; // Flip the translated seq for left-to-right rendering
        var orientedSeqSliced = reverse ? seqSliced.split("").reverse().join("") : seqSliced

        var charSize = this.getCharacterMeasurements("aminoAcid");
        var bigTiles = scale > charSize.w + 4; // whether to add .big styles to the base tiles

        var charWidth = 100/(blockLength / 3);

        var container = dom.create( 'div',{ className: 'translatedSequence' } );
        var table  = dom.create('table',
            {
                className: 'translatedSequence offset'+offset+(bigTiles ? ' big' : ''),
                style:
                {
                    width: (charWidth * translated.length) + "%"
                }
            }, container );
        var tr = dom.create('tr', {}, table );

        table.style.left = (
            reverse ? 100 - charWidth * (translated.length + offset / 3)
                    : charWidth*offset/3
        ) + "%";

        charWidth = 100/ translated.length + "%";

        var drawChars = scale >= charSize.w;
        if( drawChars )
            table.className += ' big';

        for( var i=0; i<translated.length; i++ ) {
            var aminoAcidSpan = document.createElement('td');
            var originalCodon = orientedSeqSliced.slice(3 * i, 3 * i + 3)
            originalCodon = reverse ? originalCodon.split("").reverse().join("") : originalCodon;
            aminoAcidSpan.className = 'aminoAcid aminoAcid_'+translated.charAt(i).toLowerCase();

            // However, if it's known to be a start/stop, apply those CSS classes instead.
            if (this._codonStarts.indexOf(originalCodon.toUpperCase()) != -1) {
                aminoAcidSpan.className = 'aminoAcid aminoAcid_start'
            }
            if (this._codonStops.indexOf(originalCodon.toUpperCase()) != -1) {
                aminoAcidSpan.className = 'aminoAcid aminoAcid_stop'
            }

            aminoAcidSpan.style.width = charWidth;
            if( drawChars ) {
                aminoAcidSpan.innerHTML = translated.charAt( i );
            }
            tr.appendChild(aminoAcidSpan);
        }
        return container;
    },

    /**
     * Given the start and end coordinates, and the sequence bases,
     * makes a table row containing the sequence.
     * @private
     */
    _renderSeqTr: function ( start, end, seq, scale ) {

        var charSize = this.getCharacterMeasurements('sequence');
        var container  = document.createElement('tr');
        var charWidth = 100/(end-start)+"%";
        var drawChars = scale >= charSize.w;
        var baseClassDefault = 'base';
        if(this.config.seqType === 'protein'){
            baseClassDefault += ' aaScheme_' + this.config.proteinColorScheme;
        }
        for( var i=0; i<seq.length; i++ ) {
            var base = document.createElement('td');
            base.className = baseClassDefault + ' base_'+seq.charAt(i).toLowerCase();
            base.style.width = charWidth;
            if( drawChars ) {
                base.innerHTML = seq.charAt(i);
            }
            container.appendChild(base);
        }
        return container;
    },

    startZoom: function() {
        query('.base', this.div ).empty();
    },

    /**
     * @returns {Object} containing h and w,
     *      in pixels, of the characters being used for sequences
     */
    getCharacterMeasurements: function( className ) {
        return this._charMeasurements[className] || (
            this._charMeasurements[className] = this._measureSequenceCharacterSize( this.div, className )
        );
    },

    /**
     * Conducts a test with DOM elements to measure sequence text width
     * and height.
     */
    _measureSequenceCharacterSize: function( containerElement, className ) {
        var widthTest = document.createElement("td");
        widthTest.className = className;
        widthTest.style.visibility = "hidden";
        var widthText = "12345678901234567890123456789012345678901234567890";
        widthTest.appendChild(document.createTextNode(widthText));
        containerElement.appendChild(widthTest);
        var result = {
            w:  (widthTest.clientWidth / widthText.length)+1,
            h: widthTest.clientHeight
        };
        containerElement.removeChild(widthTest);
        return result;
  },

    _trackMenuOptions: function() {
        var track = this;
        var o = this.inherited(arguments);
        o.push( { type: 'dijit/MenuSeparator' } );
        o.push.apply( o,
            [
                { label: 'Show forward strand',
                  type: 'dijit/CheckedMenuItem',
                  checked: !! this.config.showForwardStrand,
                  onClick: function(event) {
                      track.config.showForwardStrand = this.checked;
                      track.changed();
                  }
                },
                { label: 'Show reverse strand',
                  type: 'dijit/CheckedMenuItem',
                  checked: !! this.config.showReverseStrand,
                  onClick: function(event) {
                      track.config.showReverseStrand = this.checked;
                      track.changed();
                  }
                },
                { label: 'Show translation',
                  type: 'dijit/CheckedMenuItem',
                  checked: !! this.config.showTranslation,
                  onClick: function(event) {
                      track.config.showTranslation = this.checked;
                      track.changed();
                  }
                },
                { label: 'Show color',
                  type: 'dijit/CheckedMenuItem',
                  checked: !! this.config.showColor,
                  onClick: function(event) {
                      track.config.showColor = this.checked;
                      track.changed();
                  }
                }
            ]);
        return o;
    }

});
});
<commit_msg>add a little bit of error logging to the Sequence track<commit_after>define( [
            'dojo/_base/declare',
            'dojo/_base/array',
            'dojo/_base/lang',
            'dojo/dom-construct',
            'dojo/dom-class',
            'dojo/query',
            'JBrowse/View/Track/BlockBased',
            'JBrowse/View/Track/_ExportMixin',
            'JBrowse/CodonTable',
            'JBrowse/Util'
        ],
        function(
            declare,
            array,
            lang,
            dom,
            domClass,
            query,
            BlockBased,
            ExportMixin,
            CodonTable,
            Util
        ) {

return declare( [BlockBased, ExportMixin, CodonTable],
 /**
  * @lends JBrowse.View.Track.Sequence.prototype
  */
{
    /**
     * Track to display the underlying reference sequence, when zoomed in
     * far enough.
     *
     * @constructs
     * @extends JBrowse.View.Track.BlockBased
     */
    constructor: function( args ) {
        this._charMeasurements = {};
        this._codonTable = this.generateCodonTable(lang.mixin(this.defaultCodonTable,this.config.codonTable));
        this._codonStarts = this.config.codonStarts || this.defaultStarts
        this._codonStops = this.config.codonStops || this.defaultStops
    },

    _defaultConfig: function() {
        return {
            maxExportSpan: 500000,
            showForwardStrand: true,
            showReverseStrand: true,
            showTranslation: true,
            showColor: true,
            seqType: 'dna',
            proteinColorScheme: 'taylor'
        };
    },
    _exportFormats: function() {
        return [{name: 'FASTA', label: 'FASTA', fileExt: 'fasta'}];
    },

    endZoom: function(destScale, destBlockBases) {
        this.clear();
    },

    setViewInfo:function(genomeView, heightUpdate, numBlocks,
                         trackDiv,
                         widthPct, widthPx, scale) {
        this.inherited( arguments );
        this.show();
    },

    nbsp: String.fromCharCode(160),

    fillBlock:function( args ) {

        var blockIndex = args.blockIndex;
        var block = args.block;
        var leftBase = args.leftBase;
        var rightBase = args.rightBase;
        var scale = args.scale;

        var leftExtended = leftBase - 2;
        var rightExtended = rightBase + 2;

        var thisB = this;

        var blur = dojo.create(
            'div',
            { className: 'sequence_blur',
              innerHTML: 'Loading'
            }, block.domNode );

        this.heightUpdate( blur.offsetHeight+2*blur.offsetTop, blockIndex );

        // if we are zoomed in far enough to draw bases, then draw them
        if ( scale >= 1.3 ) {
            this.store.getReferenceSequence(
                {
                    ref: this.refSeq.name,
                    start: leftExtended,
                    end: rightExtended
                },
                function( seq ) {
                    if(seq.trim() == ""){
                        blur.innerHTML = 'No sequence available';;
                    }
                    else {
                        dom.empty( block.domNode );
                        thisB._fillSequenceBlock( block, blockIndex, scale, seq );
                    }
                    args.finishCallback();
                },
                function(error) {
                    if (args.errorCallback)
                        args.errorCallback(error)
                    else {
                        console.error(error)
                        args.finishCallback()
                    }
                }
            );
        }
        // otherwise, just draw a sort of line (possibly dotted) that
        // suggests there are bases there if you zoom in far enough
        else {
            blur.innerHTML = 'Zoom in to see sequence';
            args.finishCallback();
        }
    },

    _fillSequenceBlock: function( block, blockIndex, scale, seq ) {
        seq = seq.replace(/\s/g,this.nbsp);

        var blockStart = block.startBase;
        var blockEnd = block.endBase;
        var blockSeq = seq.substring( 2, seq.length - 2 );
        var blockLength = blockSeq.length;

        var extStart = blockStart-2;
        var extEnd = blockStart+2;
        var leftover = (seq.length - 2) % 3;
        var extStartSeq = seq.substring( 0, seq.length - 2 );
        var extEndSeq = seq.substring( 2 );

        if( this.config.showForwardStrand && this.config.showTranslation ) {
            var frameDiv = [];
            for( var i = 0; i < 3; i++ ) {
                var transStart = blockStart + i;
                var frame = (transStart % 3 + 3) % 3;
                var translatedDiv = this._renderTranslation( extEndSeq, i, blockStart, blockEnd, blockLength, scale );
                frameDiv[frame] = translatedDiv;
                domClass.add( translatedDiv, "frame" + frame );
            }
            for( var i = 2; i >= 0; i-- ) {
                block.domNode.appendChild( frameDiv[i] );
            }
        }

        // make a table to contain the sequences
        var charSize = this.getCharacterMeasurements('sequence');
        var bigTiles = scale > charSize.w + 4; // whether to add .big styles to the base tiles
        var seqNode;
        if( this.config.showReverseStrand || this.config.showForwardStrand )
            seqNode = dom.create(
                "table", {
                    className: "sequence" + (bigTiles ? ' big' : '') + (this.config.showColor ? '' : ' nocolor'),
                    style: { width: "100%" }
                }, block.domNode);

        // add a table for the forward strand
        if( this.config.showForwardStrand )
            seqNode.appendChild( this._renderSeqTr( blockStart, blockEnd, blockSeq, scale ));

        // and one for the reverse strand
        if( this.config.showReverseStrand ) {
            var comp = this._renderSeqTr( blockStart, blockEnd, Util.complement(blockSeq), scale );
            comp.className = 'revcom';
            seqNode.appendChild( comp );

            if( this.config.showTranslation ) {
                var frameDiv = [];
                for(var i = 0; i < 3; i++) {
                    var transStart = blockStart + 1 - i;
                    var frame = (transStart % 3 + 3 + leftover) % 3;
                    var translatedDiv = this._renderTranslation( extStartSeq, i, blockStart, blockEnd, blockLength, scale, true );
                    frameDiv[frame] = translatedDiv;
                    domClass.add( translatedDiv, "frame" + frame );
                }
                for( var i = 0; i < 3; i++ ) {
                    block.domNode.appendChild( frameDiv[i] );
                }
            }
        }

        var totalHeight = 0;
        array.forEach( block.domNode.childNodes, function( table ) {
                           totalHeight += (table.clientHeight || table.offsetHeight);
                       });
        this.heightUpdate( totalHeight, blockIndex );
    },

    _renderTranslation: function( seq, offset, blockStart, blockEnd, blockLength, scale, reverse ) {
        seq = reverse ? Util.revcom( seq ) : seq;

        var extraBases = (seq.length - offset) % 3;
        var seqSliced = seq.slice( offset, seq.length - extraBases );

        var translated = "";
        for( var i = 0; i < seqSliced.length; i += 3 ) {
            var nextCodon = seqSliced.slice(i, i + 3);
            var aminoAcid = this._codonTable[nextCodon] || this.nbsp;
            translated += aminoAcid;
        }

        translated = reverse ? translated.split("").reverse().join("") : translated; // Flip the translated seq for left-to-right rendering
        var orientedSeqSliced = reverse ? seqSliced.split("").reverse().join("") : seqSliced

        var charSize = this.getCharacterMeasurements("aminoAcid");
        var bigTiles = scale > charSize.w + 4; // whether to add .big styles to the base tiles

        var charWidth = 100/(blockLength / 3);

        var container = dom.create( 'div',{ className: 'translatedSequence' } );
        var table  = dom.create('table',
            {
                className: 'translatedSequence offset'+offset+(bigTiles ? ' big' : ''),
                style:
                {
                    width: (charWidth * translated.length) + "%"
                }
            }, container );
        var tr = dom.create('tr', {}, table );

        table.style.left = (
            reverse ? 100 - charWidth * (translated.length + offset / 3)
                    : charWidth*offset/3
        ) + "%";

        charWidth = 100/ translated.length + "%";

        var drawChars = scale >= charSize.w;
        if( drawChars )
            table.className += ' big';

        for( var i=0; i<translated.length; i++ ) {
            var aminoAcidSpan = document.createElement('td');
            var originalCodon = orientedSeqSliced.slice(3 * i, 3 * i + 3)
            originalCodon = reverse ? originalCodon.split("").reverse().join("") : originalCodon;
            aminoAcidSpan.className = 'aminoAcid aminoAcid_'+translated.charAt(i).toLowerCase();

            // However, if it's known to be a start/stop, apply those CSS classes instead.
            if (this._codonStarts.indexOf(originalCodon.toUpperCase()) != -1) {
                aminoAcidSpan.className = 'aminoAcid aminoAcid_start'
            }
            if (this._codonStops.indexOf(originalCodon.toUpperCase()) != -1) {
                aminoAcidSpan.className = 'aminoAcid aminoAcid_stop'
            }

            aminoAcidSpan.style.width = charWidth;
            if( drawChars ) {
                aminoAcidSpan.innerHTML = translated.charAt( i );
            }
            tr.appendChild(aminoAcidSpan);
        }
        return container;
    },

    /**
     * Given the start and end coordinates, and the sequence bases,
     * makes a table row containing the sequence.
     * @private
     */
    _renderSeqTr: function ( start, end, seq, scale ) {

        var charSize = this.getCharacterMeasurements('sequence');
        var container  = document.createElement('tr');
        var charWidth = 100/(end-start)+"%";
        var drawChars = scale >= charSize.w;
        var baseClassDefault = 'base';
        if(this.config.seqType === 'protein'){
            baseClassDefault += ' aaScheme_' + this.config.proteinColorScheme;
        }
        for( var i=0; i<seq.length; i++ ) {
            var base = document.createElement('td');
            base.className = baseClassDefault + ' base_'+seq.charAt(i).toLowerCase();
            base.style.width = charWidth;
            if( drawChars ) {
                base.innerHTML = seq.charAt(i);
            }
            container.appendChild(base);
        }
        return container;
    },

    startZoom: function() {
        query('.base', this.div ).empty();
    },

    /**
     * @returns {Object} containing h and w,
     *      in pixels, of the characters being used for sequences
     */
    getCharacterMeasurements: function( className ) {
        return this._charMeasurements[className] || (
            this._charMeasurements[className] = this._measureSequenceCharacterSize( this.div, className )
        );
    },

    /**
     * Conducts a test with DOM elements to measure sequence text width
     * and height.
     */
    _measureSequenceCharacterSize: function( containerElement, className ) {
        var widthTest = document.createElement("td");
        widthTest.className = className;
        widthTest.style.visibility = "hidden";
        var widthText = "12345678901234567890123456789012345678901234567890";
        widthTest.appendChild(document.createTextNode(widthText));
        containerElement.appendChild(widthTest);
        var result = {
            w:  (widthTest.clientWidth / widthText.length)+1,
            h: widthTest.clientHeight
        };
        containerElement.removeChild(widthTest);
        return result;
  },

    _trackMenuOptions: function() {
        var track = this;
        var o = this.inherited(arguments);
        o.push( { type: 'dijit/MenuSeparator' } );
        o.push.apply( o,
            [
                { label: 'Show forward strand',
                  type: 'dijit/CheckedMenuItem',
                  checked: !! this.config.showForwardStrand,
                  onClick: function(event) {
                      track.config.showForwardStrand = this.checked;
                      track.changed();
                  }
                },
                { label: 'Show reverse strand',
                  type: 'dijit/CheckedMenuItem',
                  checked: !! this.config.showReverseStrand,
                  onClick: function(event) {
                      track.config.showReverseStrand = this.checked;
                      track.changed();
                  }
                },
                { label: 'Show translation',
                  type: 'dijit/CheckedMenuItem',
                  checked: !! this.config.showTranslation,
                  onClick: function(event) {
                      track.config.showTranslation = this.checked;
                      track.changed();
                  }
                },
                { label: 'Show color',
                  type: 'dijit/CheckedMenuItem',
                  checked: !! this.config.showColor,
                  onClick: function(event) {
                      track.config.showColor = this.checked;
                      track.changed();
                  }
                }
            ]);
        return o;
    }

});
});
<|endoftext|>


<commit_before>/**
 * @fileoverview About this file
 */

/**
 * @type {Object}
 * @const
 */
var SugarCrm = {};

/**
 * Represents SugarCRM field boolean value.
 * @enum {string}
 */
SugarCrm.Boolean = {
  '0': '0',
  '1': '1'
};

/**
 * Represents SugarCRM name value list pair.
 * @constructor
 */
SugarCrm.NameValue = function() {};

/**
 * @type {string}
 */
SugarCrm.NameValue.prototype.name;

/**
 * @type {string}
 */
SugarCrm.NameValue.prototype.value;

/**
 * Represents SugarCRM name value list entry.
 * @constructor
 */
SugarCrm.NameValueEntry = function() {};

/**
 * @type {string}
 */
SugarCrm.NameValueEntry.prototype.id;

/**
 * @type {string}
 */
SugarCrm.NameValueEntry.prototype.module_name;

/**
 * @type {Object.<SugarCrm.NameValue>}
 */
SugarCrm.NameValueEntry.prototype.name_value_list;

/**
 * Represents SugarCRM email field.
 * @constructor
 */
SugarCrm.EmailField = function() {};

/**
 * @type {string}
 */
SugarCrm.EmailField.prototype.bean_id;

/**
 * @type {string}
 */
SugarCrm.EmailField.prototype.bean_module;

/**
 * @type {string}
 */
SugarCrm.EmailField.prototype.date_created;

/**
 * @type {string}
 */
SugarCrm.EmailField.prototype.date_modified;

/**
 * @type {SugarCrm.Boolean}
 */
SugarCrm.EmailField.prototype.deleted;

/**
 * @type {string}
 */
SugarCrm.EmailField.prototype.email_address;

/**
 * @type {string}
 */
SugarCrm.EmailField.prototype.email_address_caps;

/**
 * @type {string}
 */
SugarCrm.EmailField.prototype.email_address_id;

/**
 * @type {string}
 */
SugarCrm.EmailField.prototype.id;

/**
 * @type {SugarCrm.Boolean}
 */
SugarCrm.EmailField.prototype.invalid_email;

/**
 * @type {SugarCrm.Boolean}
 */
SugarCrm.EmailField.prototype.opt_out;

/**
 * @type {SugarCrm.Boolean}
 */
SugarCrm.EmailField.prototype.primary_address;

/**
 * @type {SugarCrm.Boolean}
 */
SugarCrm.EmailField.prototype.reply_to_address;

/**
 * CRUD Request result.
 * @interface
 */
SugarCrm.CrudResult = function() {};

/**
 * @type {number}
 */
SugarCrm.CrudResult.prototype.created;

/**
 * @type {number}
 */
SugarCrm.CrudResult.prototype.failed;

/**
 * @type {number}
 */
SugarCrm.CrudResult.prototype.deleted;

/**
 * Represents SugarCRM entry.
 * @constructor
 */
SugarCrm.Record = function() {};

/**
 * @type {string}
 */
SugarCrm.Record.prototype.id = '';

/**
 * @type {string}
 */
SugarCrm.Record.prototype.name = '';

/**
 * @type {string}
 */
SugarCrm.Record.prototype.date_entered = '';

/**
 * @type {string}
 */
SugarCrm.Record.prototype.date_modified = '';

/**
 * This field include in v10 REST result, but not in v4.
 * @type {string} module name.
 */
SugarCrm.Record.prototype._module = '';

/**
 * @type {string} either '1' or '0'
 */
SugarCrm.Record.prototype.deleted = '';

/**
 * @type {string}
 */
SugarCrm.Record.prototype.description = '';

/**
 * Result record from query with scoring.
 * @constructor
 * @extends {SugarCrm.Record}
 */
SugarCrm.ScoredRecord = function() {};

/**
 * @type {number}
 */
SugarCrm.ScoredRecord.prototype._score = 0;

/**
 * Record in login respond.
 * @constructor
 * @extends {SugarCrm.Record}
 */
SugarCrm.LoginRecord = function() {};

/**
 * @type {string} "-99"
 */
SugarCrm.LoginRecord.prototype.user_currency_id = '';

/**
 * @type {string} "US Dollars"
 */
SugarCrm.LoginRecord.prototype.user_currency_name = '';

/**
 * @type {string} "."
 */
SugarCrm.LoginRecord.prototype.user_decimal_seperator = '';

/**
 * @type {string} "m/d/Y"
 */
SugarCrm.LoginRecord.prototype.user_default_dateformat = '';

/**
 * @type {?string}
 */
SugarCrm.LoginRecord.prototype.user_default_team_id = '';

/**
 * @type {string} "h:ia"
 */
SugarCrm.LoginRecord.prototype.user_default_timeformat = '';

/**
 * @type {string}
 */
SugarCrm.LoginRecord.prototype.user_id = '';

/**
 * @type {boolean}
 */
SugarCrm.LoginRecord.prototype.user_is_admin = false;

/**
 * Represents SugarCRM ModuleField.
 * @constructor
 */
SugarCrm.ModuleField = function() {};

/**
 * @type {boolean}
 */
SugarCrm.ModuleField.prototype.calculated = false;

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.group = '';

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.id_name = '';

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.label = '';

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.len = '';

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.name = '';

/**
 * @type {Object.<SugarCrm.NameValue>|undefined}
 */
SugarCrm.ModuleField.prototype.options = {};

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.related_module = '';

/**
 * @type {number}
 */
SugarCrm.ModuleField.prototype.required = 0;

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.type = '';

/**
 * Represents SugarCRM LinkField.
 * @constructor
 */
SugarCrm.LinkField = function() {};

/**
 * @type {string} this is always 'link' ?
 */
SugarCrm.LinkField.prototype.type = '';

/**
 * @type {string} Module name
 */
SugarCrm.LinkField.prototype.module = '';

/**
 * @type {string} Link name. Same as its object hash key.
 */
SugarCrm.LinkField.prototype.name = '';

/**
 * Represents meta data for ModuleInfo.
 * @constructor
 */
SugarCrm.ModuleMeta = function() {};

/**
 * Color hex for module.
 * @type {string}
 */
SugarCrm.ModuleMeta.prototype.color = '';

/**
 * List of email fields.
 * @type {Array.<string>}
 */
SugarCrm.ModuleMeta.prototype.email_fields = [];

/**
 * Represents SugarCRM Acl.
 * @constructor
 */
SugarCrm.Acl = function() {};

/**
 * @type {boolean}
 */
SugarCrm.Acl.prototype.access = false;

/**
 * Enum of 'edit', 'delete', 'list', 'view', 'import', 'export'
 * @type {string}
 */
SugarCrm.Acl.prototype.action = '';

/**
 * Represents SugarCRM AvailableModule.
 * @constructor
 */
SugarCrm.AvailableModule = function() {};

/**
 * @type {boolean}
 */
SugarCrm.AvailableModule.prototype.favorite_enabled = false;

/**
 * @type {string}
 */
SugarCrm.AvailableModule.prototype.module_key = '';

/**
 * @type {string}
 */
SugarCrm.AvailableModule.prototype.module_label = '';

/**
 * @type {Array.<SugarCrm.Acl>}
 */
SugarCrm.AvailableModule.prototype.acls = [];

/**
 * Represents SugarCRM ModuleInfo.
 * @constructor
 */
SugarCrm.ModuleInfo = function() {};

/**
 * @type {SugarCrm.ModuleMeta}
 */
SugarCrm.ModuleInfo.prototype._meta = null;

/**
 * @type {string}
 */
SugarCrm.ModuleInfo.prototype.module_name = '';

/**
 * @type {string}
 */
SugarCrm.ModuleInfo.prototype.table_name = '';

/**
 * @type {!Object.<SugarCrm.ModuleField>|!Array.<SugarCrm.ModuleField>}
 */
SugarCrm.ModuleInfo.prototype.module_fields = [];

/**
 * @type {!Object.<SugarCrm.LinkField>}
 */
SugarCrm.ModuleInfo.prototype.link_fields = {};

/**
 * Represents query format in channel.
 * @constructor
 * @extends {CrmApp.ReqQuery}
 */
SugarCrm.Query = function() {};

/**
 * @type {Array.<SugarCrm.Record>} result of query.
 */
SugarCrm.Query.prototype.result = [];

/**
 * @constructor
 */
SugarCrm.ServerInfo = function() {};

/**
 * @type {(string|undefined)}
 */
SugarCrm.ServerInfo.prototype.flavor = '';

/**
 * @type {(string|undefined)}
 */
SugarCrm.ServerInfo.prototype.version = '';

/**
 * @type {(string|undefined)}
 */
SugarCrm.ServerInfo.prototype.uptime = '';

/**
 * @constructor
 */
SugarCrm.About = function() {};

/**
 * @type {(string|undefined)}
 */
SugarCrm.About.prototype.baseUrl = '';

/**
 * @type {string}
 */
SugarCrm.About.prototype.domain = '';

/**
 * @type {(string|undefined)}
 */
SugarCrm.About.prototype.userName = '';

/**
 * @type {(boolean|undefined)}
 */
SugarCrm.About.prototype.hostPermission = false;

/**
 * @type {(string|undefined)} authentication provider.
 */
SugarCrm.About.prototype.provider = '';

/**
 * @type {(boolean|undefined)}
 */
SugarCrm.About.prototype.isLogin = false;

/**
 * @constructor
 * @extends {SugarCrm.About}
 */
CrmApp.Credential = function() {};

/**
 * @type {string}
 */
CrmApp.Credential.prototype.password = '';

/**
 * @type {string|undefined}
 */
CrmApp.Credential.prototype.provider = '';

/**
 * @type {boolean|undefined}
 */
CrmApp.Credential.prototype.hashed = false;

/**
 * @constructor
 */
SugarCrm.Details = function() {};

/**
 * @type {SugarCrm.About}
 */
SugarCrm.Details.prototype.about = null;

/**
 * @type {SugarCrm.ServerInfo}
 */
SugarCrm.Details.prototype.serverInfo = null;

/**
 * @type {?SugarCrm.LoginRecord}
 */
SugarCrm.Details.prototype.loginInfo = null;

/**
 * @type {?CrmApp.Credential}
 */
SugarCrm.Details.prototype.credential = null;

/**
 * @type {Array<SugarCrm.AvailableModule>}
 */
SugarCrm.Details.prototype.availableModules = [];

/**
 * @type {Array.<SugarCrm.ModuleInfo>}
 */
SugarCrm.Details.prototype.modulesInfo = [];

/**
 * Query result for GData contact to SugarCRM records.
 * @interface
 */
SugarCrm.ContactSimilarityResult = function() {};

/**
 * @type {string}
 */
SugarCrm.ContactSimilarityResult.prototype.module;

/**
 * @type {string}
 */
SugarCrm.ContactSimilarityResult.prototype.index;

/**
 * @type {string}
 */
SugarCrm.ContactSimilarityResult.prototype.key;

/**
 * @type {Array<SugarCrm.Record>}
 */
SugarCrm.ContactSimilarityResult.prototype.result;

/**
 * Module name and id pair in relationship result.
 * @constructor
 */
SugarCrm.ModuleNameIdPair = function() {};

/**
 * @type {string}
 */
SugarCrm.ModuleNameIdPair.prototype.module_name;

/**
 * @type {string}
 */
SugarCrm.ModuleNameIdPair.prototype.id;

/**
 * Record format for relationships cache.
 * @constructor
 */
SugarCrm.Relationships = function() {};

/**
 * @type {number}
 */
SugarCrm.Relationships.prototype.modified;

/**
 * @type {SugarCrm.ModuleNameIdPair}
 */
SugarCrm.Relationships.prototype.to;

/**
 * @type {SugarCrm.ModuleNameIdPair}
 */
SugarCrm.Relationships.prototype.from;

/**
 * Represents SugarCRM entry.
 * @constructor
 */
SugarCrm.SyncCheckpoint = function() {};

/**
 * Lower value of updated timestamp.
 * @type {string}
 */
SugarCrm.SyncCheckpoint.prototype.lower = '';

/**
 * Lower record id.
 * @type {string}
 */
SugarCrm.SyncCheckpoint.prototype.lowerId = '';

/**
 * Upper value of updated timestamp.
 * @type {string}
 */
SugarCrm.SyncCheckpoint.prototype.upper = '';

/**
 * Upper record id.
 * @type {string}
 */
SugarCrm.SyncCheckpoint.prototype.upperId = '';

/**
 * Updated timestamp.
 * @type {number}
 */
SugarCrm.SyncCheckpoint.prototype.updated = 0;
<commit_msg>include attendiee<commit_after>/**
 * @fileoverview About this file
 */

/**
 * @type {Object}
 * @const
 */
var SugarCrm = {};

/**
 * Represents SugarCRM field boolean value.
 * @enum {string}
 */
SugarCrm.Boolean = {
  '0': '0',
  '1': '1'
};

/**
 * Represents SugarCRM name value list pair.
 * @constructor
 */
SugarCrm.NameValue = function() {};

/**
 * @type {string}
 */
SugarCrm.NameValue.prototype.name;

/**
 * @type {string}
 */
SugarCrm.NameValue.prototype.value;

/**
 * Represents SugarCRM name value list entry.
 * @constructor
 */
SugarCrm.NameValueEntry = function() {};

/**
 * @type {string}
 */
SugarCrm.NameValueEntry.prototype.id;

/**
 * @type {string}
 */
SugarCrm.NameValueEntry.prototype.module_name;

/**
 * @type {Object.<SugarCrm.NameValue>}
 */
SugarCrm.NameValueEntry.prototype.name_value_list;

/**
 * Represents SugarCRM email field.
 * @constructor
 */
SugarCrm.EmailField = function() {};

/**
 * @type {string}
 */
SugarCrm.EmailField.prototype.bean_id;

/**
 * @type {string}
 */
SugarCrm.EmailField.prototype.bean_module;

/**
 * @type {string}
 */
SugarCrm.EmailField.prototype.date_created;

/**
 * @type {string}
 */
SugarCrm.EmailField.prototype.date_modified;

/**
 * @type {SugarCrm.Boolean}
 */
SugarCrm.EmailField.prototype.deleted;

/**
 * @type {string}
 */
SugarCrm.EmailField.prototype.email_address;

/**
 * @type {string}
 */
SugarCrm.EmailField.prototype.email_address_caps;

/**
 * @type {string}
 */
SugarCrm.EmailField.prototype.email_address_id;

/**
 * @type {string}
 */
SugarCrm.EmailField.prototype.id;

/**
 * @type {SugarCrm.Boolean}
 */
SugarCrm.EmailField.prototype.invalid_email;

/**
 * @type {SugarCrm.Boolean}
 */
SugarCrm.EmailField.prototype.opt_out;

/**
 * @type {SugarCrm.Boolean}
 */
SugarCrm.EmailField.prototype.primary_address;

/**
 * @type {SugarCrm.Boolean}
 */
SugarCrm.EmailField.prototype.reply_to_address;

/**
 * CRUD Request result.
 * @interface
 */
SugarCrm.CrudResult = function() {};

/**
 * @type {number}
 */
SugarCrm.CrudResult.prototype.created;

/**
 * @type {number}
 */
SugarCrm.CrudResult.prototype.failed;

/**
 * @type {number}
 */
SugarCrm.CrudResult.prototype.deleted;

/**
 * Represents SugarCRM entry.
 * @constructor
 */
SugarCrm.Record = function() {};

/**
 * @type {string}
 */
SugarCrm.Record.prototype.id = '';

/**
 * @type {string}
 */
SugarCrm.Record.prototype.name = '';

/**
 * @type {string}
 */
SugarCrm.Record.prototype.date_entered = '';

/**
 * @type {string}
 */
SugarCrm.Record.prototype.date_modified = '';

/**
 * This field include in v10 REST result, but not in v4.
 * @type {string} module name.
 */
SugarCrm.Record.prototype._module = '';

/**
 * @type {string} either '1' or '0'
 */
SugarCrm.Record.prototype.deleted = '';

/**
 * @type {string}
 */
SugarCrm.Record.prototype.description = '';

/**
 * @type {string}
 */
SugarCrm.Record.prototype.assigned_user_id = '';

/**
 * Result record from query with scoring.
 * @constructor
 * @extends {SugarCrm.Record}
 */
SugarCrm.ScoredRecord = function() {};

/**
 * @type {number}
 */
SugarCrm.ScoredRecord.prototype._score = 0;

/**
 * Record in login respond.
 * @constructor
 * @extends {SugarCrm.Record}
 */
SugarCrm.LoginRecord = function() {};

/**
 * @type {string} "-99"
 */
SugarCrm.LoginRecord.prototype.user_currency_id = '';

/**
 * @type {string} "US Dollars"
 */
SugarCrm.LoginRecord.prototype.user_currency_name = '';

/**
 * @type {string} "."
 */
SugarCrm.LoginRecord.prototype.user_decimal_seperator = '';

/**
 * @type {string} "m/d/Y"
 */
SugarCrm.LoginRecord.prototype.user_default_dateformat = '';

/**
 * @type {?string}
 */
SugarCrm.LoginRecord.prototype.user_default_team_id = '';

/**
 * @type {string} "h:ia"
 */
SugarCrm.LoginRecord.prototype.user_default_timeformat = '';

/**
 * @type {string}
 */
SugarCrm.LoginRecord.prototype.user_id = '';

/**
 * @type {boolean}
 */
SugarCrm.LoginRecord.prototype.user_is_admin = false;

/**
 * Represents SugarCRM ModuleField.
 * @constructor
 */
SugarCrm.ModuleField = function() {};

/**
 * @type {boolean}
 */
SugarCrm.ModuleField.prototype.calculated = false;

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.group = '';

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.id_name = '';

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.label = '';

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.len = '';

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.name = '';

/**
 * @type {Object.<SugarCrm.NameValue>|undefined}
 */
SugarCrm.ModuleField.prototype.options = {};

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.related_module = '';

/**
 * @type {number}
 */
SugarCrm.ModuleField.prototype.required = 0;

/**
 * @type {string}
 */
SugarCrm.ModuleField.prototype.type = '';

/**
 * Represents SugarCRM LinkField.
 * @constructor
 */
SugarCrm.LinkField = function() {};

/**
 * @type {string} this is always 'link' ?
 */
SugarCrm.LinkField.prototype.type = '';

/**
 * @type {string} Module name
 */
SugarCrm.LinkField.prototype.module = '';

/**
 * @type {string} Link name. Same as its object hash key.
 */
SugarCrm.LinkField.prototype.name = '';

/**
 * Represents meta data for ModuleInfo.
 * @constructor
 */
SugarCrm.ModuleMeta = function() {};

/**
 * Color hex for module.
 * @type {string}
 */
SugarCrm.ModuleMeta.prototype.color = '';

/**
 * List of email fields.
 * @type {Array.<string>}
 */
SugarCrm.ModuleMeta.prototype.email_fields = [];

/**
 * Represents SugarCRM Acl.
 * @constructor
 */
SugarCrm.Acl = function() {};

/**
 * @type {boolean}
 */
SugarCrm.Acl.prototype.access = false;

/**
 * Enum of 'edit', 'delete', 'list', 'view', 'import', 'export'
 * @type {string}
 */
SugarCrm.Acl.prototype.action = '';

/**
 * Represents SugarCRM AvailableModule.
 * @constructor
 */
SugarCrm.AvailableModule = function() {};

/**
 * @type {boolean}
 */
SugarCrm.AvailableModule.prototype.favorite_enabled = false;

/**
 * @type {string}
 */
SugarCrm.AvailableModule.prototype.module_key = '';

/**
 * @type {string}
 */
SugarCrm.AvailableModule.prototype.module_label = '';

/**
 * @type {Array.<SugarCrm.Acl>}
 */
SugarCrm.AvailableModule.prototype.acls = [];

/**
 * Represents SugarCRM ModuleInfo.
 * @constructor
 */
SugarCrm.ModuleInfo = function() {};

/**
 * @type {SugarCrm.ModuleMeta}
 */
SugarCrm.ModuleInfo.prototype._meta = null;

/**
 * @type {string}
 */
SugarCrm.ModuleInfo.prototype.module_name = '';

/**
 * @type {string}
 */
SugarCrm.ModuleInfo.prototype.table_name = '';

/**
 * @type {!Object.<SugarCrm.ModuleField>|!Array.<SugarCrm.ModuleField>}
 */
SugarCrm.ModuleInfo.prototype.module_fields = [];

/**
 * @type {!Object.<SugarCrm.LinkField>}
 */
SugarCrm.ModuleInfo.prototype.link_fields = {};

/**
 * Represents query format in channel.
 * @constructor
 * @extends {CrmApp.ReqQuery}
 */
SugarCrm.Query = function() {};

/**
 * @type {Array.<SugarCrm.Record>} result of query.
 */
SugarCrm.Query.prototype.result = [];

/**
 * @constructor
 */
SugarCrm.ServerInfo = function() {};

/**
 * @type {(string|undefined)}
 */
SugarCrm.ServerInfo.prototype.flavor = '';

/**
 * @type {(string|undefined)}
 */
SugarCrm.ServerInfo.prototype.version = '';

/**
 * @type {(string|undefined)}
 */
SugarCrm.ServerInfo.prototype.uptime = '';

/**
 * @constructor
 */
SugarCrm.About = function() {};

/**
 * @type {(string|undefined)}
 */
SugarCrm.About.prototype.baseUrl = '';

/**
 * @type {string}
 */
SugarCrm.About.prototype.domain = '';

/**
 * @type {(string|undefined)}
 */
SugarCrm.About.prototype.userName = '';

/**
 * @type {(boolean|undefined)}
 */
SugarCrm.About.prototype.hostPermission = false;

/**
 * @type {(string|undefined)} authentication provider.
 */
SugarCrm.About.prototype.provider = '';

/**
 * @type {(boolean|undefined)}
 */
SugarCrm.About.prototype.isLogin = false;

/**
 * @constructor
 * @extends {SugarCrm.About}
 */
CrmApp.Credential = function() {};

/**
 * @type {string}
 */
CrmApp.Credential.prototype.password = '';

/**
 * @type {string|undefined}
 */
CrmApp.Credential.prototype.provider = '';

/**
 * @type {boolean|undefined}
 */
CrmApp.Credential.prototype.hashed = false;

/**
 * @constructor
 */
SugarCrm.Details = function() {};

/**
 * @type {SugarCrm.About}
 */
SugarCrm.Details.prototype.about = null;

/**
 * @type {SugarCrm.ServerInfo}
 */
SugarCrm.Details.prototype.serverInfo = null;

/**
 * @type {?SugarCrm.LoginRecord}
 */
SugarCrm.Details.prototype.loginInfo = null;

/**
 * @type {?CrmApp.Credential}
 */
SugarCrm.Details.prototype.credential = null;

/**
 * @type {Array<SugarCrm.AvailableModule>}
 */
SugarCrm.Details.prototype.availableModules = [];

/**
 * @type {Array.<SugarCrm.ModuleInfo>}
 */
SugarCrm.Details.prototype.modulesInfo = [];

/**
 * Query result for GData contact to SugarCRM records.
 * @interface
 */
SugarCrm.ContactSimilarityResult = function() {};

/**
 * @type {string}
 */
SugarCrm.ContactSimilarityResult.prototype.module;

/**
 * @type {string}
 */
SugarCrm.ContactSimilarityResult.prototype.index;

/**
 * @type {string}
 */
SugarCrm.ContactSimilarityResult.prototype.key;

/**
 * @type {Array<SugarCrm.Record>}
 */
SugarCrm.ContactSimilarityResult.prototype.result;

/**
 * Module name and id pair in relationship result.
 * @constructor
 */
SugarCrm.ModuleNameIdPair = function() {};

/**
 * @type {string}
 */
SugarCrm.ModuleNameIdPair.prototype.module_name;

/**
 * @type {string}
 */
SugarCrm.ModuleNameIdPair.prototype.id;

/**
 * Record format for relationships cache.
 * @constructor
 */
SugarCrm.Relationships = function() {};

/**
 * @type {number}
 */
SugarCrm.Relationships.prototype.modified;

/**
 * @type {SugarCrm.ModuleNameIdPair}
 */
SugarCrm.Relationships.prototype.to;

/**
 * @type {SugarCrm.ModuleNameIdPair}
 */
SugarCrm.Relationships.prototype.from;

/**
 * Represents SugarCRM entry.
 * @constructor
 */
SugarCrm.SyncCheckpoint = function() {};

/**
 * Lower value of updated timestamp.
 * @type {string}
 */
SugarCrm.SyncCheckpoint.prototype.lower = '';

/**
 * Lower record id.
 * @type {string}
 */
SugarCrm.SyncCheckpoint.prototype.lowerId = '';

/**
 * Upper value of updated timestamp.
 * @type {string}
 */
SugarCrm.SyncCheckpoint.prototype.upper = '';

/**
 * Upper record id.
 * @type {string}
 */
SugarCrm.SyncCheckpoint.prototype.upperId = '';

/**
 * Updated timestamp.
 * @type {number}
 */
SugarCrm.SyncCheckpoint.prototype.updated = 0;
<|endoftext|>


<commit_before>// VERSION 1.0.7

this.PDFJS = {
	// We need this to access protected properties, hidden from privileged code
	_unWrapped: null,
	get unWrap() {
		if(this._unWrapped && !isPDFJS) {
			this._unWrapped = null;
		}
		if(!this._unWrapped && isPDFJS) {
			this._unWrapped = XPCNativeWrapper.unwrap(content);
		}
		return this._unWrapped;
	},

	get viewerApplication() { return this.unWrap && this.unWrap.PDFViewerApplication; },
	get findController() { return this.viewerApplication && this.viewerApplication.findController; },

	getPageView: function(pIdx) {
		return this.viewerApplication.pdfViewer.getPageView(pIdx);
	},

	// make sure we don't keep old references to previous documents
	reset: function() {
		this._unWrapped = null;
		Finder.syncPDFJS();
		this.hijack();
	},

	forceMatch: function(match) {
		this.findController.selected.pageIdx = match.pIdx;
		this.findController.selected.matchIdx = match.mIdx;
		this.findController.offset.pageIdx = match.pIdx;
		this.findController.offset.matchIdx = match.mIdx;
		this.findController.offset.wrapped = false;
		this.findController.updatePage(match.pIdx);
	},

	receiver: function(data) {
		if(!this.findController) { return; }

		// this is needed so objects created in a privileged scope (the message data) can be used in unpriviliged scope (webpage/pdf reader)
		var fakeEvent = Cu.cloneInto(data, content);

		// We can't pass fakeEvent below and let the native method handle it because of a lot of limitations with its binds and stuff,
		// especially because we also replace PDFJS.findController.nextMatch. So we have to reproduce the whole thing here.
		// This turns out to be good because we can also apply our delay settings here as well.

		if(this.findController.state === null || fakeEvent.type !== 'findagain') {
			this.findController.dirtyMatch = true;
		}
		this.findController.state = fakeEvent;
		this.findController.updateUIState(this.unWrap.FindStates.FIND_PENDING);

		Timers.cancel('PDFFindTimeout');

		this.findController.firstPagePromise.then(() => {
			this.findController.extractText();

			if(this.findController.state.type === 'find') {
				Timers.init('PDFFindTimeout', () => {
					this.findController.nextMatch(this.findController.state.type);
				}, this.findController.state.delay);
			} else {
				this.findController.nextMatch(this.findController.state.type);
			}
		});
	},

	callOnPDFResults: function(aAction, aCurrentPage) {
		for(let l of Finder._listeners) {
			if(l.onPDFResult) {
				try { l.onPDFResult(aAction, aCurrentPage); }
				catch(ex) { Cu.reportError(ex); }
			}
		}
	},

	// We're hijacking PDFJS's findbar, as it is completely embedded in its DOM for remote browsers, so we can't use it with our methods.
	// Instead, we go around it, so the native (chrome) findbar can be used instead all the time.
	hijack: function() {
		if(!this.viewerApplication || this.viewerApplication[objName]) { return; }
		this.viewerApplication[objName] = true;

		// PDFViewerApplication keeps its own initialization promise closed to itself, so we have to improvize our own.
		var initViewer = new Promise.defer();
		this.viewerApplication.afterInitialized = initViewer.promise;
		if(this.viewerApplication.initialized) {
			initViewer.resolve();
		} else {
			delete this.viewerApplication.initialized;
			this.viewerApplication._initialized = false;
			this.viewerApplication.__defineGetter__('initialized', function() { return this._initialized; });
			this.viewerApplication.__defineSetter__('initialized', function(v) {
				if(v) { initViewer.resolve(); }
				return this._initialized = v;
			});
		}

		this.viewerApplication.afterInitialized.then(() => {
			// hide the viewer's own findbar, we use the native one
			setAttribute($('findbar'), 'hidden', 'true');
			setAttribute($('viewFind'), 'hidden', 'true');

			// we also need to use messages to update the integrated findbar, and not the viewer's own
			Piggyback.add('PDFJS', this.findController, 'updateUIState', function(state, previous) {
				message('PDFJS:State', { state: state, previous: previous });

				for(let l of Finder._listeners) {
					if(l.onPDFState) {
						try { l.onPDFState(state); }
						catch(ex) { Cu.reportError(ex); }
					}
				}
			});

			// The best place to report a "finished" PDF.JS find operation, so we can properly react to the found matches,
			// seems to be after each page's extractTextPromise(s) finishes, and also after the method reacting to it set by PDFFindController.nextMatch itself.
			// Don't use Piggyback, we can't send privileged JS objects (arguments) into untrusted methods
			// (even though it's PDFJS but it's still a technnicality I can't get around in Piggyback..)
			this.findController._nextMatch = this.findController.nextMatch;
			this.findController.nextMatch = function(aAction) {
				let selected = null;
				if(this.state.workAroundFind) {
					this.state.workAroundFind = false;
					if(this.selected.pageIdx > -1 && this.selected.matchIdx > -1) {
						selected = {
							pIdx: this.selected.pageIdx,
							mIdx: this.selected.matchIdx
						};
					}
				}

				this._nextMatch();

				// in case FIT wants a specific match found, make sure the new find operation run doesn't override it
				if(selected) {
					this.extractTextPromises[selected.pIdx].then(() => {
						if(selected.pIdx != this.selected.pageIdx || selected.mIdx != this.selected.matchIdx) {
							PDFJS.forceMatch(selected);
						}
					});
				}

				for(let pageIdx = 0; pageIdx < this.extractTextPromises.length; pageIdx++) {
					let extractPromise = this.extractTextPromises[pageIdx];
					extractPromise.then(() => {
						let currentPage = this.selected.pageIdx == pageIdx;
						PDFJS.callOnPDFResults(aAction, currentPage);
					});
				}
			};

			// ensure the viewer also knows it's using the integrated findbar, so the keypresses are properly handled
			this.viewerApplication._supportsIntegratedFind = this.viewerApplication.supportsIntegratedFind;
			delete this.viewerApplication.supportsIntegratedFind;
			this.viewerApplication.supportsIntegratedFind = true;
		});
	},

	unhijack: function() {
		Timers.cancel('PDFFindTimeout');

		if(!this.viewerApplication || !this.viewerApplication[objName]) { return; }
		delete this.viewerApplication[objName];

		delete this.viewerApplication.supportsIntegratedFind;
		this.viewerApplication.supportsIntegratedFind = this.viewerApplication._supportsIntegratedFind;
		delete this.viewerApplication._supportsIntegratedFind;

		Piggyback.revert('PDFJS', this.findController, 'updateUIState');

		this.findController.nextMatch = this.findController._nextMatch;
		delete this.findController._nextMatch;

		toggleAttribute($('findbar'), 'hidden', this.viewerApplication.supportsIntegratedFind);
		toggleAttribute($('viewFind'), 'hidden', this.viewerApplication.supportsIntegratedFind);

		var inited = this.viewerApplication.initialized;
		delete this.viewerApplication.afterInitialized;
		delete this.viewerApplication._initialized;
		delete this.viewerApplication.initialized;
		this.viewerApplication.initialized = inited;
	},

	onDOMContentLoaded: function(e) {
		// this is the content document of the loaded page.
		var doc = e.originalTarget;
		if(doc instanceof content.HTMLDocument) {
			// is this an inner frame?
			// Find the root document:
			while(doc.defaultView.frameElement) {
				doc = doc.defaultView.frameElement.ownerDocument;
			}

			if(doc == document) {
				this.reset();
			}
		}
	},

	onLocationChange: function(aWebProgress, aRequest, aLocation, aFlags) {
		if(aWebProgress.isTopLevel) {
			this.reset();
		}
	},

	QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference])
};

Modules.LOADMODULE = function() {
	Finder.PDFJS = null;
	Finder.__defineGetter__('isPDFJS',
		function() { return (!viewSource && document && document.contentType == 'application/pdf' && document.baseURI == 'resource://pdf.js/web/'); });

	// append getters into this object for all the info that should be passed onto chrome
	Finder._syncPDFJS = {};
	Finder.syncPDFJS = function() {
		// do aSync so we don't fire more than necessary
		Timers.init('syncPDFJS', () => {
			this.PDFJS = null;

			if(this.isPDFJS) {
				this.PDFJS = {};
				for(var s in this._syncPDFJS) {
					this.PDFJS[s] = this._syncPDFJS[s];
				}
			}

			message('PDFJS:Result', this.PDFJS);

			for(let l of this._listeners) {
				if(l.onPDFJS) {
					try { l.onPDFJS(); }
					catch(ex) { Cu.reportError(ex); }
				}
			}
		}, 0);
	};

	RemoteFinderListener.addMessage('PDFJS:Event', data => {
		PDFJS.receiver(data);
	});

	RemoteFinderListener.addMessage('PDFJS:Sync', () => {
		Finder.syncPDFJS();
	});

	WebProgress.add(PDFJS, Ci.nsIWebProgress.NOTIFY_LOCATION);
	DOMContentLoaded.add(PDFJS);

	PDFJS.reset();
};

Modules.UNLOADMODULE = function() {
	WebProgress.remove(PDFJS, Ci.nsIWebProgress.NOTIFY_LOCATION);
	DOMContentLoaded.remove(PDFJS);

	RemoteFinderListener.removeMessage('PDFJS:Event');
	RemoteFinderListener.removeMessage('PDFJS:Sync');

	delete Finder.PDFJS;
	delete Finder.isPDFJS;
	delete Finder._syncPDFJS;
	delete Finder.syncPDFJS;

	PDFJS.unhijack();
};
<commit_msg>bugfix PDFJS jumping to match while scrolling<commit_after>// VERSION 1.0.8

this.PDFJS = {
	// We need this to access protected properties, hidden from privileged code
	_unWrapped: null,
	get unWrap() {
		if(this._unWrapped && !isPDFJS) {
			this._unWrapped = null;
		}
		if(!this._unWrapped && isPDFJS) {
			this._unWrapped = XPCNativeWrapper.unwrap(content);
		}
		return this._unWrapped;
	},

	get viewerApplication() { return this.unWrap && this.unWrap.PDFViewerApplication; },
	get findController() { return this.viewerApplication && this.viewerApplication.findController; },

	getPageView: function(pIdx) {
		return this.viewerApplication.pdfViewer.getPageView(pIdx);
	},

	// make sure we don't keep old references to previous documents
	reset: function() {
		this._unWrapped = null;
		Finder.syncPDFJS();
		this.hijack();
	},

	forceMatch: function(match) {
		this.findController.selected.pageIdx = match.pIdx;
		this.findController.selected.matchIdx = match.mIdx;
		this.findController.offset.pageIdx = match.pIdx;
		this.findController.offset.matchIdx = match.mIdx;
		this.findController.offset.wrapped = false;
		this.findController.updatePage(match.pIdx);
	},

	receiver: function(data) {
		if(!this.findController) { return; }

		// this is needed so objects created in a privileged scope (the message data) can be used in unpriviliged scope (webpage/pdf reader)
		var fakeEvent = Cu.cloneInto(data, content);

		// We can't pass fakeEvent below and let the native method handle it because of a lot of limitations with its binds and stuff,
		// especially because we also replace PDFJS.findController.nextMatch. So we have to reproduce the whole thing here.
		// This turns out to be good because we can also apply our delay settings here as well.

		if(this.findController.state === null || fakeEvent.type !== 'findagain') {
			this.findController.dirtyMatch = true;
		}
		this.findController.state = fakeEvent;
		this.findController.updateUIState(this.unWrap.FindStates.FIND_PENDING);

		Timers.cancel('PDFFindTimeout');

		this.findController.firstPagePromise.then(() => {
			this.findController.extractText();

			if(this.findController.state.type === 'find') {
				Timers.init('PDFFindTimeout', () => {
					this.findController.nextMatch(this.findController.state.type);
				}, this.findController.state.delay);
			} else {
				this.findController.nextMatch(this.findController.state.type);
			}
		});
	},

	callOnPDFResults: function(aAction, aCurrentPage) {
		for(let l of Finder._listeners) {
			if(l.onPDFResult) {
				try { l.onPDFResult(aAction, aCurrentPage); }
				catch(ex) { Cu.reportError(ex); }
			}
		}
	},

	// We're hijacking PDFJS's findbar, as it is completely embedded in its DOM for remote browsers, so we can't use it with our methods.
	// Instead, we go around it, so the native (chrome) findbar can be used instead all the time.
	hijack: function() {
		if(!this.viewerApplication || this.viewerApplication[objName]) { return; }
		this.viewerApplication[objName] = true;

		// PDFViewerApplication keeps its own initialization promise closed to itself, so we have to improvize our own.
		var initViewer = new Promise.defer();
		this.viewerApplication.afterInitialized = initViewer.promise;
		if(this.viewerApplication.initialized) {
			initViewer.resolve();
		} else {
			delete this.viewerApplication.initialized;
			this.viewerApplication._initialized = false;
			this.viewerApplication.__defineGetter__('initialized', function() { return this._initialized; });
			this.viewerApplication.__defineSetter__('initialized', function(v) {
				if(v) { initViewer.resolve(); }
				return this._initialized = v;
			});
		}

		this.viewerApplication.afterInitialized.then(() => {
			// hide the viewer's own findbar, we use the native one
			setAttribute($('findbar'), 'hidden', 'true');
			setAttribute($('viewFind'), 'hidden', 'true');

			// we also need to use messages to update the integrated findbar, and not the viewer's own
			Piggyback.add('PDFJS', this.findController, 'updateUIState', function(state, previous) {
				message('PDFJS:State', { state: state, previous: previous });

				for(let l of Finder._listeners) {
					if(l.onPDFState) {
						try { l.onPDFState(state); }
						catch(ex) { Cu.reportError(ex); }
					}
				}
			});

			Piggyback.add('PDFJS', this.findController, 'updateMatchPosition', function(pageIndex, index, elements, beginIdx, endIdx) {
				// Only scroll matches into view when a find actually runs, and not when the document is scrolled (rendering pages triggers this as well)
				if(!this.showCurrentMatch) { return; }

				if(this.selected.matchIdx === index && this.selected.pageIdx === pageIndex) {
					let spot = Cu.cloneInto({
						top: -50, // FIND_SCROLL_OFFSET_TOP
						left: -400 // FIND_SCROLL_OFFSET_LEFT
					}, PDFJS.unWrap);
					PDFJS.unWrap.scrollIntoView(elements[beginIdx], spot, true);
					this.showCurrentMatch = false;
				}
			});

			// The best place to report a "finished" PDF.JS find operation, so we can properly react to the found matches,
			// seems to be after each page's extractTextPromise(s) finishes, and also after the method reacting to it set by PDFFindController.nextMatch itself.
			// Don't use Piggyback, we can't send privileged JS objects (arguments) into untrusted methods
			// (even though it's PDFJS but it's still a technnicality I can't get around in Piggyback..)
			this.findController._nextMatch = this.findController.nextMatch;
			this.findController.nextMatch = function(aAction) {
				let selected = null;
				if(this.state.workAroundFind) {
					this.state.workAroundFind = false;
					if(this.selected.pageIdx > -1 && this.selected.matchIdx > -1) {
						selected = {
							pIdx: this.selected.pageIdx,
							mIdx: this.selected.matchIdx
						};
					}
				}

				this.showCurrentMatch = true;
				this._nextMatch();

				// in case FIT wants a specific match found, make sure the new find operation run doesn't override it
				if(selected) {
					this.extractTextPromises[selected.pIdx].then(() => {
						if(selected.pIdx != this.selected.pageIdx || selected.mIdx != this.selected.matchIdx) {
							PDFJS.forceMatch(selected);
						}
					});
				}

				for(let pageIdx = 0; pageIdx < this.extractTextPromises.length; pageIdx++) {
					let extractPromise = this.extractTextPromises[pageIdx];
					extractPromise.then(() => {
						let currentPage = this.selected.pageIdx == pageIdx;
						PDFJS.callOnPDFResults(aAction, currentPage);
					});
				}
			};

			// ensure the viewer also knows it's using the integrated findbar, so the keypresses are properly handled
			this.viewerApplication._supportsIntegratedFind = this.viewerApplication.supportsIntegratedFind;
			delete this.viewerApplication.supportsIntegratedFind;
			this.viewerApplication.supportsIntegratedFind = true;
		});
	},

	unhijack: function() {
		Timers.cancel('PDFFindTimeout');

		if(!this.viewerApplication || !this.viewerApplication[objName]) { return; }
		delete this.viewerApplication[objName];

		delete this.viewerApplication.supportsIntegratedFind;
		this.viewerApplication.supportsIntegratedFind = this.viewerApplication._supportsIntegratedFind;
		delete this.viewerApplication._supportsIntegratedFind;

		Piggyback.revert('PDFJS', this.findController, 'updateUIState');
		Piggyback.revert('PDFJS', this.findController, 'updateMatchPosition');
		delete this.findController.showCurrentMatch;

		this.findController.nextMatch = this.findController._nextMatch;
		delete this.findController._nextMatch;

		toggleAttribute($('findbar'), 'hidden', this.viewerApplication.supportsIntegratedFind);
		toggleAttribute($('viewFind'), 'hidden', this.viewerApplication.supportsIntegratedFind);

		var inited = this.viewerApplication.initialized;
		delete this.viewerApplication.afterInitialized;
		delete this.viewerApplication._initialized;
		delete this.viewerApplication.initialized;
		this.viewerApplication.initialized = inited;
	},

	onDOMContentLoaded: function(e) {
		// this is the content document of the loaded page.
		var doc = e.originalTarget;
		if(doc instanceof content.HTMLDocument) {
			// is this an inner frame?
			// Find the root document:
			while(doc.defaultView.frameElement) {
				doc = doc.defaultView.frameElement.ownerDocument;
			}

			if(doc == document) {
				this.reset();
			}
		}
	},

	onLocationChange: function(aWebProgress, aRequest, aLocation, aFlags) {
		if(aWebProgress.isTopLevel) {
			this.reset();
		}
	},

	QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference])
};

Modules.LOADMODULE = function() {
	Finder.PDFJS = null;
	Finder.__defineGetter__('isPDFJS',
		function() { return (!viewSource && document && document.contentType == 'application/pdf' && document.baseURI == 'resource://pdf.js/web/'); });

	// append getters into this object for all the info that should be passed onto chrome
	Finder._syncPDFJS = {};
	Finder.syncPDFJS = function() {
		// do aSync so we don't fire more than necessary
		Timers.init('syncPDFJS', () => {
			this.PDFJS = null;

			if(this.isPDFJS) {
				this.PDFJS = {};
				for(var s in this._syncPDFJS) {
					this.PDFJS[s] = this._syncPDFJS[s];
				}
			}

			message('PDFJS:Result', this.PDFJS);

			for(let l of this._listeners) {
				if(l.onPDFJS) {
					try { l.onPDFJS(); }
					catch(ex) { Cu.reportError(ex); }
				}
			}
		}, 0);
	};

	RemoteFinderListener.addMessage('PDFJS:Event', data => {
		PDFJS.receiver(data);
	});

	RemoteFinderListener.addMessage('PDFJS:Sync', () => {
		Finder.syncPDFJS();
	});

	WebProgress.add(PDFJS, Ci.nsIWebProgress.NOTIFY_LOCATION);
	DOMContentLoaded.add(PDFJS);

	PDFJS.reset();
};

Modules.UNLOADMODULE = function() {
	WebProgress.remove(PDFJS, Ci.nsIWebProgress.NOTIFY_LOCATION);
	DOMContentLoaded.remove(PDFJS);

	RemoteFinderListener.removeMessage('PDFJS:Event');
	RemoteFinderListener.removeMessage('PDFJS:Sync');

	delete Finder.PDFJS;
	delete Finder.isPDFJS;
	delete Finder._syncPDFJS;
	delete Finder.syncPDFJS;

	PDFJS.unhijack();
};
<|endoftext|>


<commit_before>
// Copyright 2010 WebDriver committers
// Copyright 2010 Google Inc.
//
// 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.

/**
 * @fileoverview Atoms for simulating user actions against the browser window.
 *
 */

goog.provide('bot.window');

goog.require('bot');
goog.require('bot.Error');
goog.require('bot.ErrorCode');
goog.require('goog.array');
goog.require('goog.dom');
goog.require('goog.math.Size');
goog.require('goog.math.Coordinate');
goog.require('goog.userAgent');

/**
 * Whether the value of history.length includes a newly loaded page. If not,
 * after a new page load history.length is the number of pages that have loaded,
 * minus 1, but becomes the total number of pages on a subsequent back() call.
 *
 * @const
 * @type {boolean}
 * @private
 */
bot.window.HISTORY_LENGTH_INCLUDES_NEW_PAGE_ = !goog.userAgent.IE &&
    !goog.userAgent.OPERA;

/**
 * Whether value of history.length includes the pages ahead of the current one
 * in the history. If not, history.length equals the number of prior pages.
 * Here is the WebKit bug for this behavior that was fixed by version 533:
 * https://bugs.webkit.org/show_bug.cgi?id=24472
 *
 * @const
 * @type {boolean}
 * @private
 */
bot.window.HISTORY_LENGTH_INCLUDES_FORWARD_PAGES_ = !goog.userAgent.OPERA &&
    (!goog.userAgent.WEBKIT || goog.userAgent.isVersion('533'));

/**
 * Go back in the browser history. The number of pages to go back can
 * optionally be specified and defaults to 1.
 *
 * @param {number=} opt_numPages Number of pages to go back.
 */
bot.window.back = function(opt_numPages) {
  // Relax the upper bound by one for browsers that do not count
  // newly loaded pages towards the value of window.history.length.
  var maxPages = bot.window.HISTORY_LENGTH_INCLUDES_NEW_PAGE_ ?
      bot.getWindow().history.length - 1 : bot.getWindow().history.length;
  var numPages = bot.window.checkNumPages_(maxPages, opt_numPages);
  bot.getWindow().history.go(-numPages);
};

/**
 * Go forward in the browser history. The number of pages to go forward can
 * optionally be specified and defaults to 1.
 *
 * @param {number=} opt_numPages Number of pages to go forward.
 */
bot.window.forward = function(opt_numPages) {
  // Do not check the upper bound (use null for infinity) for browsers that
  // do not count forward pages towards the value of window.history.length.
  var maxPages = bot.window.HISTORY_LENGTH_INCLUDES_FORWARD_PAGES_ ?
      bot.getWindow().history.length - 1 : null;
  var numPages = bot.window.checkNumPages_(maxPages, opt_numPages);
  bot.getWindow().history.go(numPages);
};

/**
 * @param {?number} maxPages Upper bound on number of pages; null for infinity.
 * @param {number=} opt_numPages Number of pages to move in history.
 * @return {number} Correct number of pages to move in history.
 * @private
 */
bot.window.checkNumPages_ = function(maxPages, opt_numPages) {
  var numPages = goog.isDef(opt_numPages) ? opt_numPages : 1;
  if (numPages <= 0) {
    throw new bot.Error(bot.ErrorCode.UNKNOWN_ERROR,
        'number of pages must be positive');
  }
  if (maxPages !== null && numPages > maxPages) {
    throw new bot.Error(bot.ErrorCode.UNKNOWN_ERROR,
        'number of pages must be less than the length of the browser history');
  }
  return numPages;
};

/**
 * Determine the size of the window that a user could interact with. This will
 * be the greatest of document.body.(width|scrollWidth), the same for
 * document.documentElement or the size of the viewport.
 *
 * @param {!Window=} opt_win Window to determine the size of. Defaults to
 *   bot.getWindow().
 * @return {!goog.math.Size} The calculated size.
 */
bot.window.getInteractableSize = function(opt_win) {
  var win = opt_win || bot.getWindow();
  var doc = win.document;
  var elem = doc.documentElement;
  var body = doc.body;

  var widths = [
      elem.clientWidth, elem.scrollWidth, elem.offsetWidth,
      body.scrollWidth, body.offsetWidth
  ];
  var heights = [
      elem.clientHeight, elem.scrollHeight, elem.offsetHeight,
      body.scrollHeight, body.offsetHeight
  ];

  var sortFunc = function(a, b) {
    return a - b;
  };

  var width = /**@type {number}*/goog.array.peek(widths.sort(sortFunc)) || 0;
  var height = /**@type {number}*/goog.array.peek(heights.sort(sortFunc)) || 0;

  return new goog.math.Size(width, height);
};

/**
 * Determine the outer size of the window.
 *
 * @param {!Window=} opt_win Window to determine the size of. Defaults to
 *   bot.getWindow().
 * @return {!goog.math.Size} The calculated size.
 */
bot.window.getSize = function(opt_win) {
  var win = opt_win || bot.getWindow();

  var width = win.outerWidth;
  var height = win.outerHeight;

  return new goog.math.Size(width, height);
};

/** Set the outer size of the window.
 *
 * @param {!goog.math.Size} size The new window size.
 * @param {!Window=} opt_win Window to determine the size of. Defaults to
 *   bot.getWindow().
 */
bot.window.setSize = function(size, opt_win) {
  var win = opt_win || bot.getWindow();

  win.resizeTo(size.width, size.height);
};

/** Get the position of the window.
 *
 * @param {!Window=} opt_win Window to determine the position of. Defaults to
 *   bot.getWindow().
 * @return {!goog.math.Coordinate} The position of the window.
 */
bot.window.getPosition = function(opt_win) {
  var win = opt_win || bot.getWindow();
  var x, y;

  if (goog.userAgent.IE) {
    x = win.screenLeft;
    y = win.screenTop;
  } else {
    x = win.screenX;
    y = win.screenY;
  }

  return new goog.math.Coordinate(x, y);
};

/** Set the position of the window.
 *
 * @param {!goog.math.Coordinate} targetPosition The target position.
 * @param {!Window=} opt_win Window to set the position of. Defaults to
 *   bot.getWindow().
 */
bot.window.setPosition = function(targetPosition, opt_win) {
  var win = opt_win || bot.getWindow();
  win.moveTo(targetPosition.x, targetPosition.y);
};<commit_msg>JariBakken: Remove IE branch from bot.window.getPosition<commit_after>
// Copyright 2010 WebDriver committers
// Copyright 2010 Google Inc.
//
// 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.

/**
 * @fileoverview Atoms for simulating user actions against the browser window.
 *
 */

goog.provide('bot.window');

goog.require('bot');
goog.require('bot.Error');
goog.require('bot.ErrorCode');
goog.require('goog.array');
goog.require('goog.dom');
goog.require('goog.math.Size');
goog.require('goog.math.Coordinate');
goog.require('goog.userAgent');

/**
 * Whether the value of history.length includes a newly loaded page. If not,
 * after a new page load history.length is the number of pages that have loaded,
 * minus 1, but becomes the total number of pages on a subsequent back() call.
 *
 * @const
 * @type {boolean}
 * @private
 */
bot.window.HISTORY_LENGTH_INCLUDES_NEW_PAGE_ = !goog.userAgent.IE &&
    !goog.userAgent.OPERA;

/**
 * Whether value of history.length includes the pages ahead of the current one
 * in the history. If not, history.length equals the number of prior pages.
 * Here is the WebKit bug for this behavior that was fixed by version 533:
 * https://bugs.webkit.org/show_bug.cgi?id=24472
 *
 * @const
 * @type {boolean}
 * @private
 */
bot.window.HISTORY_LENGTH_INCLUDES_FORWARD_PAGES_ = !goog.userAgent.OPERA &&
    (!goog.userAgent.WEBKIT || goog.userAgent.isVersion('533'));

/**
 * Go back in the browser history. The number of pages to go back can
 * optionally be specified and defaults to 1.
 *
 * @param {number=} opt_numPages Number of pages to go back.
 */
bot.window.back = function(opt_numPages) {
  // Relax the upper bound by one for browsers that do not count
  // newly loaded pages towards the value of window.history.length.
  var maxPages = bot.window.HISTORY_LENGTH_INCLUDES_NEW_PAGE_ ?
      bot.getWindow().history.length - 1 : bot.getWindow().history.length;
  var numPages = bot.window.checkNumPages_(maxPages, opt_numPages);
  bot.getWindow().history.go(-numPages);
};

/**
 * Go forward in the browser history. The number of pages to go forward can
 * optionally be specified and defaults to 1.
 *
 * @param {number=} opt_numPages Number of pages to go forward.
 */
bot.window.forward = function(opt_numPages) {
  // Do not check the upper bound (use null for infinity) for browsers that
  // do not count forward pages towards the value of window.history.length.
  var maxPages = bot.window.HISTORY_LENGTH_INCLUDES_FORWARD_PAGES_ ?
      bot.getWindow().history.length - 1 : null;
  var numPages = bot.window.checkNumPages_(maxPages, opt_numPages);
  bot.getWindow().history.go(numPages);
};

/**
 * @param {?number} maxPages Upper bound on number of pages; null for infinity.
 * @param {number=} opt_numPages Number of pages to move in history.
 * @return {number} Correct number of pages to move in history.
 * @private
 */
bot.window.checkNumPages_ = function(maxPages, opt_numPages) {
  var numPages = goog.isDef(opt_numPages) ? opt_numPages : 1;
  if (numPages <= 0) {
    throw new bot.Error(bot.ErrorCode.UNKNOWN_ERROR,
        'number of pages must be positive');
  }
  if (maxPages !== null && numPages > maxPages) {
    throw new bot.Error(bot.ErrorCode.UNKNOWN_ERROR,
        'number of pages must be less than the length of the browser history');
  }
  return numPages;
};

/**
 * Determine the size of the window that a user could interact with. This will
 * be the greatest of document.body.(width|scrollWidth), the same for
 * document.documentElement or the size of the viewport.
 *
 * @param {!Window=} opt_win Window to determine the size of. Defaults to
 *   bot.getWindow().
 * @return {!goog.math.Size} The calculated size.
 */
bot.window.getInteractableSize = function(opt_win) {
  var win = opt_win || bot.getWindow();
  var doc = win.document;
  var elem = doc.documentElement;
  var body = doc.body;

  var widths = [
      elem.clientWidth, elem.scrollWidth, elem.offsetWidth,
      body.scrollWidth, body.offsetWidth
  ];
  var heights = [
      elem.clientHeight, elem.scrollHeight, elem.offsetHeight,
      body.scrollHeight, body.offsetHeight
  ];

  var sortFunc = function(a, b) {
    return a - b;
  };

  var width = /**@type {number}*/goog.array.peek(widths.sort(sortFunc)) || 0;
  var height = /**@type {number}*/goog.array.peek(heights.sort(sortFunc)) || 0;

  return new goog.math.Size(width, height);
};

/**
 * Determine the outer size of the window.
 *
 * @param {!Window=} opt_win Window to determine the size of. Defaults to
 *   bot.getWindow().
 * @return {!goog.math.Size} The calculated size.
 */
bot.window.getSize = function(opt_win) {
  var win = opt_win || bot.getWindow();

  var width = win.outerWidth;
  var height = win.outerHeight;

  return new goog.math.Size(width, height);
};

/** Set the outer size of the window.
 *
 * @param {!goog.math.Size} size The new window size.
 * @param {!Window=} opt_win Window to determine the size of. Defaults to
 *   bot.getWindow().
 */
bot.window.setSize = function(size, opt_win) {
  var win = opt_win || bot.getWindow();

  win.resizeTo(size.width, size.height);
};

/** Get the position of the window.
 *
 * @param {!Window=} opt_win Window to determine the position of. Defaults to
 *   bot.getWindow().
 * @return {!goog.math.Coordinate} The position of the window.
 */
bot.window.getPosition = function(opt_win) {
  var win = opt_win || bot.getWindow();

  var x = win.screenX;
  var y = win.screenY;

  return new goog.math.Coordinate(x, y);
};

/** Set the position of the window.
 *
 * @param {!goog.math.Coordinate} targetPosition The target position.
 * @param {!Window=} opt_win Window to set the position of. Defaults to
 *   bot.getWindow().
 */
bot.window.setPosition = function(targetPosition, opt_win) {
  var win = opt_win || bot.getWindow();
  win.moveTo(targetPosition.x, targetPosition.y);
};<|endoftext|>


<commit_before>/**
 * Filter for producing HAML code from abbreviation.
 * @author Sergey Chikuyonok (serge.che@gmail.com)
 * @link http://chikuyonok.ru
 * @constructor
 * @memberOf __hamlFilterDefine
 * @param {Function} require
 * @param {Underscore} _
 */
zen_coding.exec(function(require, _) {
	var childToken = '${child}';

	/**
	 * Creates HAML attributes string from tag according to profile settings
	 * @param {ZenNode} tag
	 * @param {Object} profile
	 */
	function makeAttributesString(tag, profile) {
		var p = require('profile');
		// make attribute string
		var attrs = '';
		var otherAttrs = [];
		var attrQuote = profile.attr_quotes == 'single' ? "'" : '"';
		var cursor = profile.place_cursor ? require('utils').getCaretPlaceholder() : '';

		_.each(tag.attributes, function(a) {
			switch (a.name.toLowerCase()) {
				// use short notation for ID and CLASS attributes
				case 'id':
					attrs += '#' + (a.value || cursor);
					break;
				case 'class':
					attrs += '.' + (a.value || cursor);
					break;
				// process other attributes
				default:
					var attrName = p.stringCase(a.name, profile.attr_case);
					otherAttrs.push(':' +attrName + ' => ' + attrQuote + (a.value || cursor) + attrQuote);
			}
		});

		if (otherAttrs.length)
			attrs += '{' + otherAttrs.join(', ') + '}';

		return attrs;
	}

	/**
	 * Processes element with snippet type
	 * @param {ZenNode} item
	 * @param {Object} profile
	 * @param {Number} level Depth level
	 */
	function processSnippet(item, profile, level) {
		var data = item.source.value;
		var utils = require('utils');

		if (!data)
			// snippet wasn't found, process it as tag
			return processTag(item, profile, level);

		var parts = data.split(childToken);
		var start = parts[0] || '';
		var end = parts[1] || '';
		var padding = item.parent ? item.parent.padding : '';

		item.start = item.start.replace('%s', utils.padString(start, padding));
		item.end = item.end.replace('%s', utils.padString(end, padding));

		var cb = require('tabStops').variablesResolver(item);
		item.start = utils.replaceVariables(item.start, cb);
		item.end = utils.replaceVariables(item.end, cb);

		return item;
	}

	/**
	 * Test if passed node has block-level sibling element
	 * @param {ZenNode} item
	 * @return {Boolean}
	 */
	function hasBlockSibling(item) {
		return item.parent && item.parent.hasBlockChildren();
	}

	/**
	 * Processes element with tag type
	 * @param {ZenNode} item
	 * @param {Object} profile
	 * @param {Number} level Depth level
	 */
	function processTag(item, profile, level) {
		if (!item.name)
			// looks like it's root element
			return item;

		var p = require('profile');
		var attrs = makeAttributesString(item, profile);
		var cursor = profile.place_cursor ? require('utils').getCaretPlaceholder() : '';
		var isUnary = item.isUnary() && !item.children.length;
		var selfClosing = profile.self_closing_tag && isUnary ? '/' : '';
		var start= '';

		// define tag name
		var tagName = '%' + p.stringCase(item.name, profile.tag_case);
		if (tagName.toLowerCase() == '%div' && attrs && attrs.indexOf('{') == -1)
			// omit div tag
			tagName = '';

		item.end = '';
		start = tagName + attrs + selfClosing + ' ';

		var placeholder = '%s';
		// We can't just replace placeholder with new value because
		// JavaScript will treat double $ character as a single one, assuming
		// we're using RegExp literal.
		var pos = item.start.indexOf(placeholder);
		item.start = item.start.substring(0, pos) + start + item.start.substring(pos + placeholder.length);

		if (!item.children.length && !isUnary)
			item.start += cursor;

		return item;
	}

	/**
	 * Processes simplified tree, making it suitable for output as HTML structure
	 * @param {ZenNode} tree
	 * @param {Object} profile
	 * @param {Number} level Depth level
	 */
	require('filters').add('haml', function process(tree, profile, level) {
		level = level || 0;
		/** @type zen_coding.utils */
		var utils = require('utils');
		var editorUtils = require('editorUtils');
		var elements = require('elements');

		if (level == 0) {
			// preformat tree
			tree = require('filters').apply(tree, '_format', profile);
			require('tabStops').resetPlaceholderCounter();
		}

		_.each(tree.children, function(item) {
			item = elements.is(item.source, 'parsedElement')
				? processTag(item, profile, level)
				: processSnippet(item, profile, level);

			// replace counters
			var counter = editorUtils.getCounterForNode(item);
			item.start = utils.unescapeText(utils.replaceCounter(item.start, counter));
			item.end = utils.unescapeText(utils.replaceCounter(item.end, counter));

			process(item, profile, level + 1);
		});

		return tree;
	});
});<commit_msg>Fixed #36: Bug with multiple classes in HAML filter<commit_after>/**
 * Filter for producing HAML code from abbreviation.
 * @author Sergey Chikuyonok (serge.che@gmail.com)
 * @link http://chikuyonok.ru
 * @constructor
 * @memberOf __hamlFilterDefine
 * @param {Function} require
 * @param {Underscore} _
 */
zen_coding.exec(function(require, _) {
	var childToken = '${child}';

	function transformClassName(className) {
		return require('utils').trim(className).replace(/\s+/g, '.');
	}

	/**
	 * Creates HAML attributes string from tag according to profile settings
	 * @param {ZenNode} tag
	 * @param {Object} profile
	 */
	function makeAttributesString(tag, profile) {
		var p = require('profile');
		// make attribute string
		var attrs = '';
		var otherAttrs = [];
		var attrQuote = profile.attr_quotes == 'single' ? "'" : '"';
		var cursor = profile.place_cursor ? require('utils').getCaretPlaceholder() : '';

		_.each(tag.attributes, function(a) {
			switch (a.name.toLowerCase()) {
				// use short notation for ID and CLASS attributes
				case 'id':
					attrs += '#' + (a.value || cursor);
					break;
				case 'class':
					attrs += '.' + transformClassName(a.value || cursor);
					break;
				// process other attributes
				default:
					var attrName = p.stringCase(a.name, profile.attr_case);
					otherAttrs.push(':' +attrName + ' => ' + attrQuote + (a.value || cursor) + attrQuote);
			}
		});

		if (otherAttrs.length)
			attrs += '{' + otherAttrs.join(', ') + '}';

		return attrs;
	}

	/**
	 * Processes element with snippet type
	 * @param {ZenNode} item
	 * @param {Object} profile
	 * @param {Number} level Depth level
	 */
	function processSnippet(item, profile, level) {
		var data = item.source.value;
		var utils = require('utils');

		if (!data)
			// snippet wasn't found, process it as tag
			return processTag(item, profile, level);

		var parts = data.split(childToken);
		var start = parts[0] || '';
		var end = parts[1] || '';
		var padding = item.parent ? item.parent.padding : '';

		item.start = item.start.replace('%s', utils.padString(start, padding));
		item.end = item.end.replace('%s', utils.padString(end, padding));

		var cb = require('tabStops').variablesResolver(item);
		item.start = utils.replaceVariables(item.start, cb);
		item.end = utils.replaceVariables(item.end, cb);

		return item;
	}

	/**
	 * Test if passed node has block-level sibling element
	 * @param {ZenNode} item
	 * @return {Boolean}
	 */
	function hasBlockSibling(item) {
		return item.parent && item.parent.hasBlockChildren();
	}

	/**
	 * Processes element with tag type
	 * @param {ZenNode} item
	 * @param {Object} profile
	 * @param {Number} level Depth level
	 */
	function processTag(item, profile, level) {
		if (!item.name)
			// looks like it's root element
			return item;

		var p = require('profile');
		var attrs = makeAttributesString(item, profile);
		var cursor = profile.place_cursor ? require('utils').getCaretPlaceholder() : '';
		var isUnary = item.isUnary() && !item.children.length;
		var selfClosing = profile.self_closing_tag && isUnary ? '/' : '';
		var start= '';

		// define tag name
		var tagName = '%' + p.stringCase(item.name, profile.tag_case);
		if (tagName.toLowerCase() == '%div' && attrs && attrs.indexOf('{') == -1)
			// omit div tag
			tagName = '';

		item.end = '';
		start = tagName + attrs + selfClosing + ' ';

		var placeholder = '%s';
		// We can't just replace placeholder with new value because
		// JavaScript will treat double $ character as a single one, assuming
		// we're using RegExp literal.
		var pos = item.start.indexOf(placeholder);
		item.start = item.start.substring(0, pos) + start + item.start.substring(pos + placeholder.length);

		if (!item.children.length && !isUnary)
			item.start += cursor;

		return item;
	}

	/**
	 * Processes simplified tree, making it suitable for output as HTML structure
	 * @param {ZenNode} tree
	 * @param {Object} profile
	 * @param {Number} level Depth level
	 */
	require('filters').add('haml', function process(tree, profile, level) {
		level = level || 0;
		/** @type zen_coding.utils */
		var utils = require('utils');
		var editorUtils = require('editorUtils');
		var elements = require('elements');

		if (level == 0) {
			// preformat tree
			tree = require('filters').apply(tree, '_format', profile);
			require('tabStops').resetPlaceholderCounter();
		}

		_.each(tree.children, function(item) {
			item = elements.is(item.source, 'parsedElement')
				? processTag(item, profile, level)
				: processSnippet(item, profile, level);

			// replace counters
			var counter = editorUtils.getCounterForNode(item);
			item.start = utils.unescapeText(utils.replaceCounter(item.start, counter));
			item.end = utils.unescapeText(utils.replaceCounter(item.end, counter));

			process(item, profile, level + 1);
		});

		return tree;
	});
});<|endoftext|>


<commit_before>
 // var global_url      = "http://datos.rtve.es/elecciones/autonomicas-municipales/data";
 // var tiles_pngs_url  = "http://datos{n}.rtve.es/elecciones/autonomicas-municipales/data/tiles/";
  var global_url = "http://ec2-50-16-13-57.compute-1.amazonaws.com/data"
  var tiles_pngs_url  = "http://ec2-50-16-13-57.compute-1.amazonaws.com/data/tiles/";
  var proxy_url       = "proxy.php";
  var tiles_version   = "live"; //"current"; //3;
  var bubbles_version = "current"; //10;
  var gmaps_version   = "current"; //4;
  var graph_version   = "current"; //"v7";
  var ua              = $.browser;
  var ie_             = false;

  if (window.location.hostname == "localhost" || window.location.hostname == "elections" || window.location.hostname == "192.168.1.147") {
    global_url = proxy_url+"?proxy_url="+global_url;
    //tiles_pngs_url = "http://rtvedata{n}.ipq.co/data/tiles/";
  } else if (window.location.hostname == "datos.rtve.es") {
    global_url = "http://datos.rtve.es/elecciones/autonomicas-municipales/data";
  }

  //Go to IE6 page
  if (ua.msie) {
    if (ua.version.slice(0,3) < '7.0') {
      window.location.href = "http://cartodb.com/";
    } else if (ua.version.slice(0,3) < '9.0') {
      ie_ = true;
    }
  }

<commit_msg>changes on urls<commit_after>
  var global_url      = "http://datos.rtve.es/elecciones/autonomicas-municipales/data";
  var tiles_pngs_url  = "http://datos{n}.rtve.es/elecciones/autonomicas-municipales/data/tiles/";
  //var global_url = "http://ec2-50-16-13-57.compute-1.amazonaws.com/data"
  //var tiles_pngs_url  = "http://ec2-50-16-13-57.compute-1.amazonaws.com/data/tiles/";
  var proxy_url       = "proxy.php";
  var tiles_version   = "live"; //"current"; //3;
  var bubbles_version = "current"; //10;
  var gmaps_version   = "current"; //4;
  var graph_version   = "current"; //"v7";
  var ua              = $.browser;
  var ie_             = false;

  if (window.location.hostname == "localhost" || window.location.hostname == "elections" || window.location.hostname == "192.168.1.147") {
    global_url = proxy_url+"?proxy_url="+global_url;
    //tiles_pngs_url = "http://rtvedata{n}.ipq.co/data/tiles/";
  } else if (window.location.hostname == "datos.rtve.es") {
    global_url = "http://datos.rtve.es/elecciones/autonomicas-municipales/data";
  }

  //Go to IE6 page
  if (ua.msie) {
    if (ua.version.slice(0,3) < '7.0') {
      window.location.href = "http://cartodb.com/";
    } else if (ua.version.slice(0,3) < '9.0') {
      ie_ = true;
    }
  }

<|endoftext|>