<commit_before>const LRU = cjsRequire('lru-cache')
const { IndexedCramFile, CraiIndex } = cjsRequire('@gmod/cram')

const cramIndexedFilesCache = LRU(5)

const BlobFilehandleWrapper = cjsRequire('../../Model/BlobFilehandleWrapper')
const MAX_INSERT_SIZE_FOR_STATS = 30000

class CramSlightlyLazyFeature {

    _get_name() { return this.record.readName }
    _get_start() { return this.record.alignmentStart-1 }
    _get_end() { return this.record.alignmentStart+this.record.lengthOnRef-1 }
    _get_cram_read_features() { return this.record.readFeatures }
    _get_type() { return 'match'}
    _get_mapping_quality() { return this.record.mappingQuality}
    _get_flags() { return `0x${this.record.flags.toString(16)}`}
    _get_cramFlags() { return `0x${this.record.cramFlags.toString(16)}`}
    _get_strand() { return this.record.isReverseComplemented() ? -1 : 1 }
    _get_read_group_id() { return this.record.readGroupId }
    _get_qual() { return (this.record.qualityScores || []).map(q => q+33).join(' ')}
    _get_seq() { return this.record.readBases}
    _get_seq_id() { return this._store._refIdToName(this.record.sequenceId)}
    _get_qc_failed() { return this.record.isFailedQc()}
    _get_secondary_alignment() { return this.record.isSecondary()}
    _get_duplicate() { return this.record.isDuplicate()}
    _get_supplementary_alignment() { return this.record.isSupplementary()}
    _get_pair_orientation() { return this.record.getPairOrientation()}
    _get_multi_segment_template() { return this.record.isPaired()}
    _get_multi_segment_all_correctly_aligned() { return this.record.isProperlyPaired()}
    _get_multi_segment_all_aligned() { return this.record.isProperlyPaired()}
    _get_multi_segment_next_segment_unmapped() { return this.record.isMateUnmapped()}
    _get_multi_segment_first() { return this.record.isRead1()}
    _get_multi_segment_last() { return this.record.isRead2()}
    _get_multi_segment_next_segment_reversed() { return this.record.isMateReverseComplemented()}
    _get_unmapped() { return this.record.isSegmentUnmapped()}
    _get_template_length() { return this.record.templateLength || this.record.templateSize}
    _get_next_seq_id() { return this.record.mate ? this._store._refIdToName(this.record.mate.sequenceId) : undefined }
    _get_next_pos() { return this.record.mate ? this.record.mate.alignmentStart : undefined }
    _get_next_segment_position() { return this.record.mate
        ? ( this._store._refIdToName(this.record.mate.sequenceId)+':'+this.record.mate.alignmentStart) : undefined }
    _get_tags() { return this.record.tags }
    _get_seq() { return this.record.getReadBases() }

    constructor(record, store) {
        this.record = record
        this._store = store
    }

    tags() {
        const properties = Object.getOwnPropertyNames(CramSlightlyLazyFeature.prototype)
        return properties
            .filter(prop => /^_get_/.test(prop))
            .map(methodName => methodName.replace('_get_',''))
    }

    id() {
        return this.record.uniqueId + 1
    }

    _get(field) {
        const methodName = `_get_${field}`
        if (this[methodName]) return this[methodName]()
        return undefined
    }
    get(field) {
        const methodName = `_get_${field.toLowerCase()}`
        if (this[methodName]) return this[methodName]()
        return undefined
    }

    parent() {}

    children() {}

    pairedFeature() { return false }
}

define( [
            'dojo/_base/declare',
            'JBrowse/Util',
            'JBrowse/Errors',
            'JBrowse/Store/SeqFeature',
            'JBrowse/Store/DeferredStatsMixin',
            'JBrowse/Store/DeferredFeaturesMixin',
            'JBrowse/Store/SeqFeature/GlobalStatsEstimationMixin',
            'JBrowse/Store/SeqFeature/_PairCache',
            'JBrowse/Store/SeqFeature/_SpanCache',
            'JBrowse/Store/SeqFeature/_InsertSizeCache',
            'JBrowse/Model/XHRBlob',
            'JBrowse/Model/SimpleFeature',
        ],
        function(
            declare,
            Util,
            Errors,
            SeqFeatureStore,
            DeferredStatsMixin,
            DeferredFeaturesMixin,
            GlobalStatsEstimationMixin,
            PairCache,
            SpanCache,
            InsertSizeCache,
            XHRBlob,
            SimpleFeature,
        ) {

return declare( [ SeqFeatureStore, DeferredStatsMixin, DeferredFeaturesMixin, GlobalStatsEstimationMixin ],

/**
 * @lends JBrowse.Store.SeqFeature.CRAM
 */
{
    /**
     * Data backend for reading feature data directly from a
     * web-accessible CRAM file.
     *
     * @constructs
     */
    constructor: function( args ) {

        let dataBlob
        if (args.cram)
            dataBlob = new BlobFilehandleWrapper(args.cram)
        else if (args.urlTemplate)
            dataBlob = new BlobFilehandleWrapper(new XHRBlob(this.resolveUrl(args.urlTemplate || 'data.cram'), { expectRanges: true }))
        else throw new Error('must provide either `cram` or `urlTemplate`')

        let indexBlob
        if (args.crai)
            indexBlob = new BlobFilehandleWrapper(args.crai)
        else if (args.craiUrlTemplate)
            indexBlob = new BlobFilehandleWrapper(new XHRBlob(this.resolveUrl(args.craiUrlTemplate)))
        else if (args.urlTemplate)
            indexBlob = new BlobFilehandleWrapper(new XHRBlob(this.resolveUrl(args.urlTemplate+'.crai')))
        else throw new Error('no index provided, must provide a CRAM index')

        this.source = dataBlob.toString()

        // LRU-cache the CRAM object so we don't have to re-download the
        // index when we switch chromosomes
        const cacheKey = `data: ${dataBlob}, index: ${indexBlob}`
        this.cram = cramIndexedFilesCache.get(cacheKey)
        if (!this.cram) {
            this.cram = new IndexedCramFile({
                cramFilehandle: dataBlob,
                index: new CraiIndex({filehandle: indexBlob}),
                seqFetch: this._seqFetch.bind(this),
                checkSequenceMD5: false,
                fetchSizeLimit: args.fetchSizeLimit || 60000000
            })

            cramIndexedFilesCache.set(cacheKey, this.cram)
        }

        // pre-download the index before running the statistics estimation so that the stats
        // estimation doesn't time out
        this.cram.hasDataForReferenceSequence(0)
            .then(() => this.cram.cram.getSamHeader())
            .then(samHeader => {
                this._setSamHeader(samHeader)
            })
            .then(() => {
                this._deferred.features.resolve({success:true});
            })
            .then(() => this._estimateGlobalStats())
            .then(stats => {
                this.globalStats = stats;
                this._deferred.stats.resolve({success:true});
            })
            .catch(err => {
                this._deferred.features.reject(err)
                this._deferred.stats.reject(err)
            })

        this.storeTimeout = args.storeTimeout || 3000;
        this.insertSizeCache = new InsertSizeCache(args);
        this.pairCache = new PairCache(args);
        this.spanCache = new SpanCache(args);
    },

    // process the parsed SAM header from the cram file
    _setSamHeader(samHeader) {
        this._samHeader = {}

        // use the @SQ lines in the header to figure out the
        // mapping between ref seq ID numbers and names
        const refSeqIdToName = []
        const refSeqNameToId = {}
        const sqLines = samHeader.filter(l => l.tag === 'SQ')
        sqLines.forEach((sqLine, seqId) => {
            sqLine.data.forEach(item => {
                if (item.tag === 'SN') {
                    // this is the seq name
                    const seqName = item.value
                    refSeqNameToId[seqName] = seqId
                    refSeqIdToName[seqId] = seqName
                }
            })
        })
        if (refSeqIdToName.length) {
            this._samHeader.refSeqIdToName = refSeqIdToName
            this._samHeader.refSeqNameToId = refSeqNameToId
        }
    },

    _refNameToId(refName) {
        // use info from the SAM header if possible, but fall back to using
        // the ref seq order from when the browser's refseqs were loaded
        if (this._samHeader.refSeqNameToId && refName in this._samHeader.refSeqNameToId)
            return this._samHeader.refSeqNameToId[refName]
        else
            return this.browser.getRefSeqNumber(refName)
    },

    _refIdToName(refId) {
        // use info from the SAM header if possible, but fall back to using
        // the ref seq order from when the browser's refseqs were loaded
        if (this._samHeader.refSeqIdToName) {
            return this._samHeader.refSeqIdToName[refId]
        } else {
            let ref = this.browser.getRefSeqById(refId)
            return ref ? ref.name : undefined
        }
    },

    _getRefSeqStore() {
        return new Promise((resolve,reject) => {
            this.browser.getStore('refseqs',resolve,reject)
        })
    },

    // used by the CRAM backend to fetch a region of the underlying reference
    // sequence.  needed for some of its calculations
    async _seqFetch(seqId, start, end) {
        start -= 1 // convert from 1-based closed to interbase

        const refSeqStore = await this._getRefSeqStore()
        if (!refSeqStore) return undefined
        const refName = this._refIdToName(seqId)
        if (!refName) return undefined

        const seqChunks = await new Promise((resolve,reject) => {
            let features = []
            refSeqStore.getFeatures(
                {ref: refName, start: start-1, end},
                f => features.push(f),
                () => resolve(features),
                reject
            )
        })

        const trimmed = []
        seqChunks
        .sort((a,b) => a.get('start') - b.get('start'))
        .forEach( (chunk,i) => {
            let chunkStart = chunk.get('start')
            let chunkEnd = chunk.get('end')
            let trimStart = Math.max(start - chunkStart, 0)
            let trimEnd = Math.min(end - chunkStart, chunkEnd-chunkStart)
            let trimLength = trimEnd - trimStart
            let chunkSeq = chunk.get('seq') || chunk.get('residues')
            trimmed.push(chunkSeq.substr(trimStart,trimLength))
        })

        const sequence = trimmed.join('')
        if (sequence.length !== (end-start))
            throw new Error(`sequence fetch failed: fetching ${
                (start-1).toLocaleString()}-${end.toLocaleString()
                } only returned ${
                    sequence.length.toLocaleString()
                } bases, but should have returned ${
                    (end-start).toLocaleString()
                }`)
        return sequence
    },

    /**
     * Interrogate whether a store has data for a given reference
     * sequence.  Calls the given callback with either true or false.
     */
    hasRefSeq: function( seqName, callback, errorCallback ) {
        seqName = this.browser.regularizeReferenceName( seqName );
        const refSeqNumber = this._refNameToId(seqName)
        if (refSeqNumber === undefined) callback(false)

        this._deferred.stats
        .then(() => this.cram.hasDataForReferenceSequence(refSeqNumber))
        .then(callback, errorCallback)
    },

    // called by getFeatures from the DeferredFeaturesMixin
    _getFeatures: function( query, featCallback, endCallback, errorCallback ) {
        const pairCache = {};
        const seqName = query.ref || this.refSeq.name
        const refSeqNumber = this._refNameToId(seqName)
        if (refSeqNumber === undefined) {
            endCallback()
            return
        }
        this.cram.getRecordsForRange(refSeqNumber, query.start + 1, query.end, { viewAsPairs: query.viewAsPairs, viewAsSpans: query.viewAsSpans, maxInsertSize: query.maxInsertSize })
            .then(records => {
                if(query.viewAsPairs) {
                    const recs = records.map(f => this._cramRecordToFeature(f))
                    recs.forEach(r => this.insertSizeCache.insertFeat(r))
                    this.pairCache.pairFeatures(query, recs, featCallback, endCallback, errorCallback)
                } else if(query.viewAsSpans) {
                    const recs = records.map(f => this._cramRecordToFeature(f))
                    recs.forEach(r => this.insertSizeCache.insertFeat(r))
                    this.spanCache.pairFeatures(query, recs, featCallback, endCallback, errorCallback)
                } else {
                    for(let i = 0; i < records.length; i++) {
                        let feat = this._cramRecordToFeature(records[i])
                        this.insertSizeCache.insertFeat(feat);
                        featCallback(feat)
                    }
                }
                endCallback()
            })
            .catch(err => {
                // map the CramSizeLimitError to JBrowse Errors.DataOverflow
                console.error(err)

                errorCallback(err)
            })
    },

    getInsertSizeStats() {
        return this.insertSizeCache.getInsertSizeStats()
    },

    cleanFeatureCache(query) {
        this.pairCache.cleanFeatureCache(query)
        this.spanCache.cleanFeatureCache(query)
    },

    _cramRecordToFeature(record) {
        return new CramSlightlyLazyFeature(record, this)
    },

    saveStore() {
        return {
            urlTemplate: this.config.cram.url,
            craiUrlTemplate: this.config.crai.url
        };
    }

});
});
<commit_msg>Unused param<commit_after>const LRU = cjsRequire('lru-cache')
const { IndexedCramFile, CraiIndex } = cjsRequire('@gmod/cram')

const cramIndexedFilesCache = LRU(5)

const BlobFilehandleWrapper = cjsRequire('../../Model/BlobFilehandleWrapper')

class CramSlightlyLazyFeature {

    _get_name() { return this.record.readName }
    _get_start() { return this.record.alignmentStart-1 }
    _get_end() { return this.record.alignmentStart+this.record.lengthOnRef-1 }
    _get_cram_read_features() { return this.record.readFeatures }
    _get_type() { return 'match'}
    _get_mapping_quality() { return this.record.mappingQuality}
    _get_flags() { return `0x${this.record.flags.toString(16)}`}
    _get_cramFlags() { return `0x${this.record.cramFlags.toString(16)}`}
    _get_strand() { return this.record.isReverseComplemented() ? -1 : 1 }
    _get_read_group_id() { return this.record.readGroupId }
    _get_qual() { return (this.record.qualityScores || []).map(q => q+33).join(' ')}
    _get_seq() { return this.record.readBases}
    _get_seq_id() { return this._store._refIdToName(this.record.sequenceId)}
    _get_qc_failed() { return this.record.isFailedQc()}
    _get_secondary_alignment() { return this.record.isSecondary()}
    _get_duplicate() { return this.record.isDuplicate()}
    _get_supplementary_alignment() { return this.record.isSupplementary()}
    _get_pair_orientation() { return this.record.getPairOrientation()}
    _get_multi_segment_template() { return this.record.isPaired()}
    _get_multi_segment_all_correctly_aligned() { return this.record.isProperlyPaired()}
    _get_multi_segment_all_aligned() { return this.record.isProperlyPaired()}
    _get_multi_segment_next_segment_unmapped() { return this.record.isMateUnmapped()}
    _get_multi_segment_first() { return this.record.isRead1()}
    _get_multi_segment_last() { return this.record.isRead2()}
    _get_multi_segment_next_segment_reversed() { return this.record.isMateReverseComplemented()}
    _get_unmapped() { return this.record.isSegmentUnmapped()}
    _get_template_length() { return this.record.templateLength || this.record.templateSize}
    _get_next_seq_id() { return this.record.mate ? this._store._refIdToName(this.record.mate.sequenceId) : undefined }
    _get_next_pos() { return this.record.mate ? this.record.mate.alignmentStart : undefined }
    _get_next_segment_position() { return this.record.mate
        ? ( this._store._refIdToName(this.record.mate.sequenceId)+':'+this.record.mate.alignmentStart) : undefined }
    _get_tags() { return this.record.tags }
    _get_seq() { return this.record.getReadBases() }

    constructor(record, store) {
        this.record = record
        this._store = store
    }

    tags() {
        const properties = Object.getOwnPropertyNames(CramSlightlyLazyFeature.prototype)
        return properties
            .filter(prop => /^_get_/.test(prop))
            .map(methodName => methodName.replace('_get_',''))
    }

    id() {
        return this.record.uniqueId + 1
    }

    _get(field) {
        const methodName = `_get_${field}`
        if (this[methodName]) return this[methodName]()
        return undefined
    }
    get(field) {
        const methodName = `_get_${field.toLowerCase()}`
        if (this[methodName]) return this[methodName]()
        return undefined
    }

    parent() {}

    children() {}

    pairedFeature() { return false }
}

define( [
            'dojo/_base/declare',
            'JBrowse/Util',
            'JBrowse/Errors',
            'JBrowse/Store/SeqFeature',
            'JBrowse/Store/DeferredStatsMixin',
            'JBrowse/Store/DeferredFeaturesMixin',
            'JBrowse/Store/SeqFeature/GlobalStatsEstimationMixin',
            'JBrowse/Store/SeqFeature/_PairCache',
            'JBrowse/Store/SeqFeature/_SpanCache',
            'JBrowse/Store/SeqFeature/_InsertSizeCache',
            'JBrowse/Model/XHRBlob',
            'JBrowse/Model/SimpleFeature',
        ],
        function(
            declare,
            Util,
            Errors,
            SeqFeatureStore,
            DeferredStatsMixin,
            DeferredFeaturesMixin,
            GlobalStatsEstimationMixin,
            PairCache,
            SpanCache,
            InsertSizeCache,
            XHRBlob,
            SimpleFeature,
        ) {

return declare( [ SeqFeatureStore, DeferredStatsMixin, DeferredFeaturesMixin, GlobalStatsEstimationMixin ],

/**
 * @lends JBrowse.Store.SeqFeature.CRAM
 */
{
    /**
     * Data backend for reading feature data directly from a
     * web-accessible CRAM file.
     *
     * @constructs
     */
    constructor: function( args ) {

        let dataBlob
        if (args.cram)
            dataBlob = new BlobFilehandleWrapper(args.cram)
        else if (args.urlTemplate)
            dataBlob = new BlobFilehandleWrapper(new XHRBlob(this.resolveUrl(args.urlTemplate || 'data.cram'), { expectRanges: true }))
        else throw new Error('must provide either `cram` or `urlTemplate`')

        let indexBlob
        if (args.crai)
            indexBlob = new BlobFilehandleWrapper(args.crai)
        else if (args.craiUrlTemplate)
            indexBlob = new BlobFilehandleWrapper(new XHRBlob(this.resolveUrl(args.craiUrlTemplate)))
        else if (args.urlTemplate)
            indexBlob = new BlobFilehandleWrapper(new XHRBlob(this.resolveUrl(args.urlTemplate+'.crai')))
        else throw new Error('no index provided, must provide a CRAM index')

        this.source = dataBlob.toString()

        // LRU-cache the CRAM object so we don't have to re-download the
        // index when we switch chromosomes
        const cacheKey = `data: ${dataBlob}, index: ${indexBlob}`
        this.cram = cramIndexedFilesCache.get(cacheKey)
        if (!this.cram) {
            this.cram = new IndexedCramFile({
                cramFilehandle: dataBlob,
                index: new CraiIndex({filehandle: indexBlob}),
                seqFetch: this._seqFetch.bind(this),
                checkSequenceMD5: false,
                fetchSizeLimit: args.fetchSizeLimit || 60000000
            })

            cramIndexedFilesCache.set(cacheKey, this.cram)
        }

        // pre-download the index before running the statistics estimation so that the stats
        // estimation doesn't time out
        this.cram.hasDataForReferenceSequence(0)
            .then(() => this.cram.cram.getSamHeader())
            .then(samHeader => {
                this._setSamHeader(samHeader)
            })
            .then(() => {
                this._deferred.features.resolve({success:true});
            })
            .then(() => this._estimateGlobalStats())
            .then(stats => {
                this.globalStats = stats;
                this._deferred.stats.resolve({success:true});
            })
            .catch(err => {
                this._deferred.features.reject(err)
                this._deferred.stats.reject(err)
            })

        this.storeTimeout = args.storeTimeout || 3000;
        this.insertSizeCache = new InsertSizeCache(args);
        this.pairCache = new PairCache(args);
        this.spanCache = new SpanCache(args);
    },

    // process the parsed SAM header from the cram file
    _setSamHeader(samHeader) {
        this._samHeader = {}

        // use the @SQ lines in the header to figure out the
        // mapping between ref seq ID numbers and names
        const refSeqIdToName = []
        const refSeqNameToId = {}
        const sqLines = samHeader.filter(l => l.tag === 'SQ')
        sqLines.forEach((sqLine, seqId) => {
            sqLine.data.forEach(item => {
                if (item.tag === 'SN') {
                    // this is the seq name
                    const seqName = item.value
                    refSeqNameToId[seqName] = seqId
                    refSeqIdToName[seqId] = seqName
                }
            })
        })
        if (refSeqIdToName.length) {
            this._samHeader.refSeqIdToName = refSeqIdToName
            this._samHeader.refSeqNameToId = refSeqNameToId
        }
    },

    _refNameToId(refName) {
        // use info from the SAM header if possible, but fall back to using
        // the ref seq order from when the browser's refseqs were loaded
        if (this._samHeader.refSeqNameToId && refName in this._samHeader.refSeqNameToId)
            return this._samHeader.refSeqNameToId[refName]
        else
            return this.browser.getRefSeqNumber(refName)
    },

    _refIdToName(refId) {
        // use info from the SAM header if possible, but fall back to using
        // the ref seq order from when the browser's refseqs were loaded
        if (this._samHeader.refSeqIdToName) {
            return this._samHeader.refSeqIdToName[refId]
        } else {
            let ref = this.browser.getRefSeqById(refId)
            return ref ? ref.name : undefined
        }
    },

    _getRefSeqStore() {
        return new Promise((resolve,reject) => {
            this.browser.getStore('refseqs',resolve,reject)
        })
    },

    // used by the CRAM backend to fetch a region of the underlying reference
    // sequence.  needed for some of its calculations
    async _seqFetch(seqId, start, end) {
        start -= 1 // convert from 1-based closed to interbase

        const refSeqStore = await this._getRefSeqStore()
        if (!refSeqStore) return undefined
        const refName = this._refIdToName(seqId)
        if (!refName) return undefined

        const seqChunks = await new Promise((resolve,reject) => {
            let features = []
            refSeqStore.getFeatures(
                {ref: refName, start: start-1, end},
                f => features.push(f),
                () => resolve(features),
                reject
            )
        })

        const trimmed = []
        seqChunks
        .sort((a,b) => a.get('start') - b.get('start'))
        .forEach( (chunk,i) => {
            let chunkStart = chunk.get('start')
            let chunkEnd = chunk.get('end')
            let trimStart = Math.max(start - chunkStart, 0)
            let trimEnd = Math.min(end - chunkStart, chunkEnd-chunkStart)
            let trimLength = trimEnd - trimStart
            let chunkSeq = chunk.get('seq') || chunk.get('residues')
            trimmed.push(chunkSeq.substr(trimStart,trimLength))
        })

        const sequence = trimmed.join('')
        if (sequence.length !== (end-start))
            throw new Error(`sequence fetch failed: fetching ${
                (start-1).toLocaleString()}-${end.toLocaleString()
                } only returned ${
                    sequence.length.toLocaleString()
                } bases, but should have returned ${
                    (end-start).toLocaleString()
                }`)
        return sequence
    },

    /**
     * Interrogate whether a store has data for a given reference
     * sequence.  Calls the given callback with either true or false.
     */
    hasRefSeq: function( seqName, callback, errorCallback ) {
        seqName = this.browser.regularizeReferenceName( seqName );
        const refSeqNumber = this._refNameToId(seqName)
        if (refSeqNumber === undefined) callback(false)

        this._deferred.stats
        .then(() => this.cram.hasDataForReferenceSequence(refSeqNumber))
        .then(callback, errorCallback)
    },

    // called by getFeatures from the DeferredFeaturesMixin
    _getFeatures: function( query, featCallback, endCallback, errorCallback ) {
        const pairCache = {};
        const seqName = query.ref || this.refSeq.name
        const refSeqNumber = this._refNameToId(seqName)
        if (refSeqNumber === undefined) {
            endCallback()
            return
        }
        this.cram.getRecordsForRange(refSeqNumber, query.start + 1, query.end, { viewAsPairs: query.viewAsPairs, viewAsSpans: query.viewAsSpans, maxInsertSize: query.maxInsertSize })
            .then(records => {
                if(query.viewAsPairs) {
                    const recs = records.map(f => this._cramRecordToFeature(f))
                    recs.forEach(r => this.insertSizeCache.insertFeat(r))
                    this.pairCache.pairFeatures(query, recs, featCallback, endCallback, errorCallback)
                } else if(query.viewAsSpans) {
                    const recs = records.map(f => this._cramRecordToFeature(f))
                    recs.forEach(r => this.insertSizeCache.insertFeat(r))
                    this.spanCache.pairFeatures(query, recs, featCallback, endCallback, errorCallback)
                } else {
                    for(let i = 0; i < records.length; i++) {
                        let feat = this._cramRecordToFeature(records[i])
                        this.insertSizeCache.insertFeat(feat);
                        featCallback(feat)
                    }
                }
                endCallback()
            })
            .catch(err => {
                // map the CramSizeLimitError to JBrowse Errors.DataOverflow
                console.error(err)

                errorCallback(err)
            })
    },

    getInsertSizeStats() {
        return this.insertSizeCache.getInsertSizeStats()
    },

    cleanFeatureCache(query) {
        this.pairCache.cleanFeatureCache(query)
        this.spanCache.cleanFeatureCache(query)
    },

    _cramRecordToFeature(record) {
        return new CramSlightlyLazyFeature(record, this)
    },

    saveStore() {
        return {
            urlTemplate: this.config.cram.url,
            craiUrlTemplate: this.config.crai.url
        };
    }

});
});
<|endoftext|>


<commit_before>/**
* @license Apache-2.0
*
* Copyright (c) 2021 The Stdlib Authors.
*
* 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.
*/

'use strict';

// MODULES //

var isString = require( '@stdlib/assert/is-string' ).isPrimitive;
var capitalize = require( '@stdlib/string/base/capitalize' );
var lowercase = require( '@stdlib/string/base/lowercase' );
var replace = require( '@stdlib/string/base/replace' );
var format = require( '@stdlib/string/format' );
var trim = require( '@stdlib/string/base/trim' );

// VARIABLES //

var RE_WHITESPACE = /\s+/g;
var RE_SPECIAL = /[-!"'(),–.:;<>?`{}|~\/\\\[\]_#$*&^@%]+/g; // eslint-disable-line no-useless-escape
var RE_TO_PASCAL = /(?:\s|^)([^\s]+)(?=\s|$)/g;
var RE_CAMEL = /([a-z0-9])([A-Z])/g;

// FUNCTIONS //

/**
* Callback invoked upon a match.
*
* @private
* @param {string} match - entire match
* @param {string} p1 - first capture group
* @returns {string} capitalized capture group
*/
function replacer( match, p1 ) {
	return capitalize( lowercase( p1 ) );
}

// MAIN //

/**
* Converts a string to Pascal case.
*
* @param {string} str - string to convert
* @throws {TypeError} must provide a string
* @returns {string} Pascal-cased string
*
* @example
* var out = pascalcase( 'foo bar' );
* // returns 'FooBar'
*
* @example
* var out = pascalcase( 'IS_MOBILE' );
* // returns 'IsMobile'
*
* @example
* var out = pascalcase( 'Hello World!' );
* // returns 'HelloWorld'
*
* @example
* var out = pascalcase( '--foo-bar--' );
* // returns 'FooBar'
*/
function pascalcase( str ) {
	if ( !isString( str ) ) {
		throw new TypeError( format( 'invalid argument. First argument must be a string. Value: `%s`.', str ) );
	}
	str = replace( str, RE_SPECIAL, ' ' );
	str = replace( str, RE_WHITESPACE, ' ' );
	str = replace( str, RE_CAMEL, '$1 $2' );
	str = trim( str );
	return replace( str, RE_TO_PASCAL, replacer );
}

// EXPORTS //

module.exports = pascalcase;
<commit_msg>Use base package<commit_after>/**
* @license Apache-2.0
*
* Copyright (c) 2021 The Stdlib Authors.
*
* 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.
*/

'use strict';

// MODULES //

var isString = require( '@stdlib/assert/is-string' ).isPrimitive;
var format = require( '@stdlib/string/format' );
var base = require( '@stdlib/string/base/pascalcase' );

// MAIN //

/**
* Converts a string to Pascal case.
*
* @param {string} str - string to convert
* @throws {TypeError} must provide a string
* @returns {string} Pascal-cased string
*
* @example
* var out = pascalcase( 'foo bar' );
* // returns 'FooBar'
*
* @example
* var out = pascalcase( 'IS_MOBILE' );
* // returns 'IsMobile'
*
* @example
* var out = pascalcase( 'Hello World!' );
* // returns 'HelloWorld'
*
* @example
* var out = pascalcase( '--foo-bar--' );
* // returns 'FooBar'
*/
function pascalcase( str ) {
	if ( !isString( str ) ) {
		throw new TypeError( format( 'invalid argument. First argument must be a string. Value: `%s`.', str ) );
	}
	return base( str );
}

// EXPORTS //

module.exports = pascalcase;
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, browser: true, nomen: true, indent: 4, maxerr: 50 */
/*global define: false, $: false, CodeMirror: false */

/**
 * Text-editing commands that apply to whichever Editor is currently focused
 */
define(function (require, exports, module) {
    'use strict';

    // Load dependent modules
    var Commands           = require("command/Commands"),
        CommandManager     = require("command/CommandManager"),
        EditorManager      = require("editor/EditorManager");

    /**
     * Add or remove line-comment tokens to all the lines in the selected range, preserving selection
     * and cursor position. Applies to currently focused Editor.
     *
     * If all non-whitespace lines are already commented out, then we uncomment; otherwise we comment
     * out. Commenting out adds "//" to at column 0 of every line. Uncommenting removes the first "//"
     * on each line (if any - empty lines might not have one).
     */
    function lineCommentSlashSlash(editor) {

        var sel = editor.getSelection();
        var startLine = sel.start.line;
        var endLine = sel.end.line;

        // Is a range of text selected? (vs just an insertion pt)
        var hasSelection = (startLine !== endLine) || (sel.start.ch !== sel.end.ch);

        // In full-line selection, cursor pos is start of next line - but don't want to modify that line
        if (sel.end.ch === 0 && hasSelection) {
            endLine--;
        }

        // Decide if we're commenting vs. un-commenting
        // Are there any non-blank lines that aren't commented out? (We ignore blank lines because
        // some editors like Sublime don't comment them out)
        var containsUncommented = false;
        var i;
        var line;
        for (i = startLine; i <= endLine; i++) {
            line = editor.getLineText(i);
            // A line is commented out if it starts with 0-N whitespace chars, then "//"
            if (!line.match(/^\s*\/\//) && line.match(/\S/)) {
                containsUncommented = true;
                break;
            }
        }

        // Make the edit
        // TODO (#803): should go through Document, not Editor._codeMirror
        var cm = editor._codeMirror;
        cm.operation(function () {

            if (containsUncommented) {
                // Comment out - prepend "//" to each line
                for (i = startLine; i <= endLine; i++) {
                    cm.replaceRange("//", {line: i, ch: 0});
                }

                // Make sure selection includes "//" that was added at start of range
                if (sel.start.ch === 0 && hasSelection) {
                    // use *current* selection end, which has been updated for our text insertions
                    editor.setSelection({line: startLine, ch: 0}, editor.getSelection().end);
                }

            } else {
                // Uncomment - remove first "//" on each line (if any)
                for (i = startLine; i <= endLine; i++) {
                    line = editor.getLineText(i);
                    var commentI = line.indexOf("//");
                    if (commentI !== -1) {
                        cm.replaceRange("", {line: i, ch: commentI}, {line: i, ch: commentI + 2});
                    }
                }
            }
        });

    }

    /** Invokes a language-specific line-comment/uncomment handler */
    function lineComment() {
        var editor = EditorManager.getFocusedEditor();
        if (!editor) {
            return;
        }

        // TODO: use mode *at cursor location*, so we can support mixed-mode e.g. JS in script blocks
        var mode = editor._codeMirror.getOption("mode");

        // Currently we only support languages with "//" commenting
        if (mode === "javascript" || mode === "less") {
            lineCommentSlashSlash(editor);
        }
    }

    // Register commands
    CommandManager.register(Commands.EDIT_LINE_COMMENT, lineComment);
});
<commit_msg>Fixed Capitalization in TODO Comments<commit_after>/*
 * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, browser: true, nomen: true, indent: 4, maxerr: 50 */
/*global define: false, $: false, CodeMirror: false */

/**
 * Text-editing commands that apply to whichever Editor is currently focused
 */
define(function (require, exports, module) {
    'use strict';

    // Load dependent modules
    var Commands           = require("command/Commands"),
        CommandManager     = require("command/CommandManager"),
        EditorManager      = require("editor/EditorManager");

    /**
     * Add or remove line-comment tokens to all the lines in the selected range, preserving selection
     * and cursor position. Applies to currently focused Editor.
     *
     * If all non-whitespace lines are already commented out, then we uncomment; otherwise we comment
     * out. Commenting out adds "//" to at column 0 of every line. Uncommenting removes the first "//"
     * on each line (if any - empty lines might not have one).
     */
    function lineCommentSlashSlash(editor) {

        var sel = editor.getSelection();
        var startLine = sel.start.line;
        var endLine = sel.end.line;

        // Is a range of text selected? (vs just an insertion pt)
        var hasSelection = (startLine !== endLine) || (sel.start.ch !== sel.end.ch);

        // In full-line selection, cursor pos is start of next line - but don't want to modify that line
        if (sel.end.ch === 0 && hasSelection) {
            endLine--;
        }

        // Decide if we're commenting vs. un-commenting
        // Are there any non-blank lines that aren't commented out? (We ignore blank lines because
        // some editors like Sublime don't comment them out)
        var containsUncommented = false;
        var i;
        var line;
        for (i = startLine; i <= endLine; i++) {
            line = editor.getLineText(i);
            // A line is commented out if it starts with 0-N whitespace chars, then "//"
            if (!line.match(/^\s*\/\//) && line.match(/\S/)) {
                containsUncommented = true;
                break;
            }
        }

        // Make the edit
        // TODO (#803): should go through Document, not editor._codeMirror
        var cm = editor._codeMirror;
        cm.operation(function () {

            if (containsUncommented) {
                // Comment out - prepend "//" to each line
                for (i = startLine; i <= endLine; i++) {
                    cm.replaceRange("//", {line: i, ch: 0});
                }

                // Make sure selection includes "//" that was added at start of range
                if (sel.start.ch === 0 && hasSelection) {
                    // use *current* selection end, which has been updated for our text insertions
                    editor.setSelection({line: startLine, ch: 0}, editor.getSelection().end);
                }

            } else {
                // Uncomment - remove first "//" on each line (if any)
                for (i = startLine; i <= endLine; i++) {
                    line = editor.getLineText(i);
                    var commentI = line.indexOf("//");
                    if (commentI !== -1) {
                        cm.replaceRange("", {line: i, ch: commentI}, {line: i, ch: commentI + 2});
                    }
                }
            }
        });

    }

    /** Invokes a language-specific line-comment/uncomment handler */
    function lineComment() {
        var editor = EditorManager.getFocusedEditor();
        if (!editor) {
            return;
        }

        // TODO: use mode *at cursor location*, so we can support mixed-mode e.g. JS in script blocks
        var mode = editor._codeMirror.getOption("mode");

        // Currently we only support languages with "//" commenting
        if (mode === "javascript" || mode === "less") {
            lineCommentSlashSlash(editor);
        }
    }

    // Register commands
    CommandManager.register(Commands.EDIT_LINE_COMMENT, lineComment);
});
<|endoftext|>


<commit_before>Ext.namespace('Vps.Component');
Vps.Component.Pages = Ext.extend(Ext.Panel, {
    initComponent : function()
    {
        this.actions = {};
        this.componentConfigs = {};

        this.pageButtonMenu = new Ext.menu.Menu({
            items    : [
                '-',
                this.getAction('properties'),
                this.getAction('add'),
                this.getAction('delete'),
                this.getAction('copy'),
                this.getAction('paste'),
                this.getAction('visible'),
                this.getAction('makeHome'),
                this.getAction('preview')
            ]
        });
        this.pageButton = new Ext.Toolbar.Button({
            cls     : 'x-btn-text-icon bmenu',
            text    : trlVps('Page'),
            menu    : this.pageButtonMenu,
            icon    : '/assets/silkicons/page.png',
            disabled: true
        });

        this.treePanel = new Vps.Auto.TreePanel({
            controllerUrl: '/admin/component/pages',
            title       : trlVps('Seitenbaum'),
            region      : 'west',
            split       : true,
            width       : 300,
            collapsible : true,
            minSize     : 200,
            maxSize     : 600,
            autoScroll: true,
            tbar: []
        });

        var tbar = this.treePanel.getTopToolbar();
        tbar.add(this.pageButton);

        tbar.add('-');
        tbar.add(trlVps('Search: '));

        var filter = new Vps.Auto.Filter.Text({name: 'text', 'paramName': 'query'});
        filter.on('filter', function(f, params) {
            this.applyBaseParams(params);
            this.reload();
        }, this.treePanel);
        filter.getToolbarItem().each(function(i) {
            tbar.add(i);
        });

        tbar.add('->');
        tbar.add(this.treePanel.getAction('reload'));

        this.treePanel.onMoved = function (response) {
            this.tree.getRootNode().reload();
        };

        this.contentTabPanel = new Ext.TabPanel({
            region      : 'center'
        });
        this.contentTabPanel.on('render', function() {
            this.contentTabPanel.strip.setHeight(24);
        }, this);

        this.layout = 'border';
        this.items = [this.treePanel, this.contentTabPanel];

        Vps.Component.Pages.superclass.initComponent.call(this);

        this.treePanel.on('loaded', this.onTreePanelLoaded, this);
        this.setupEditform();

        this.editActions = {};
    },
    setupEditform : function ()
    {
       this.editDialog = new Vps.Auto.Form.Window({
            width: 600,
            height: 400,
            controllerUrl: '/admin/component/page'
        });
        this.editDialog.on('datachange', function(test) {
            this.treePanel.tree.root.reload();
        }, this);
        this.editDialog.on('renderform', function() {
            var component = this.editDialog.getAutoForm().findField('component');
            if (component && component.formsForComponent) {
                component.on('changevalue', function() {
                    //hide/show forms depending on selected component
                    this.editDialog.getAutoForm().cascade(function(i) {
                        var showForms = component.formsForComponent[component.getValue()] || [];
                        if (i.showDependingOnComponent) {
                            if (showForms.indexOf(i.name) !== -1) {
                                i.show();
                                i.enableRecursive();
                            } else {
                                i.disableRecursive(); //to disable validation
                                i.hide();
                            }
                        }
                    }, this);
                }, this);
            }
        }, this);
    },

    onTreePanelLoaded : function(tree)
    {
        tree.loader.on('load', function(tree, node, response) {
            var result = Ext.decode(response.responseText);
            Ext.applyIf(this.componentConfigs, result.componentConfigs);
        }, this);

        this.contextMenu = new Ext.menu.Menu({
             items: [
                '-',
                this.getAction('properties'),
                this.getAction('add'),
                this.getAction('delete'),
                this.getAction('copy'),
                this.getAction('paste'),
                this.getAction('visible'),
                this.getAction('makeHome'),
                this.getAction('preview')
            ]
        });

        tree.on('click', this.treeSelectionchange, this);
        tree.on('contextmenu', function (node) {
            node.select();
            this.treeSelectionchange(node);
            this.contextMenu.show(node.ui.getAnchor());
        }, this);

        tree.on('dblclick', function (o, e) {
            var i = this.contextMenu.items.first();
            if (i) {
                i.handler.call(i.scope, i, e);
            }
        }, this);

    },

    treeSelectionchange : function (node) {
        if (!node) return;
        var data = node.attributes;

        if (data.disabled) {
            this.pageButton.disable();
        } else {
            this.pageButton.enable();
        }

        for (var action in data.actions) {
            if (data.actions[action]) {
                this.getAction(action).enable();
            } else {
                this.getAction(action).disable();
            }
        }

        this.contextMenu.items.each(function(i) {
            if (i.initialConfig.actionKey) {
                this.contextMenu.remove(i);
            }
        }, this);
        this.pageButtonMenu.items.each(function(i) {
            if (i.initialConfig.actionKey) {
                this.pageButtonMenu.remove(i);
            }
        }, this);
        var actionsAdded = 0;
        data.editComponents.each(function(editComponent) {
        	var editKey = editComponent.componentId;
            var actionKey = editComponent.componentClass+'-'+editComponent.type;
            if (!this.editActions[editKey]) {
                this.editActions[editKey] = new Ext.Action({
                    text    : this.componentConfigs[actionKey].title,
                    handler : function (o, e) {
                        var node = this.treePanel.tree.getSelectionModel().getSelectedNode();
                        node.attributes.editComponents.each(function(editComponent) {
                        	if (editComponent.componentId == o.editKey) {
                                this.loadComponent({
                                    id: editComponent.componentId,
                                    componentClass: editComponent.componentClass,
                                    type: editComponent.type,
                                    text: node.text,
                                    icon: node.attributes.bIcon,
                                    editComponents: node.attributes.editComponents,
                                    pageId: node.attributes.id
                                });
                                return false;
                            }
                        }, this);
                    },
                    icon    : this.componentConfigs[actionKey].icon,
                    cls     : 'x-btn-text-icon',
                    scope   : this,
                    actionKey: actionKey,
                    editKey: editKey
                });
            }
            this.editActions[editKey].setDisabled(data.disabled);
            this.contextMenu.insert(actionsAdded, new Ext.menu.Item(this.editActions[editKey]));
            this.pageButtonMenu.insert(actionsAdded, new Ext.menu.Item(this.editActions[editKey]));
            actionsAdded++;
        }, this);
    },

    loadComponent: function(data)
    {
        var panel = this.contentTabPanel.getItem('page'+data.pageId);
        if (!panel) {
            panel = this.createComponentPanel(data);
            this.contentTabPanel.add(panel);
        }
        this.contentTabPanel.setActiveTab(panel);

        panel.applyBaseParams({
            id: data.id
        });
        panel.mainComponentText = data.text;
        panel.mainComponentIcon = data.icon;
        panel.load({
            componentClass: data.componentClass,
            type: data.type,
            editComponents: data.editComponents
        });
    },

    createComponentPanel: function(data)
    {
        var panel = new Vps.Component.ComponentPanel({
            id          : 'page'+data.pageId,
            title       : data.text,
            closable    : true,
            autoLoad    : false,
            componentConfigs : this.componentConfigs
        });
        return panel;
    },

    _removeEditDialogForm: function()
    {
        var form = this.editDialog.getAutoForm();
        if (form.formPanel) {
            form.remove(form.formPanel, true);
            this.editDialog.getAutoForm().formPanel = null;
        }
    },

    getAction : function(type)
    {
        if (this.actions[type]) return this.actions[type];

        if (type == 'properties') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Page properties'),
                handler : function () {
                    this._removeEditDialogForm();
                    var node = this.treePanel.tree.selModel.selNode;
                    this.editDialog.getAutoForm().setBaseParams({
                        componentId: node.attributes.editControllerComponentId
                    });
                    this.editDialog.showEdit(node.id);
                },
                icon    : '/assets/silkicons/page_gear.png',
                cls     : 'x-btn-text-icon',
                scope   : this
            });
        } else if (type == 'add') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Add new child page'),
                handler : function () {
                    this._removeEditDialogForm();
                    var node = this.treePanel.tree.selModel.selNode;
                    this.editDialog.getAutoForm().setBaseParams({
                        componentId: node.attributes.editControllerComponentId,
                        parent_id: node.id
                    });
                    this.editDialog.showAdd();
                },
                icon    : '/assets/silkicons/page_add.png',
                cls     : 'x-btn-text-icon',
                scope   : this
            });
        } else if (type == 'delete') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Delete page'),
                handler : function() {
                    this.treePanel.onDelete();
                },
                icon    : '/assets/silkicons/page_delete.png',
                cls     : 'x-btn-text-icon',
                scope   : this
            });
        } else if (type == 'copy') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Copy page'),
                handler : function() {
                    this.onCopy();
                },
                icon    : '/assets/silkicons/page_copy.png',
                cls     : 'x-btn-text-icon',
                scope   : this
            });
        } else if (type == 'paste') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Paste page'),
                handler : function() {
                    this.onPaste();
                },
                icon    : '/assets/silkicons/page_paste.png',
                cls     : 'x-btn-text-icon',
                scope   : this
            });
        } else if (type == 'visible') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Show / hide page'),
                handler : function() {
                    this.treePanel.onVisible();
                },
                icon    : '/assets/fx_invisible/silkicons/page.png',
                cls     : 'x-btn-text-icon',
                scope   : this
            });
        } else if (type == 'makeHome') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Use as homepage'),
                handler : function (o, e) {
                    Ext.Msg.show({
                        title: trlVps('Use as homepage'),
                        msg: trlVps('Attention! You are about to set the selected page as the homepage of your website. This may affect the entire website. Do you wish to proceed?'),
                        buttons: Ext.Msg.YESNO,
                        icon: Ext.MessageBox.WARNING,
                        fn: function(btn, text) {
                            if (btn == 'yes') {
                                Ext.Ajax.request({
                                    url: '/admin/component/pages/json-make-home',
                                    success: function(r) {
                                        response = Ext.decode(r.responseText);
                                        var oldhome = this.treePanel.tree.getNodeById(response.oldhome);
                                        oldhome.attributes.visible = response.oldhomeVisible;
                                        this.treePanel.setVisible(oldhome);
                                        oldhome.ui.iconNode.style.backgroundImage = 'url(/assets/silkicons/page.png)';
                                        var home = this.treePanel.tree.getNodeById(response.home);
                                        home.ui.iconNode.style.backgroundImage = 'url(/assets/silkicons/application_home.png)';
                                    },
                                    params: {id: this.treePanel.tree.getSelectionModel().getSelectedNode().id },
                                    scope: this
                                });
                            }
                        },
                        scope: this
                    });
                },
                icon    : '/assets/silkicons/application_home.png',
                cls     : 'x-btn-text-icon',
                scope   : this
            });
        } else if (type == 'expand') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Expand here'),
                handler : function () { this.treePanel.tree.getSelectionModel().getSelectedNode().expand(true); },
                icon    : '/assets/silkicons/bullet_add.png',
                cls     : 'x-btn-text-icon',
                scope   : this
            });
        } else if (type == 'collapse') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Collapse here'),
                handler : function () { this.treePanel.tree.getSelectionModel().getSelectedNode().collapse(true); },
                icon    : '/assets/silkicons/bullet_delete.png',
                cls     : 'x-btn-text-icon',
                scope   : this
            });
        } else if (type == 'preview') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Open preview'),
                handler : function () {
                    window.open('/admin/component/pages/open-preview?page_id='+
                                this.treePanel.getSelectedId());
                },
                icon    : '/assets/silkicons/page_white_magnify.png',
                cls     : 'x-btn-text-icon',
                scope   : this
            });
        } else {
            throw 'unknown action-type: ' + type;
        }
        return this.actions[type];
    },

    onCopy: function(o, e) {
        Ext.Ajax.request({
            url: this.treePanel.controllerUrl + '/json-copy',
            params: Ext.apply({id:this.treePanel.getSelectedId()}, this.treePanel.getBaseParams()),
            mask: this.el,
            success: function(response, options, result) {
            },
            scope: this
        });
    },

    onPaste: function() {
        Ext.Ajax.request({
            url: this.treePanel.controllerUrl + '/json-paste',
            params: Ext.apply({id:this.treePanel.getSelectedId()}, this.treePanel.getBaseParams()),
            timeout: 5*60*1000,
            progress: true,
            progressTitle : trlVps('Paste Page'),
            showCancel: false,
            success: function(response, options, result) {
                this.treePanel.reload();
            },
            scope: this
        });
    }

});

Ext.reg('vps.component.pages', Vps.Component.Pages);
<commit_msg>initially hide all forms to avoid flicker<commit_after>Ext.namespace('Vps.Component');
Vps.Component.Pages = Ext.extend(Ext.Panel, {
    initComponent : function()
    {
        this.actions = {};
        this.componentConfigs = {};

        this.pageButtonMenu = new Ext.menu.Menu({
            items    : [
                '-',
                this.getAction('properties'),
                this.getAction('add'),
                this.getAction('delete'),
                this.getAction('copy'),
                this.getAction('paste'),
                this.getAction('visible'),
                this.getAction('makeHome'),
                this.getAction('preview')
            ]
        });
        this.pageButton = new Ext.Toolbar.Button({
            cls     : 'x-btn-text-icon bmenu',
            text    : trlVps('Page'),
            menu    : this.pageButtonMenu,
            icon    : '/assets/silkicons/page.png',
            disabled: true
        });

        this.treePanel = new Vps.Auto.TreePanel({
            controllerUrl: '/admin/component/pages',
            title       : trlVps('Seitenbaum'),
            region      : 'west',
            split       : true,
            width       : 300,
            collapsible : true,
            minSize     : 200,
            maxSize     : 600,
            autoScroll: true,
            tbar: []
        });

        var tbar = this.treePanel.getTopToolbar();
        tbar.add(this.pageButton);

        tbar.add('-');
        tbar.add(trlVps('Search: '));

        var filter = new Vps.Auto.Filter.Text({name: 'text', 'paramName': 'query'});
        filter.on('filter', function(f, params) {
            this.applyBaseParams(params);
            this.reload();
        }, this.treePanel);
        filter.getToolbarItem().each(function(i) {
            tbar.add(i);
        });

        tbar.add('->');
        tbar.add(this.treePanel.getAction('reload'));

        this.treePanel.onMoved = function (response) {
            this.tree.getRootNode().reload();
        };

        this.contentTabPanel = new Ext.TabPanel({
            region      : 'center'
        });
        this.contentTabPanel.on('render', function() {
            this.contentTabPanel.strip.setHeight(24);
        }, this);

        this.layout = 'border';
        this.items = [this.treePanel, this.contentTabPanel];

        Vps.Component.Pages.superclass.initComponent.call(this);

        this.treePanel.on('loaded', this.onTreePanelLoaded, this);
        this.setupEditform();

        this.editActions = {};
    },
    setupEditform : function ()
    {
       this.editDialog = new Vps.Auto.Form.Window({
            width: 600,
            height: 400,
            controllerUrl: '/admin/component/page'
        });
        this.editDialog.on('datachange', function(test) {
            this.treePanel.tree.root.reload();
        }, this);
        this.editDialog.on('renderform', function() {
            var component = this.editDialog.getAutoForm().findField('component');
            if (component && component.formsForComponent) {
                //initially hide all to avoid flickr
                this.editDialog.getAutoForm().cascade(function(i) {
                    if (i.showDependingOnComponent) {
                        i.disableRecursive(); //to disable validation
                        i.hide();
                    }
                }, this);
                component.on('changevalue', function() {
                    //hide/show forms depending on selected component
                    this.editDialog.getAutoForm().cascade(function(i) {
                        var showForms = component.formsForComponent[component.getValue()] || [];
                        if (i.showDependingOnComponent) {
                            if (showForms.indexOf(i.name) !== -1) {
                                i.show();
                                i.enableRecursive();
                            } else {
                                i.disableRecursive(); //to disable validation
                                i.hide();
                            }
                        }
                    }, this);
                }, this);
            }
        }, this);
    },

    onTreePanelLoaded : function(tree)
    {
        tree.loader.on('load', function(tree, node, response) {
            var result = Ext.decode(response.responseText);
            Ext.applyIf(this.componentConfigs, result.componentConfigs);
        }, this);

        this.contextMenu = new Ext.menu.Menu({
             items: [
                '-',
                this.getAction('properties'),
                this.getAction('add'),
                this.getAction('delete'),
                this.getAction('copy'),
                this.getAction('paste'),
                this.getAction('visible'),
                this.getAction('makeHome'),
                this.getAction('preview')
            ]
        });

        tree.on('click', this.treeSelectionchange, this);
        tree.on('contextmenu', function (node) {
            node.select();
            this.treeSelectionchange(node);
            this.contextMenu.show(node.ui.getAnchor());
        }, this);

        tree.on('dblclick', function (o, e) {
            var i = this.contextMenu.items.first();
            if (i) {
                i.handler.call(i.scope, i, e);
            }
        }, this);

    },

    treeSelectionchange : function (node) {
        if (!node) return;
        var data = node.attributes;

        if (data.disabled) {
            this.pageButton.disable();
        } else {
            this.pageButton.enable();
        }

        for (var action in data.actions) {
            if (data.actions[action]) {
                this.getAction(action).enable();
            } else {
                this.getAction(action).disable();
            }
        }

        this.contextMenu.items.each(function(i) {
            if (i.initialConfig.actionKey) {
                this.contextMenu.remove(i);
            }
        }, this);
        this.pageButtonMenu.items.each(function(i) {
            if (i.initialConfig.actionKey) {
                this.pageButtonMenu.remove(i);
            }
        }, this);
        var actionsAdded = 0;
        data.editComponents.each(function(editComponent) {
        	var editKey = editComponent.componentId;
            var actionKey = editComponent.componentClass+'-'+editComponent.type;
            if (!this.editActions[editKey]) {
                this.editActions[editKey] = new Ext.Action({
                    text    : this.componentConfigs[actionKey].title,
                    handler : function (o, e) {
                        var node = this.treePanel.tree.getSelectionModel().getSelectedNode();
                        node.attributes.editComponents.each(function(editComponent) {
                        	if (editComponent.componentId == o.editKey) {
                                this.loadComponent({
                                    id: editComponent.componentId,
                                    componentClass: editComponent.componentClass,
                                    type: editComponent.type,
                                    text: node.text,
                                    icon: node.attributes.bIcon,
                                    editComponents: node.attributes.editComponents,
                                    pageId: node.attributes.id
                                });
                                return false;
                            }
                        }, this);
                    },
                    icon    : this.componentConfigs[actionKey].icon,
                    cls     : 'x-btn-text-icon',
                    scope   : this,
                    actionKey: actionKey,
                    editKey: editKey
                });
            }
            this.editActions[editKey].setDisabled(data.disabled);
            this.contextMenu.insert(actionsAdded, new Ext.menu.Item(this.editActions[editKey]));
            this.pageButtonMenu.insert(actionsAdded, new Ext.menu.Item(this.editActions[editKey]));
            actionsAdded++;
        }, this);
    },

    loadComponent: function(data)
    {
        var panel = this.contentTabPanel.getItem('page'+data.pageId);
        if (!panel) {
            panel = this.createComponentPanel(data);
            this.contentTabPanel.add(panel);
        }
        this.contentTabPanel.setActiveTab(panel);

        panel.applyBaseParams({
            id: data.id
        });
        panel.mainComponentText = data.text;
        panel.mainComponentIcon = data.icon;
        panel.load({
            componentClass: data.componentClass,
            type: data.type,
            editComponents: data.editComponents
        });
    },

    createComponentPanel: function(data)
    {
        var panel = new Vps.Component.ComponentPanel({
            id          : 'page'+data.pageId,
            title       : data.text,
            closable    : true,
            autoLoad    : false,
            componentConfigs : this.componentConfigs
        });
        return panel;
    },

    _removeEditDialogForm: function()
    {
        var form = this.editDialog.getAutoForm();
        if (form.formPanel) {
            form.remove(form.formPanel, true);
            this.editDialog.getAutoForm().formPanel = null;
        }
    },

    getAction : function(type)
    {
        if (this.actions[type]) return this.actions[type];

        if (type == 'properties') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Page properties'),
                handler : function () {
                    this._removeEditDialogForm();
                    var node = this.treePanel.tree.selModel.selNode;
                    this.editDialog.getAutoForm().setBaseParams({
                        componentId: node.attributes.editControllerComponentId
                    });
                    this.editDialog.showEdit(node.id);
                },
                icon    : '/assets/silkicons/page_gear.png',
                cls     : 'x-btn-text-icon',
                scope   : this
            });
        } else if (type == 'add') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Add new child page'),
                handler : function () {
                    this._removeEditDialogForm();
                    var node = this.treePanel.tree.selModel.selNode;
                    this.editDialog.getAutoForm().setBaseParams({
                        componentId: node.attributes.editControllerComponentId,
                        parent_id: node.id
                    });
                    this.editDialog.showAdd();
                },
                icon    : '/assets/silkicons/page_add.png',
                cls     : 'x-btn-text-icon',
                scope   : this
            });
        } else if (type == 'delete') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Delete page'),
                handler : function() {
                    this.treePanel.onDelete();
                },
                icon    : '/assets/silkicons/page_delete.png',
                cls     : 'x-btn-text-icon',
                scope   : this
            });
        } else if (type == 'copy') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Copy page'),
                handler : function() {
                    this.onCopy();
                },
                icon    : '/assets/silkicons/page_copy.png',
                cls     : 'x-btn-text-icon',
                scope   : this
            });
        } else if (type == 'paste') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Paste page'),
                handler : function() {
                    this.onPaste();
                },
                icon    : '/assets/silkicons/page_paste.png',
                cls     : 'x-btn-text-icon',
                scope   : this
            });
        } else if (type == 'visible') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Show / hide page'),
                handler : function() {
                    this.treePanel.onVisible();
                },
                icon    : '/assets/fx_invisible/silkicons/page.png',
                cls     : 'x-btn-text-icon',
                scope   : this
            });
        } else if (type == 'makeHome') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Use as homepage'),
                handler : function (o, e) {
                    Ext.Msg.show({
                        title: trlVps('Use as homepage'),
                        msg: trlVps('Attention! You are about to set the selected page as the homepage of your website. This may affect the entire website. Do you wish to proceed?'),
                        buttons: Ext.Msg.YESNO,
                        icon: Ext.MessageBox.WARNING,
                        fn: function(btn, text) {
                            if (btn == 'yes') {
                                Ext.Ajax.request({
                                    url: '/admin/component/pages/json-make-home',
                                    success: function(r) {
                                        response = Ext.decode(r.responseText);
                                        var oldhome = this.treePanel.tree.getNodeById(response.oldhome);
                                        oldhome.attributes.visible = response.oldhomeVisible;
                                        this.treePanel.setVisible(oldhome);
                                        oldhome.ui.iconNode.style.backgroundImage = 'url(/assets/silkicons/page.png)';
                                        var home = this.treePanel.tree.getNodeById(response.home);
                                        home.ui.iconNode.style.backgroundImage = 'url(/assets/silkicons/application_home.png)';
                                    },
                                    params: {id: this.treePanel.tree.getSelectionModel().getSelectedNode().id },
                                    scope: this
                                });
                            }
                        },
                        scope: this
                    });
                },
                icon    : '/assets/silkicons/application_home.png',
                cls     : 'x-btn-text-icon',
                scope   : this
            });
        } else if (type == 'expand') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Expand here'),
                handler : function () { this.treePanel.tree.getSelectionModel().getSelectedNode().expand(true); },
                icon    : '/assets/silkicons/bullet_add.png',
                cls     : 'x-btn-text-icon',
                scope   : this
            });
        } else if (type == 'collapse') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Collapse here'),
                handler : function () { this.treePanel.tree.getSelectionModel().getSelectedNode().collapse(true); },
                icon    : '/assets/silkicons/bullet_delete.png',
                cls     : 'x-btn-text-icon',
                scope   : this
            });
        } else if (type == 'preview') {
            this.actions[type] = new Ext.Action({
                text    : trlVps('Open preview'),
                handler : function () {
                    window.open('/admin/component/pages/open-preview?page_id='+
                                this.treePanel.getSelectedId());
                },
                icon    : '/assets/silkicons/page_white_magnify.png',
                cls     : 'x-btn-text-icon',
                scope   : this
            });
        } else {
            throw 'unknown action-type: ' + type;
        }
        return this.actions[type];
    },

    onCopy: function(o, e) {
        Ext.Ajax.request({
            url: this.treePanel.controllerUrl + '/json-copy',
            params: Ext.apply({id:this.treePanel.getSelectedId()}, this.treePanel.getBaseParams()),
            mask: this.el,
            success: function(response, options, result) {
            },
            scope: this
        });
    },

    onPaste: function() {
        Ext.Ajax.request({
            url: this.treePanel.controllerUrl + '/json-paste',
            params: Ext.apply({id:this.treePanel.getSelectedId()}, this.treePanel.getBaseParams()),
            timeout: 5*60*1000,
            progress: true,
            progressTitle : trlVps('Paste Page'),
            showCancel: false,
            success: function(response, options, result) {
                this.treePanel.reload();
            },
            scope: this
        });
    }

});

Ext.reg('vps.component.pages', Vps.Component.Pages);
<|endoftext|>


<commit_before>'use strict';

// MODULES //

var tape = require( 'tape' );
var noop = require( '@stdlib/utils/noop' );
var whileEach = require( './../lib' );

// TESTS //

tape( 'main export is a function', function test( t ) {
	t.ok( true, __filename );
	t.strictEqual( typeof whileEach, 'function', 'main export is a function' );
	t.end();
});

tape( 'the function throws an error if not provided a collection', function test( t ) {
	var values;
	var i;

	values = [
		'5',
		5,
		NaN,
		true,
		false,
		null,
		undefined,
		{},
		function noop() {},
		/.*/,
		new Date()
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws a type error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function badValue() {
			whileEach( value, noop, noop );
		};
	}
});

tape( 'the function throws an error if not provided a predicate function', function test( t ) {
	var values;
	var i;

	values = [
		'5',
		5,
		NaN,
		true,
		false,
		null,
		undefined,
		{},
		[],
		/.*/,
		new Date()
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws a type error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function badValue() {
			whileEach( [ 1, 2, 3 ], value, noop );
		};
	}
});

tape( 'the function throws an error if not provided a function to invoke', function test( t ) {
	var values;
	var i;

	values = [
		'5',
		5,
		NaN,
		true,
		false,
		null,
		undefined,
		{},
		[],
		/.*/,
		new Date()
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws a type error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function badValue() {
			whileEach( [ 1, 2, 3 ], noop, value );
		};
	}
});

tape( 'if provided an empty collection, the function never invokes a provided function', function test( t ) {
	var arr = [];

	function predicate() {
		t.fail( 'should not be invoked' );
	}

	function foo() {
		t.fail( 'should not be invoked' );
	}

	whileEach( arr, predicate, foo );

	t.deepEqual( arr, [], 'expected result' );
	t.end();
});

tape( 'the function returns the input collection', function test( t ) {
	var arr;
	var out;

	function predicate() {
		t.pass( 'invoked predicated function' );
		return true;
	}

	function foo() {
		t.pass( 'invoked provided function' );
	}

	arr = [ 1, 2, 3 ];

	out = whileEach( arr, predicate, foo );

	t.strictEqual( out, arr, 'returns input collection' );
	t.end();
});

tape( 'while a test condition is true, the function invokes a provided function for each element in a collection (array)', function test( t ) {
	var arr;
	var out;

	arr = [ 1, 2, NaN, 3 ];
	out = [ 0, 0, 0, 0 ];

	function predicate( value ) {
		return ( value === value );
	}

	function copy( value, index ) {
		out[ index ] = value;
	}

	whileEach( arr, predicate, copy );

	t.deepEqual( out, [ 1, 2, 0, 0 ], 'expected result' );
	t.end();
});

tape( 'while a test condition is true, the function invokes a provided function for each element in a collection (array-like object)', function test( t ) {
	var expected;
	var arr;
	var out;

	function predicate( value ) {
		return ( value === value );
	}

	function copy( value, index ) {
		out[ index ] = value;
	}

	arr = {
		'length': 3,
		'0': 1,
		'1': 2,
		'2': NaN,
		'3': 3
	};
	out = {
		'length': 3,
		'0': 0,
		'1': 0,
		'2': 0,
		'3': 0
	};

	whileEach( arr, predicate, copy );

	expected = {
		'length': 3,
		'0': 1,
		'1': 2,
		'2': 0,
		'3': 0
	};

	t.deepEqual( out, expected, 'expected result' );
	t.end();
});

tape( 'while a test condition is true, the function invokes a provided function for each element in a collection (typed array)', function test( t ) {
	var arr;
	var out;

	function predicate( value ) {
		return ( value === value );
	}

	function copy( value, index ) {
		out[ index ] = value;
	}

	arr = new Float64Array( [ 1.0, 2.0, NaN, 3.0 ] );
	out = new Float32Array( [ 0.0, 0.0, 0.0, 0.0 ] );

	whileEach( arr, predicate, copy );

	t.strictEqual( out[ 0 ], 1.0, 'expected result' );
	t.strictEqual( out[ 1 ], 2.0, 'expected result' );
	t.strictEqual( out[ 2 ], 0.0, 'expected result' );
	t.strictEqual( out[ 3 ], 0.0, 'expected result' );

	t.end();
});

tape( 'the function supports providing an execution context', function test( t ) {
	var ctx;
	var arr;

	function predicate( value ) {
		return ( value === value );
	}

	function sum( value ) {
		/* eslint-disable no-invalid-this */
		this.sum += value;
		this.count += 1;
	}

	ctx = {
		'sum': 0,
		'count': 0
	};
	arr = [ 1.0, 2.0, 3.0 ];

	whileEach( arr, predicate, sum, ctx );

	t.strictEqual( ctx.sum/ctx.count, 2.0, 'expected result' );

	t.end();
});

tape( 'the function provides basic support for dynamic arrays', function test( t ) {
	var arr;
	var out;

	function predicate( value ) {
		return ( value === value );
	}

	function copy( value, index, collection ) {
		out.push( value );
		if ( index === collection.length-1 && collection.length < 10 ) {
			collection.push( index+2 );
		}
	}

	arr = [ 1, 2, 3 ];
	out = [];

	whileEach( arr, predicate, copy );

	t.deepEqual( arr, [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ], 'expected result' );
	t.deepEqual( out, [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ], 'expected result' );
	t.end();
});

tape( 'the function does not skip empty elements', function test( t ) {
	var expected;
	var arr;

	function predicate( value, index ) {
		t.strictEqual( value, expected[ index ], 'provides expected value' );
		return true;
	}

	function verify( value, index ) {
		t.strictEqual( value, expected[ index ], 'provides expected value' );
	}

	arr = [ 1, , , 4 ]; // eslint-disable-line no-sparse-arrays
	expected = [ 1, void 0, void 0, 4 ];

	whileEach( arr, predicate, verify );
	t.end();
});

tape( 'the function invokes the predicate function and the function to apply with three arguments: value, index, collection', function test( t ) {
	var indices;
	var values;
	var arr;

	function predicate( value, index, collection ) {
		t.strictEqual( value, values[ index ], 'provides expected value' );
		t.strictEqual( index, indices[ index ], 'provides expected index' );
		t.strictEqual( collection, arr, 'provides input collection' );
		return true;
	}

	function verify( value, index, collection ) {
		t.strictEqual( value, values[ index ], 'provides expected value' );
		t.strictEqual( index, indices[ index ], 'provides expected index' );
		t.strictEqual( collection, arr, 'provides input collection' );
	}

	arr = [ 1, 2, 3, 4 ];
	values = [ 1, 2, 3, 4 ];
	indices = [ 0, 1, 2, 3 ];

	whileEach( arr, predicate, verify );
	t.end();
});
<commit_msg>Fix length values<commit_after>'use strict';

// MODULES //

var tape = require( 'tape' );
var noop = require( '@stdlib/utils/noop' );
var whileEach = require( './../lib' );

// TESTS //

tape( 'main export is a function', function test( t ) {
	t.ok( true, __filename );
	t.strictEqual( typeof whileEach, 'function', 'main export is a function' );
	t.end();
});

tape( 'the function throws an error if not provided a collection', function test( t ) {
	var values;
	var i;

	values = [
		'5',
		5,
		NaN,
		true,
		false,
		null,
		undefined,
		{},
		function noop() {},
		/.*/,
		new Date()
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws a type error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function badValue() {
			whileEach( value, noop, noop );
		};
	}
});

tape( 'the function throws an error if not provided a predicate function', function test( t ) {
	var values;
	var i;

	values = [
		'5',
		5,
		NaN,
		true,
		false,
		null,
		undefined,
		{},
		[],
		/.*/,
		new Date()
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws a type error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function badValue() {
			whileEach( [ 1, 2, 3 ], value, noop );
		};
	}
});

tape( 'the function throws an error if not provided a function to invoke', function test( t ) {
	var values;
	var i;

	values = [
		'5',
		5,
		NaN,
		true,
		false,
		null,
		undefined,
		{},
		[],
		/.*/,
		new Date()
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws a type error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function badValue() {
			whileEach( [ 1, 2, 3 ], noop, value );
		};
	}
});

tape( 'if provided an empty collection, the function never invokes a provided function', function test( t ) {
	var arr = [];

	function predicate() {
		t.fail( 'should not be invoked' );
	}

	function foo() {
		t.fail( 'should not be invoked' );
	}

	whileEach( arr, predicate, foo );

	t.deepEqual( arr, [], 'expected result' );
	t.end();
});

tape( 'the function returns the input collection', function test( t ) {
	var arr;
	var out;

	function predicate() {
		t.pass( 'invoked predicated function' );
		return true;
	}

	function foo() {
		t.pass( 'invoked provided function' );
	}

	arr = [ 1, 2, 3 ];

	out = whileEach( arr, predicate, foo );

	t.strictEqual( out, arr, 'returns input collection' );
	t.end();
});

tape( 'while a test condition is true, the function invokes a provided function for each element in a collection (array)', function test( t ) {
	var arr;
	var out;

	arr = [ 1, 2, NaN, 3 ];
	out = [ 0, 0, 0, 0 ];

	function predicate( value ) {
		return ( value === value );
	}

	function copy( value, index ) {
		out[ index ] = value;
	}

	whileEach( arr, predicate, copy );

	t.deepEqual( out, [ 1, 2, 0, 0 ], 'expected result' );
	t.end();
});

tape( 'while a test condition is true, the function invokes a provided function for each element in a collection (array-like object)', function test( t ) {
	var expected;
	var arr;
	var out;

	function predicate( value ) {
		return ( value === value );
	}

	function copy( value, index ) {
		out[ index ] = value;
	}

	arr = {
		'length': 4,
		'0': 1,
		'1': 2,
		'2': NaN,
		'3': 3
	};
	out = {
		'length': 4,
		'0': 0,
		'1': 0,
		'2': 0,
		'3': 0
	};

	whileEach( arr, predicate, copy );

	expected = {
		'length': 4,
		'0': 1,
		'1': 2,
		'2': 0,
		'3': 0
	};

	t.deepEqual( out, expected, 'expected result' );
	t.end();
});

tape( 'while a test condition is true, the function invokes a provided function for each element in a collection (typed array)', function test( t ) {
	var arr;
	var out;

	function predicate( value ) {
		return ( value === value );
	}

	function copy( value, index ) {
		out[ index ] = value;
	}

	arr = new Float64Array( [ 1.0, 2.0, NaN, 3.0 ] );
	out = new Float32Array( [ 0.0, 0.0, 0.0, 0.0 ] );

	whileEach( arr, predicate, copy );

	t.strictEqual( out[ 0 ], 1.0, 'expected result' );
	t.strictEqual( out[ 1 ], 2.0, 'expected result' );
	t.strictEqual( out[ 2 ], 0.0, 'expected result' );
	t.strictEqual( out[ 3 ], 0.0, 'expected result' );

	t.end();
});

tape( 'the function supports providing an execution context', function test( t ) {
	var ctx;
	var arr;

	function predicate( value ) {
		return ( value === value );
	}

	function sum( value ) {
		/* eslint-disable no-invalid-this */
		this.sum += value;
		this.count += 1;
	}

	ctx = {
		'sum': 0,
		'count': 0
	};
	arr = [ 1.0, 2.0, 3.0 ];

	whileEach( arr, predicate, sum, ctx );

	t.strictEqual( ctx.sum/ctx.count, 2.0, 'expected result' );

	t.end();
});

tape( 'the function provides basic support for dynamic arrays', function test( t ) {
	var arr;
	var out;

	function predicate( value ) {
		return ( value === value );
	}

	function copy( value, index, collection ) {
		out.push( value );
		if ( index === collection.length-1 && collection.length < 10 ) {
			collection.push( index+2 );
		}
	}

	arr = [ 1, 2, 3 ];
	out = [];

	whileEach( arr, predicate, copy );

	t.deepEqual( arr, [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ], 'expected result' );
	t.deepEqual( out, [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ], 'expected result' );
	t.end();
});

tape( 'the function does not skip empty elements', function test( t ) {
	var expected;
	var arr;

	function predicate( value, index ) {
		t.strictEqual( value, expected[ index ], 'provides expected value' );
		return true;
	}

	function verify( value, index ) {
		t.strictEqual( value, expected[ index ], 'provides expected value' );
	}

	arr = [ 1, , , 4 ]; // eslint-disable-line no-sparse-arrays
	expected = [ 1, void 0, void 0, 4 ];

	whileEach( arr, predicate, verify );
	t.end();
});

tape( 'the function invokes the predicate function and the function to apply with three arguments: value, index, collection', function test( t ) {
	var indices;
	var values;
	var arr;

	function predicate( value, index, collection ) {
		t.strictEqual( value, values[ index ], 'provides expected value' );
		t.strictEqual( index, indices[ index ], 'provides expected index' );
		t.strictEqual( collection, arr, 'provides input collection' );
		return true;
	}

	function verify( value, index, collection ) {
		t.strictEqual( value, values[ index ], 'provides expected value' );
		t.strictEqual( index, indices[ index ], 'provides expected index' );
		t.strictEqual( collection, arr, 'provides input collection' );
	}

	arr = [ 1, 2, 3, 4 ];
	values = [ 1, 2, 3, 4 ];
	indices = [ 0, 1, 2, 3 ];

	whileEach( arr, predicate, verify );
	t.end();
});
<|endoftext|>


<commit_before>/** @module b3e */

(function () {
  'use strict';

  b3e.node.create = function(name, category, kwargs) {
    kwargs = kwargs || {};

    // Default
    var title = kwargs.title || name;
    var description = kwargs.description || null;
    var properties = kwargs.properties || [];
    var maxInConnections = -1;
    var maxOutConnections = -1;

    if (typeof kwargs.maxInConnections !== 'undefined')
      maxInConnections = kwargs.maxInConnections;

    if (typeof kwargs.maxOutConnections !== 'undefined')
      maxOutConnections = kwargs.maxOutConnections;

    // Static
    var _static = {
      title: title,
      category: category,
      description: description,
      properties: properties,
      maxInConnections: maxInConnections,
      maxOutConnections: maxOutConnections,
    };

    // Prototype
    var _prototype = {
      name: name,
      title: title,
      category: category,
      description: description,
      maxInConnections: maxInConnections,
      maxOutConnections: maxOutConnections,
    };

    if (kwargs.onAdd) _prototype.onAdd = kwargs.onAdd;
    if (kwargs.onLoad) _prototype.onLoad = kwargs.onLoad;
    if (kwargs.onSelect) _prototype.onSelect = kwargs.onSelect;
    if (kwargs.onDeselect) _prototype.onDeselect = kwargs.onDeselect;
    if (kwargs.onConnected) _prototype.onConnected = kwargs.onConnected;
    if (kwargs.onInConnected) _prototype.onInConnected = kwargs.onInConnected;
    if (kwargs.onOutConnected) _prototype.onOutConnected = kwargs.onOutConnected;
    if (kwargs.onDisconnected) _prototype.onDisconnected = kwargs.onDisconnected;
    if (kwargs.onInDisconnected) _prototype.onInDisconnected = kwargs.onInDisconnected;
    if (kwargs.onOutDisconnected) _prototype.onOutDisconnected = kwargs.onOutDisconnected;
    if (kwargs.onPropertyChange) _prototype.onPropertyChange = kwargs.onPropertyChange;
    if (kwargs.onRemove) _prototype.onRemove = kwargs.onRemove;

    var Cls = new JS.Class(name, b3e.node.Node, _prototype);
    Cls.extend(_static);
    return Cls;
  };
})();<commit_msg>Nodes now can replace getTitle<commit_after>/** @module b3e */

(function () {
  'use strict';

  b3e.node.create = function(name, category, kwargs) {
    kwargs = kwargs || {};

    // Default
    var title = kwargs.title;
    var description = kwargs.description || null;
    var properties = kwargs.properties || [];
    var maxInConnections = -1;
    var maxOutConnections = -1;

    if (typeof kwargs.maxInConnections !== 'undefined')
      maxInConnections = kwargs.maxInConnections;

    if (typeof kwargs.maxOutConnections !== 'undefined')
      maxOutConnections = kwargs.maxOutConnections;

    // Static
    var _static = {
      title: title,
      category: category,
      description: description,
      properties: properties,
      maxInConnections: maxInConnections,
      maxOutConnections: maxOutConnections,
    };

    // Prototype
    var _prototype = {
      name: name,
      title: title,
      category: category,
      description: description,
      maxInConnections: maxInConnections,
      maxOutConnections: maxOutConnections,
    };

    if (kwargs.onAdd) _prototype.onAdd = kwargs.onAdd;
    if (kwargs.onLoad) _prototype.onLoad = kwargs.onLoad;
    if (kwargs.onSelect) _prototype.onSelect = kwargs.onSelect;
    if (kwargs.onDeselect) _prototype.onDeselect = kwargs.onDeselect;
    if (kwargs.onConnected) _prototype.onConnected = kwargs.onConnected;
    if (kwargs.onInConnected) _prototype.onInConnected = kwargs.onInConnected;
    if (kwargs.onOutConnected) _prototype.onOutConnected = kwargs.onOutConnected;
    if (kwargs.onDisconnected) _prototype.onDisconnected = kwargs.onDisconnected;
    if (kwargs.onInDisconnected) _prototype.onInDisconnected = kwargs.onInDisconnected;
    if (kwargs.onOutDisconnected) _prototype.onOutDisconnected = kwargs.onOutDisconnected;
    if (kwargs.onPropertyChange) _prototype.onPropertyChange = kwargs.onPropertyChange;
    if (kwargs.onRemove) _prototype.onRemove = kwargs.onRemove;
    if (kwargs.getTitle) _prototype.getTitle = kwargs.getTitle;

    var Cls = new JS.Class(name, b3e.node.Node, _prototype);
    Cls.extend(_static);
    return Cls;
  };
})();<|endoftext|>