<commit_before>function SliderInput(element, callback, xPad, yMin, yMax, min, max) {
    this.element = typeof element !== 'undefined' ? element : document;
    this.maxWidth = element.width || element.offsetWidth || element.clientWidth || window.innerWidth;
    this.callback = callback;
    this.xPad = xPad;
    this.yMin = yMin;
    this.yMax = yMax;
    this.min = min;
    this.max = max;
    this.value = min;
    this.listeners = {
        touchStart: this.startTouch.bind(this),
        touchMove: this.determineValue.bind(this),
        touchEnd: this.endTouch.bind(this),
        keyDown: this.setValue.bind(this)
    };
}

SliderInput.prototype.listen = function() {
    this.element.addEventListener('touchstart', this.listeners.touchStart);
    this.element.addEventListener('touchmove', this.listeners.touchMove);
    this.element.addEventListener('touchend', this.listeners.touchEnd);
    document.addEventListener('keydown', this.listeners.keyDown);
};

SliderInput.prototype.startTouch = function(event) {
    event.preventDefault();
    event.stopPropagation();
    this.xStart = event.touches[0].clientX;
    this.yStart = event.touches[0].clientY;
};

SliderInput.prototype.determineValue = function(event) {
    event.preventDefault();
    event.stopPropagation();
    if( this.xStart === null || this.yStart < this.yMin || this.yStart > this.yMax ) {
        return;
    }
    var xEnd = event.touches[0].clientX;
    var yEnd = event.touches[0].clientY;
    if(Math.abs(this.yStart - yEnd) > Math.abs(this.xStart - xEnd) - 8 && (yEnd - this.yStart > 0)) {
        if( yEnd - this.yStart > 20 ) {
            this.onSelect(this.value);
        }
        return;
    }
    if(this.dispatchBeforeTouchValueChange(this.value, xEnd, yEnd)) {
        return;
    }
    var value = Math.max(this.min, Math.min(Math.round((((xEnd - this.xPad) * (this.max - this.min)) / (this.maxWidth - this.xPad * 2)) + this.min), this.max));
    this.value = value;
    if(typeof this.callback === 'function') {
        var x = Math.max(this.xPad, Math.min(xEnd, this.maxWidth - this.xPad));
        var y = event.touches[0].clientY;
        this.lastX = x;
        this.lastY = y;
        this.callback(value, x, y, false);
    }
};

SliderInput.prototype.endTouch = function(event) {
    this.xStart = null;
    this.yStart = null;
    this.callback(this.value, this.lastX, this.lastY, true);
}

SliderInput.prototype.dispatchBeforeTouchValueChange = function(value, xEnd, yEnd) {
    if(!(typeof this.beforeTouchValueChange === 'function')) {
        return;
    }
    return this.beforeTouchValueChange({currentValue: value, xStart: this.xStart, yStart: this.yStart, xEnd: xEnd, yEnd: yEnd });
}

SliderInput.prototype.onSelect = function(value) {};

SliderInput.prototype.setValue = function(event) {
    var keyCode = event.keyCode;
    //For number input - tries to append digit if within range
    if(keyCode >= 48 && keyCode <= 57 || keyCode >= 96 && keyCode <= 105) {
       var zeroCode = keyCode <= 57 ? 48 : 96;
       var number = keyCode - zeroCode;
       var value = this.value.toString();
       value += number.toString();
       this.value = value > this.max ? Math.min(number, this.max) : parseInt(value);
    //Backspace input - let's do it mathematically
    } else if(keyCode === 8) {
        event.preventDefault();
       this.value = Math.floor(this.value / 10);
    //Left and right arrow keys - treat it like a number line
    } else {
        switch(event.keyCode) {
            //Left
            case 37 :
            case 65 :
            case 72 :
                if(this.value > this.min) {
                    this.value -= 1;
                }
                break;
            //Right
            case 39 :
            case 68 :
            case 76 :
                if(this.value < this.max) {
                    this.value += 1;
                }
                break;
            //Down or enter
            case 40 :
            case 74 :
            case 83 :
            case 13 :
              this.onSelect(this.value);
              return;
        }
    }
    var x = Math.max(this.value, this.min) * ((this.maxWidth - this.xPad * 2) / this.max) + this.xPad;
    this.callback(this.value, x, null, true);
};

SliderInput.prototype.triggerInitial = function(value) {
    var x = Math.max(value, this.min) * ((this.maxWidth - this.xPad * 2) / this.max) + this.xPad;
    this.lastX = x;
    this.value = value;
    this.callback(value, x, null, true);
};

SliderInput.prototype.detach = function() {
    this.element.removeEventListener('touchstart', this.listeners.touchStart);
    this.element.removeEventListener('touchmove', this.listeners.touchMove);
    this.element.removeEventListener('touchend', this.listeners.touchEnd);
    document.removeEventListener('keydown', this.listeners.keyDown);
};
<commit_msg>Improve level select slider touch response<commit_after>function SliderInput(element, callback, xPad, yMin, yMax, min, max) {
    this.element = typeof element !== 'undefined' ? element : document;
    this.maxWidth = element.width || element.offsetWidth || element.clientWidth || window.innerWidth;
    this.callback = callback;
    this.xPad = xPad;
    this.yMin = yMin;
    this.yMax = yMax;
    this.min = min;
    this.max = max;
    this.value = min;
    this.listeners = {
        touchStart: this.startTouch.bind(this),
        touchMove: this.determineValue.bind(this),
        touchEnd: this.endTouch.bind(this),
        keyDown: this.setValue.bind(this)
    };
}

SliderInput.prototype.listen = function() {
    this.element.addEventListener('touchstart', this.listeners.touchStart);
    this.element.addEventListener('touchmove', this.listeners.touchMove);
    this.element.addEventListener('touchend', this.listeners.touchEnd);
    document.addEventListener('keydown', this.listeners.keyDown);
};

SliderInput.prototype.startTouch = function(event) {
    event.preventDefault();
    event.stopPropagation();
    this.xStart = event.touches[0].clientX;
    this.yStart = event.touches[0].clientY;
};

SliderInput.prototype.determineValue = function(event) {
    event.preventDefault();
    event.stopPropagation();
    if(this.xStart === null || this.yStart < this.yMin || this.yStart > this.yMax) {
        return;
    }
    var xEnd = event.touches[0].clientX;
    var yEnd = event.touches[0].clientY;
    if(Math.abs(this.yStart - yEnd) > Math.abs(this.xStart - xEnd) - 8 && (yEnd - this.yStart > 0)) {
        return;
    }
    var value = Math.max(this.min, Math.min(Math.round((((xEnd - this.xPad) * (this.max - this.min)) / (this.maxWidth - this.xPad * 2)) + this.min), this.max));
    this.value = value;
    if(typeof this.callback === 'function') {
        var x = Math.max(this.xPad, Math.min(xEnd, this.maxWidth - this.xPad));
        var y = event.touches[0].clientY;
        this.lastX = x;
        this.lastY = y;
        this.callback(value, x, y, false);
    }
};

SliderInput.prototype.endTouch = function(event) {
    var xEnd = event.changedTouches[0].clientX;
    var yEnd = event.changedTouches[0].clientY;
    if(Math.abs(this.yStart - yEnd) > Math.abs(this.xStart - xEnd) - 8 && (yEnd - this.yStart > 20)) {
        return this.onSelect(this.value);
    }
    this.xStart = null;
    this.yStart = null;
    this.callback(this.value, this.lastX, this.lastY, true);
};

SliderInput.prototype.onSelect = function(value) {};

SliderInput.prototype.setValue = function(event) {
    var keyCode = event.keyCode;
    //For number input - tries to append digit if within range
    if(keyCode >= 48 && keyCode <= 57 || keyCode >= 96 && keyCode <= 105) {
       var zeroCode = keyCode <= 57 ? 48 : 96;
       var number = keyCode - zeroCode;
       var value = this.value.toString();
       value += number.toString();
       this.value = value > this.max ? Math.min(number, this.max) : parseInt(value);
    //Backspace input - let's do it mathematically
    } else if(keyCode === 8) {
        event.preventDefault();
       this.value = Math.floor(this.value / 10);
    //Left and right arrow keys - treat it like a number line
    } else {
        switch(event.keyCode) {
            //Left
            case 37 :
            case 65 :
            case 72 :
                if(this.value > this.min) {
                    this.value -= 1;
                }
                break;
            //Right
            case 39 :
            case 68 :
            case 76 :
                if(this.value < this.max) {
                    this.value += 1;
                }
                break;
            //Down or enter
            case 40 :
            case 74 :
            case 83 :
            case 13 :
              this.onSelect(this.value);
              return;
        }
    }
    var x = Math.max(this.value, this.min) * ((this.maxWidth - this.xPad * 2) / this.max) + this.xPad;
    this.callback(this.value, x, null, true);
};

SliderInput.prototype.triggerInitial = function(value) {
    var x = Math.max(value, this.min) * ((this.maxWidth - this.xPad * 2) / this.max) + this.xPad;
    this.lastX = x;
    this.value = value;
    this.callback(value, x, null, true);
};

SliderInput.prototype.detach = function() {
    this.element.removeEventListener('touchstart', this.listeners.touchStart);
    this.element.removeEventListener('touchmove', this.listeners.touchMove);
    this.element.removeEventListener('touchend', this.listeners.touchEnd);
    document.removeEventListener('keydown', this.listeners.keyDown);
};
<|endoftext|>


<commit_before>/**
 * Comparator used to sort search results by score in descending order.
 *
 * @param {object} a
 * @param {object}} b
 */
const scoreComparator = (a, b) => b.score - a.score;

module.exports = scoreComparator;
<commit_msg>Elasticsearch: Fix typo<commit_after>/**
 * Comparator used to sort search results by score in descending order.
 *
 * @param {object} a
 * @param {object} b
 */
const scoreComparator = (a, b) => b.score - a.score;

module.exports = scoreComparator;
<|endoftext|>


<commit_before>define( ['dojo/_base/declare',
         'JBrowse/View/Track/Canvas',
         'JBrowse/View/Track/YScaleMixin'
        ],
        function( declare,  CanvasTrack, YScaleMixin ) {
var Wiggle = declare( CanvasTrack,
/**
 * @lends JBrowse.View.Track.Wiggle.prototype
 */
{
    constructor: function( args ) {
        this.inherited( arguments );
        this.store = args.store;
        this.store.whenReady( this, '_calculateScaling' );
        this.store.whenReady( this, 'loadSuccess' );
    },

    load: function() {
    },

    loadSuccess: function(o,url) {
        this.empty = this.store.empty || false;
        this.setLoaded();
    },

    _getView: function(scale) {
        if( !this._viewCache || this._viewCache.scale != scale ) {
            this._viewCache = {
                scale: scale,
                view: this.store.getView(1/scale)
            };
        }
        return this._viewCache.view;
    },

    makeWiggleYScale: function() {
        // bump minDisplayed to 0 if it is within 0.5% of it
        if( Math.abs( this.scale.min / this.scale.max ) < 0.005 )
            this.scale.min = 0;

        this.makeYScale({
            fixBounds: true,
            min: this.scale.min,
            max: this.scale.max
        });
        this.scale.min = this.ruler.scaler.bounds.lower;
        this.scale.max = this.ruler.scaler.bounds.upper;
        this.scale.range = this.scale.max - this.scale.min;
    },

    _calculateScaling: function() {
        var s = this.store.getStats();
        var min = 'min_score' in this.config ? this.config.min_score :
            (function() {
                 switch( this.config.autoscale ) {
                     case 'z_score':
                         return Math.max( -(this.config.z_score_bound || 4), (s.global_min-s.mean) / s.stdDev );
                     case 'global':
                         return s.global_min;
                     case 'clipped_global':
                     default:
                         return Math.max( s.global_min, s.mean - (this.config.z_score_bound || 4) * s.stdDev );
                 }
             }).call(this);
        var max = 'max_score' in this.config ? this.config.max_score :
            (function() {
                 switch( this.config.autoscale ) {
                     case 'z_score':
                         return Math.min( this.config.z_score_bound || 4, (s.global_max-s.mean) / s.stdDev );
                     case 'global':
                         return s.global_max;
                     case 'clipped_global':
                     default:
                         return Math.min( s.global_max, s.mean + (this.config.z_score_bound || 4) * s.stdDev );
                 }
             }).call(this);

        // if autoscale is set to z_score, config.scale should default to z_score
        if( this.config.autoscale == 'z_score' )
            this.config.scale = this.config.scale || 'z_score';

        // if we have a log scale, need to take the log of the min and max
        if( this.config.scale == 'log' ) {
            max = Math.log(max);
            min = min ? Math.log(min) : 0;
        }

        var offset = this.config.data_offset || 0;
        this.scale = {
            offset: offset,
            min: min + offset,
            max: max + offset,
            range: max - min
        };

        // make a func that converts wiggle values to Y coordinates on
        // the plot, depending on what kind of scale we are using
        this.scale.toY = function() {
            var scale = this.scale;
            switch( this.config.scale ) {
            case 'z_score':
                return function( canvasHeight, value ) {
                    with(scale)
                        return canvasHeight * (1-((value+offset-s.mean)/s.stdDev-min)/range);
                };
            case 'log':
                return function( canvasHeight, value ) {
                    with(scale)
                        return canvasHeight * (1-(Math.log(value+offset)-min)/range);
                };
            case 'linear':
            default:
                return function( canvasHeight, value ) {
                    with(scale)
                        return canvasHeight * (1-(value+offset-min)/range);
                };
            }
        }.call(this);

        return this.scale;
    },

    renderCanvases: function( scale, leftBase, rightBase, callback ) {
        if( ! callback ) {
            console.error('null callback?');
            return;
        }

        var canvasWidth  = Math.ceil(( rightBase - leftBase ) * scale);
        var canvasHeight = 100;
        this.height = canvasHeight;

        var dataFillStyle = (this.config.style||{}).dataFillStyle || '#00f';

        this._getView( scale )
            .readWigData( this.refSeq.name, leftBase, rightBase, dojo.hitch(this,function( features ) {
                if(! this.yscale )
                    this.makeWiggleYScale();

                var c = dojo.create(
                    'canvas',
                    { height: canvasHeight,
                      width:  canvasWidth,
                      innerHTML: 'Canvas-based tracks not supported by this browser'
                    }
                );
                c.startBase = leftBase;
                var context = c && c.getContext && c.getContext('2d');
                if( context ) {
                    var toY = dojo.hitch( this, this.scale.toY, canvasHeight );
                    if( this.config.variance_band )
                        (function() {
                             var stats = this.store.getStats();
                             var drawBand = function( plusminus, fill ) {
                                 context.fillStyle = fill;
                                 var varTop = toY( stats.mean + plusminus );
                                 var varHeight = toY( stats.mean - plusminus ) - varTop;
                                 varHeight = Math.max( 1, varHeight );
                                 context.fillRect( 0, varTop, c.width, varHeight );
                             };
                             drawBand( 2*stats.stdDev, 'rgba(0,0,0,0.15)' );
                             drawBand( stats.stdDev, 'rgba(0,0,0,0.25)' );
                             drawBand( 0,'yellow' );
                         }).call(this);

                    //context.fillText(features.length+' spans', 10,10);
                    context.fillStyle = dataFillStyle;
                    //console.log( 'filling '+leftBase+'-'+rightBase);
                    dojo.forEach(features, function(f) {
                        //console.log( f.get('start') +'-'+f.get('end')+':'+f.get('score') );
                        var rTop = toY( f.get('score') );
                        if( rTop <= canvasHeight ) {
                            var rWidth = Math.ceil(( f.get('end') - f.get('start') + 1 ) * scale );
                            var rLeft  = Math.floor(( f.get('start')-1 - leftBase ) * scale );
                            context.fillRect( rLeft, rTop, rWidth, canvasHeight-rTop );
//                            console.log('fillRect', rLeft, rTop, rWidth, canvasHeight-rTop );
                        }
                    }, this );
                }

                callback( [c] );
            }));
    },

    updateStaticElements: function( coords ) {
        this.inherited( arguments );
        this.updateYScaleFromViewDimensions( coords );
    }
});

/**
 * Mixin: JBrowse.View.Track.YScaleMixin.
 */
declare.safeMixin( Wiggle.prototype, YScaleMixin );

return Wiggle;
});
<commit_msg>make the wiggle draw in bicolor<commit_after>define( ['dojo/_base/declare',
         'JBrowse/View/Track/Canvas',
         'JBrowse/View/Track/YScaleMixin'
        ],
        function( declare,  CanvasTrack, YScaleMixin ) {
var Wiggle = declare( CanvasTrack,
/**
 * @lends JBrowse.View.Track.Wiggle.prototype
 */
{
    constructor: function( args ) {
        this.inherited( arguments );
        this.store = args.store;
        this.store.whenReady( this, '_calculateScaling' );
        this.store.whenReady( this, 'loadSuccess' );
    },

    load: function() {
    },

    loadSuccess: function(o,url) {
        this.empty = this.store.empty || false;
        this.setLoaded();
    },

    _getView: function(scale) {
        if( !this._viewCache || this._viewCache.scale != scale ) {
            this._viewCache = {
                scale: scale,
                view: this.store.getView(1/scale)
            };
        }
        return this._viewCache.view;
    },

    makeWiggleYScale: function() {
        // bump minDisplayed to 0 if it is within 0.5% of it
        if( Math.abs( this.scale.min / this.scale.max ) < 0.005 )
            this.scale.min = 0;

        this.makeYScale({
            fixBounds: true,
            min: this.scale.min,
            max: this.scale.max
        });
        this.scale.min = this.ruler.scaler.bounds.lower;
        this.scale.max = this.ruler.scaler.bounds.upper;
        this.scale.range = this.scale.max - this.scale.min;
    },

    _calculateScaling: function() {
        var s = this.store.getStats();
        var min = 'min_score' in this.config ? this.config.min_score :
            (function() {
                 switch( this.config.autoscale ) {
                     case 'z_score':
                         return Math.max( -(this.config.z_score_bound || 4), (s.global_min-s.mean) / s.stdDev );
                     case 'global':
                         return s.global_min;
                     case 'clipped_global':
                     default:
                         return Math.max( s.global_min, s.mean - (this.config.z_score_bound || 4) * s.stdDev );
                 }
             }).call(this);
        var max = 'max_score' in this.config ? this.config.max_score :
            (function() {
                 switch( this.config.autoscale ) {
                     case 'z_score':
                         return Math.min( this.config.z_score_bound || 4, (s.global_max-s.mean) / s.stdDev );
                     case 'global':
                         return s.global_max;
                     case 'clipped_global':
                     default:
                         return Math.min( s.global_max, s.mean + (this.config.z_score_bound || 4) * s.stdDev );
                 }
             }).call(this);

        // if autoscale is set to z_score, config.scale should default to z_score
        if( this.config.autoscale == 'z_score' )
            this.config.scale = this.config.scale || 'z_score';

        // if we have a log scale, need to take the log of the min and max
        if( this.config.scale == 'log' ) {
            max = Math.log(max);
            min = min ? Math.log(min) : 0;
        }

        var offset = this.config.data_offset || 0;
        this.scale = {
            offset: offset,
            min: min + offset,
            max: max + offset,
            range: max - min,
            origin: this.config.scale == 'z_score' ? s.mean : 0
        };

        // make a func that converts wiggle values to Y coordinates on
        // the plot, depending on what kind of scale we are using
        this.scale.toY = function() {
            var scale = this.scale;
            switch( this.config.scale ) {
            case 'z_score':
                return function( canvasHeight, value ) {
                    with(scale)
                        return canvasHeight * (1-((value+offset-s.mean)/s.stdDev-min)/range);
                };
            case 'log':
                return function( canvasHeight, value ) {
                    with(scale)
                        return canvasHeight * (1-(Math.log(value+offset)-min)/range);
                };
            case 'linear':
            default:
                return function( canvasHeight, value ) {
                    with(scale)
                        return canvasHeight * (1-(value+offset-min)/range);
                };
            }
        }.call(this);

        return this.scale;
    },

    renderCanvases: function( scale, leftBase, rightBase, callback ) {
        if( ! callback ) {
            console.error('null callback?');
            return;
        }

        var canvasWidth  = Math.ceil(( rightBase - leftBase ) * scale);
        var canvasHeight = 100;
        this.height = canvasHeight;

        var posColor = (this.config.style||{}).pos_color || '#00f';
        var negColor = (this.config.style||{}).neg_color || '#f00';

        this._getView( scale )
            .readWigData( this.refSeq.name, leftBase, rightBase, dojo.hitch(this,function( features ) {
                if(! this.yscale )
                    this.makeWiggleYScale();

                var c = dojo.create(
                    'canvas',
                    { height: canvasHeight,
                      width:  canvasWidth,
                      innerHTML: 'Canvas-based tracks not supported by this browser'
                    }
                );
                c.startBase = leftBase;
                var context = c && c.getContext && c.getContext('2d');
                if( context ) {
                    var toY = dojo.hitch( this, this.scale.toY, c.height );
                    var originY = toY( this.scale.origin );
                    if( this.config.variance_band )
                        (function() {
                             var stats = this.store.getStats();
                             var drawBand = function( plusminus, fill ) {
                                 context.fillStyle = fill;
                                 var varTop = toY( stats.mean + plusminus );
                                 var varHeight = toY( stats.mean - plusminus ) - varTop;
                                 varHeight = Math.max( 1, varHeight );
                                 context.fillRect( 0, varTop, c.width, varHeight );
                             };
                             drawBand( 2*stats.stdDev, 'rgba(0,0,0,0.15)' );
                             drawBand( stats.stdDev, 'rgba(0,0,0,0.25)' );
                             drawBand( 0,'yellow' );
                         }).call(this);

                    //context.fillText(features.length+' spans', 10,10);
                    //console.log( 'filling '+leftBase+'-'+rightBase);
                    dojo.forEach(features, function(f) {
                        //console.log( f.get('start') +'-'+f.get('end')+':'+f.get('score') );
                        var rTop = toY( f.get('score') );
                        if( rTop <= canvasHeight ) {
                            var rWidth = Math.ceil(( f.get('end') - f.get('start') + 1 ) * scale );
                            var rLeft  = Math.floor(( f.get('start')-1 - leftBase ) * scale );
                            if( rTop <= originY ) {
                                // bar goes upward
                                context.fillStyle = posColor;
                                context.fillRect( rLeft, rTop, rWidth, originY-rTop );
                            }
                            else {
                                // bar goes downward
                                context.fillStyle = negColor;
                                context.fillRect( rLeft, originY, rWidth, canvasHeight-rTop );
                            }
                        }
                    }, this );
                }

                callback( [c] );
            }));
    },

    updateStaticElements: function( coords ) {
        this.inherited( arguments );
        this.updateYScaleFromViewDimensions( coords );
    }
});

/**
 * Mixin: JBrowse.View.Track.YScaleMixin.
 */
declare.safeMixin( Wiggle.prototype, YScaleMixin );

return Wiggle;
});
<|endoftext|>


<commit_before>'use strict';

module.exports = {
  createOptions: {
    passwordAlgorithm: 'sha1',
    passwordKey: process.env.DS_GAMBIT_CREATE_USER_PASSWORD_KEY || 'puppetSlothForever',
    passwordLength: 6,
  },
  updatesByMacro: {
    // TODO: DRY subscription helper config, hardcoding these values for now.
    subscriptionStatusActive: { sms_status: 'active' },
    subscriptionStatusLess: { sms_status: 'less' },
    subscriptionStatusResubscribed: { sms_status: 'active' },
    subscriptionStatusStop: { sms_status: 'stop' },
  },
};
<commit_msg>Adds getSubscriptionStatusUpdate to DRY sms_status field name<commit_after>'use strict';

const userFields = {
  subscriptionStatus: 'sms_status',
};

function getSubscriptionStatusUpdate(value) {
  const payload = {};
  payload[userFields.subscriptionStatus] = value;
  return payload;
}

module.exports = {
  createOptions: {
    passwordAlgorithm: 'sha1',
    passwordKey: process.env.DS_GAMBIT_CREATE_USER_PASSWORD_KEY || 'puppetSlothForever',
    passwordLength: 6,
  },
  fields: {
    // Note: All platforms are storing subscription status on sms_status for now, as sms is the only
    // supported medium our users can message us (Slack is internal to staff via gambit-slack app)
    subscriptionStatus: 'sms_status',
  },
  // TODO: DRY subscription helper config, hardcoding these values for now.
  updatesByMacro: {
    subscriptionStatusActive: getSubscriptionStatusUpdate('active'),
    subscriptionStatusLess: getSubscriptionStatusUpdate('less'),
    subscriptionStatusResubscribed: getSubscriptionStatusUpdate('active'),
    subscriptionStatusStop: getSubscriptionStatusUpdate('stop'),
    // TODO: Add more macros to save values to voting plan fields
    // e.g. votingPlanStatusVoting, votingPlanAttendingWithFriends
  },
};
<|endoftext|>


<commit_before>/*global JSON, JsonML */
/*
	JsonML2.js
	JsonML support

	Created: 2006-11-09-0116
	Modified: 2009-04-16-0812

	Copyright (c)2006-2009 Stephen M. McKamey
	Distributed under an open-source license: http://jsonml.org/license

    This file creates a global JsonML object containing this method:

        JsonML.parse(string|array, filter)

            This method produces a tree of DOM elements from a JsonML tree. The
            array must not contain any cyclical references.

            The optional filter parameter is a function which can filter and
            transform the results. It receives each of the DOM nodes, and
            its return value is used instead of the original value. If it
            returns what it received, then structure is not modified. If it
            returns undefined then the member is deleted.

			This is useful for binding unobtrusive JavaScript to the generated
			DOM elements.

            Example:

            // Parses the structure. If an element has a specific CSS value then
            // takes appropriate action: Remove from results, add special event
            // handlers, or bind to a custom component.

            var myUI = JsonML.parse(myUITemplate, function (elem) {
				if (elem.className.indexOf("Remove-Me") >= 0) {
					// this will remove from resulting DOM tree
					return null;
				}

				if (elem.tagName && elem.tagName.toLowerCase() === "a" &&
					elem.className.indexOf("External-Link") >= 0) {
					// this is the equivalent of target="_blank"
					elem.onclick = function(evt) {
						window.open(elem.href); return false;
					};

				} else if (elem.className.indexOf("Fancy-Widgit") >= 0) {
					// bind to a custom component
					FancyWidgit.bindDOM(elem);
				}
				return elem;
			});

*/

if ("undefined" === typeof window.JsonML) {
	window.JsonML = {};
}

(function() {
	function Unparsed(/*string*/ value) {
		this.value = value;
	}

	JsonML.parse = function(/*JsonML*/ jml, /*DOM function(DOM)*/ filter) {

		//attribute name mapping
		var am = {
			rowspan : "rowSpan",
			colspan : "colSpan",
			cellpadding : "cellPadding",
			cellspacing : "cellSpacing",
			tabindex : "tabIndex",
			accesskey : "accessKey",
			hidefocus : "hideFocus",
			usemap : "useMap",
			maxlength : "maxLength",
			readonly : "readOnly",
			contenteditable : "contentEditable"
			// can add more attributes here as needed
		};

		// attribute duplicates
		var ad = {
			enctype : "encoding"
			// can add more attributes here as needed
		};

		//addAttributes
		/*DOM*/ function aa(/*DOM*/ el, /*object*/ a) {
			if (a.name && document.attachEvent) {
				try {
					// IE fix for not being able to programatically change the name attribute
					var el2 = document.createElement("<"+el.tagName+" name='"+a.name+"'>");
					// fix for Opera 8.5 and Netscape 7.1 creating malformed elements
					if (el.tagName === el2.tagName) {
						el = el2;
					}
				} catch (ex) { }
			}

			// for each attributeName
			for (var an in a) {
				if (a.hasOwnProperty(an)) {
					// attributeValue
					var av = a[an];
					if (an && av) {
						an = am[an.toLowerCase()] || an;
						if (an === "style") {
							if ("undefined" !== typeof el.style.cssText) {
								el.style.cssText = av;
							} else {
								el.style = av;
							}
						} else if (an === "class") {
							el.className = av;
						} else if ("string" === typeof av || "number" === typeof av || "boolean" === typeof av) {
							el.setAttribute(an, av);

							// TODO: explicitly name these to prevent false positives
							// in IE cannot set onclick events directly
							if (an.indexOf('on') === 0 && "function" !== typeof el[an]) {
								/*jslint evil:true */
								el[an] = new Function(av);

								// also set duplicated attributes
								if (ad[an]) {
									el[ad[an]] = new Function(av);
								}
								/*jslint evil:false */
							} else {
								// also set duplicated attributes
								if (ad[an]) {
									el.setAttribute(ad[an], av);
								}
							}
						} else {

							// allow direct setting of complex properties
							el[an] = av;

							// also set duplicated attributes
							if (ad[an]) {
								el[ad[an]] = av;
							}
						}
					}
				}
			}
			return el;
		}

		//appendChild
		/*void*/ function ac(/*DOM*/ el, /*DOM*/ c) {
			var ct, tb;
			if (c) {
				if (el.tagName && el.tagName.toLowerCase() === "table" && el.tBodies) {
					if (!c.tagName) {
						return;
					}
					// in IE must explicitly nest TRs in TBODY
					ct = c.tagName.toLowerCase();// child tagName
					if (ct && ct !== "tbody" && ct !== "thead") {
						// insert in last tbody
						tb = el.tBodies.length > 0 ? el.tBodies[el.tBodies.length-1] : null;// tBody
						if (!tb) {
							tb = document.createElement(ct==="th" ? "thead" : "tbody");
							el.appendChild(tb);
						}
						tb.appendChild(c);
					} else if (el.canHaveChildren !== false) {
						el.appendChild(c);
					}
				} else if (el.canHaveChildren !== false) {
					el.appendChild(c);
				} else if (el.tagName && el.tagName.toLowerCase() === "object" &&
					c.tagName && c.tagName.toLowerCase() === "param") {
						try {
							// IE-only path
							el.appendChild(c);
						} catch (ex1) {}
						try {
							if (el.object) {
								el.object[c.name] = c.value;
							}
						} catch (ex2) {}
				}
			}
		}

		// isWhitespace
		/*bool*/ function ws(/*DOM*/ n) {
			return n && (n.nodeType === 3) && (!n.nodeValue || !/\S/.exec(n.nodeValue));
		}

		// trimWhitespace
		/*void*/ function tw(/*DOM*/ el) {
			if (el) {
				while (ws(el.firstChild)) {
					// trim leading whitespace text nodes
					el.removeChild(el.firstChild);
				}
				while (ws(el.lastChild)) {
					// trim trailing whitespace text nodes
					el.removeChild(el.lastChild);
				}
			}
		}

		//unparsed
		/*DOM*/ function u(/*string*/ s) {
			// wrapper
			var w = document.createElement("div");
			w.innerHTML = s;

			// trim extraneous whitespace
			tw(w);

			// eliminate wrapper for single nodes
			if (w.childNodes.length === 1) {
				return w.firstChild;
			}

			// create a document fragment to hold elements
			var f = document.createDocumentFragment ?
				document.createDocumentFragment() :
				document.createElement("");

			while (w.firstChild) {
				f.appendChild(w.firstChild);
			}
			return f;
		}

		//JsonML.parse
		/*DOM*/ function p(/*JsonML*/ jml) {
			if (!jml) {
				return null;
			}
			if ("string" === typeof jml) {
				return document.createTextNode(jml);
			}
			if (jml instanceof Unparsed) {
				return u(jml.value);
			}
			if (!(jml instanceof Array) || !jml.length || "string" !== typeof jml[0]) {
				throw new Error("JsonML.parse: invalid JsonML tree");
			}

			var i;
			var t = jml[0]; // tagName
			if (!t) {
				// correctly handle a list of JsonML trees
				// create a document fragment to hold elements
				var f = document.createDocumentFragment ?
					document.createDocumentFragment() :
					document.createElement("");
				for (i=1; i<jml.length; i++) {
					ac(f, p(jml[i]));
				}

				// trim extraneous whitespace
				tw(f);

				// eliminate wrapper for single nodes
				if (f.childNodes.length === 1) {
					return f.firstChild;
				}
				return f;
			}

			var x = (t.toLowerCase() === "script"); // check for scripts
			var css = (t.toLowerCase() === "style" && document.createStyleSheet);
			var el;
			if (css) {
				// IE requires this interface for styles
				el = document.createStyleSheet();
			} else {
				el = x ? null : document.createElement(t);
			}

			for (i=1; i<jml.length; i++) {
				if (!x) {
					if (jml[i] instanceof Array || "string" === typeof jml[i]) {
						if (css) {
							// IE requires this interface for styles
							el.cssText = jml[i];
						} else {
							// append children
							ac(el, p(jml[i]));
						}
					} else if (jml[i] instanceof Unparsed) {
						ac(el, u(jml[i].value));
					} else if ("object" === typeof jml[i] && jml[i] !== null && el.nodeType === 1) {
						// add attributes
						el = aa(el, jml[i]);
					}
				//} else if (typeof(jml[i]) === "string") {
					/*	JSLint: "eval is evil"
						uncomment at your own risk, executes script elements immediately */
					//eval( "(" + jml[i] + ")" );
				}
			}

			if (css) {
				// in IE styles are effective immediately
				return null;
			}

			// trim extraneous whitespace
			tw(el);
			return (el && "function" === typeof filter) ? filter(el) : el;
		}

		if (jml instanceof Array) {
			return p(jml);
		}
		if ("string" === typeof jml) {
			try {
				jml = JSON.parse(jml);
			} catch (ex) {
				return null;
			}
			if (jml instanceof Array) {
				return p(jml);
			}
		}
		return null;
	};

	JsonML.raw = function(/*string*/ value) {
		return new Unparsed(value);
	};
})();<commit_msg>- comment tweak<commit_after>/*global JSON, JsonML */
/*
	JsonML2.js
	JsonML support

	Created: 2006-11-09-0116
	Modified: 2009-04-16-0812

	Copyright (c)2006-2009 Stephen M. McKamey
	Distributed under an open-source license: http://jsonml.org/license

    This file creates a global JsonML object containing this method:

        JsonML.parse(string|array, filter)

            This method produces a tree of DOM elements from a JsonML tree. The
            array must not contain any cyclical references.

            The optional filter parameter is a function which can filter and
            transform the results. It receives each of the DOM nodes, and
            its return value is used instead of the original value. If it
            returns what it received, then structure is not modified. If it
            returns undefined then the member is deleted.

			This is useful for binding unobtrusive JavaScript to the generated
			DOM elements.

            Example:

            // Parses the structure. If an element has a specific CSS value then
            // takes appropriate action: Remove from results, add special event
            // handlers, or bind to a custom component.

            var myUI = JsonML.parse(myUITemplate, function (elem) {
				if (elem.className.indexOf("Remove-Me") >= 0) {
					// this will remove from resulting DOM tree
					return null;
				}

				if (elem.tagName && elem.tagName.toLowerCase() === "a" &&
					elem.className.indexOf("External-Link") >= 0) {
					// this is the equivalent of target="_blank"
					elem.onclick = function(evt) {
						window.open(elem.href); return false;
					};

				} else if (elem.className.indexOf("Fancy-Widgit") >= 0) {
					// bind to a custom component
					FancyWidgit.bindDOM(elem);
				}
				return elem;
			});

*/

if ("undefined" === typeof window.JsonML) {
	window.JsonML = {};
}

(function() {
	function Unparsed(/*string*/ value) {
		this.value = value;
	}

	JsonML.parse = function(/*JsonML*/ jml, /*DOM function(DOM)*/ filter) {

		//attribute name mapping
		var am = {
			rowspan : "rowSpan",
			colspan : "colSpan",
			cellpadding : "cellPadding",
			cellspacing : "cellSpacing",
			tabindex : "tabIndex",
			accesskey : "accessKey",
			hidefocus : "hideFocus",
			usemap : "useMap",
			maxlength : "maxLength",
			readonly : "readOnly",
			contenteditable : "contentEditable"
			// can add more attributes here as needed
		};

		// attribute duplicates
		var ad = {
			enctype : "encoding"
			// can add more attributes here as needed
		};

		//addAttributes
		/*DOM*/ function aa(/*DOM*/ el, /*object*/ a) {
			if (a.name && document.attachEvent) {
				try {
					// IE fix for not being able to programatically change the name attribute
					var el2 = document.createElement("<"+el.tagName+" name='"+a.name+"'>");
					// fix for Opera 8.5 and Netscape 7.1 creating malformed elements
					if (el.tagName === el2.tagName) {
						el = el2;
					}
				} catch (ex) { }
			}

			// for each attributeName
			for (var an in a) {
				if (a.hasOwnProperty(an)) {
					// attributeValue
					var av = a[an];
					if (an && av) {
						an = am[an.toLowerCase()] || an;
						if (an === "style") {
							if ("undefined" !== typeof el.style.cssText) {
								el.style.cssText = av;
							} else {
								el.style = av;
							}
						} else if (an === "class") {
							el.className = av;
						} else if ("string" === typeof av || "number" === typeof av || "boolean" === typeof av) {
							el.setAttribute(an, av);

							// TODO: explicitly name these to prevent false positives
							// in IE cannot set onclick events directly
							if (an.indexOf('on') === 0 && "function" !== typeof el[an]) {
								/*jslint evil:true */
								el[an] = new Function(av);

								// also set duplicated attributes
								if (ad[an]) {
									el[ad[an]] = new Function(av);
								}
								/*jslint evil:false */
							} else {
								// also set duplicated attributes
								if (ad[an]) {
									el.setAttribute(ad[an], av);
								}
							}
						} else {

							// allow direct setting of complex properties
							el[an] = av;

							// also set duplicated attributes
							if (ad[an]) {
								el[ad[an]] = av;
							}
						}
					}
				}
			}
			return el;
		}

		//appendChild
		/*void*/ function ac(/*DOM*/ el, /*DOM*/ c) {
			var ct, tb;
			if (c) {
				if (el.tagName && el.tagName.toLowerCase() === "table" && el.tBodies) {
					if (!c.tagName) {
						return;
					}
					// in IE must explicitly nest TRs in TBODY
					ct = c.tagName.toLowerCase();// child tagName
					if (ct && ct !== "tbody" && ct !== "thead") {
						// insert in last tbody
						tb = el.tBodies.length > 0 ? el.tBodies[el.tBodies.length-1] : null;// tBody
						if (!tb) {
							tb = document.createElement(ct==="th" ? "thead" : "tbody");
							el.appendChild(tb);
						}
						tb.appendChild(c);
					} else if (el.canHaveChildren !== false) {
						el.appendChild(c);
					}
				} else if (el.canHaveChildren !== false) {
					el.appendChild(c);
				} else if (el.tagName && el.tagName.toLowerCase() === "object" &&
					c.tagName && c.tagName.toLowerCase() === "param") {
						// IE-only path
						try {
							el.appendChild(c);
						} catch (ex1) {}
						try {
							if (el.object) {
								el.object[c.name] = c.value;
							}
						} catch (ex2) {}
				}
			}
		}

		// isWhitespace
		/*bool*/ function ws(/*DOM*/ n) {
			return n && (n.nodeType === 3) && (!n.nodeValue || !/\S/.exec(n.nodeValue));
		}

		// trimWhitespace
		/*void*/ function tw(/*DOM*/ el) {
			if (el) {
				while (ws(el.firstChild)) {
					// trim leading whitespace text nodes
					el.removeChild(el.firstChild);
				}
				while (ws(el.lastChild)) {
					// trim trailing whitespace text nodes
					el.removeChild(el.lastChild);
				}
			}
		}

		//unparsed
		/*DOM*/ function u(/*string*/ s) {
			// wrapper
			var w = document.createElement("div");
			w.innerHTML = s;

			// trim extraneous whitespace
			tw(w);

			// eliminate wrapper for single nodes
			if (w.childNodes.length === 1) {
				return w.firstChild;
			}

			// create a document fragment to hold elements
			var f = document.createDocumentFragment ?
				document.createDocumentFragment() :
				document.createElement("");

			while (w.firstChild) {
				f.appendChild(w.firstChild);
			}
			return f;
		}

		//JsonML.parse
		/*DOM*/ function p(/*JsonML*/ jml) {
			if (!jml) {
				return null;
			}
			if ("string" === typeof jml) {
				return document.createTextNode(jml);
			}
			if (jml instanceof Unparsed) {
				return u(jml.value);
			}
			if (!(jml instanceof Array) || !jml.length || "string" !== typeof jml[0]) {
				throw new Error("JsonML.parse: invalid JsonML tree");
			}

			var i;
			var t = jml[0]; // tagName
			if (!t) {
				// correctly handle a list of JsonML trees
				// create a document fragment to hold elements
				var f = document.createDocumentFragment ?
					document.createDocumentFragment() :
					document.createElement("");
				for (i=1; i<jml.length; i++) {
					ac(f, p(jml[i]));
				}

				// trim extraneous whitespace
				tw(f);

				// eliminate wrapper for single nodes
				if (f.childNodes.length === 1) {
					return f.firstChild;
				}
				return f;
			}

			var x = (t.toLowerCase() === "script"); // check for scripts
			var css = (t.toLowerCase() === "style" && document.createStyleSheet);
			var el;
			if (css) {
				// IE requires this interface for styles
				el = document.createStyleSheet();
			} else {
				el = x ? null : document.createElement(t);
			}

			for (i=1; i<jml.length; i++) {
				if (!x) {
					if (jml[i] instanceof Array || "string" === typeof jml[i]) {
						if (css) {
							// IE requires this interface for styles
							el.cssText = jml[i];
						} else {
							// append children
							ac(el, p(jml[i]));
						}
					} else if (jml[i] instanceof Unparsed) {
						ac(el, u(jml[i].value));
					} else if ("object" === typeof jml[i] && jml[i] !== null && el.nodeType === 1) {
						// add attributes
						el = aa(el, jml[i]);
					}
				//} else if (typeof(jml[i]) === "string") {
					/*	JSLint: "eval is evil"
						uncomment at your own risk, executes script elements immediately */
					//eval( "(" + jml[i] + ")" );
				}
			}

			if (css) {
				// in IE styles are effective immediately
				return null;
			}

			// trim extraneous whitespace
			tw(el);
			return (el && "function" === typeof filter) ? filter(el) : el;
		}

		if (jml instanceof Array) {
			return p(jml);
		}
		if ("string" === typeof jml) {
			try {
				jml = JSON.parse(jml);
			} catch (ex) {
				return null;
			}
			if (jml instanceof Array) {
				return p(jml);
			}
		}
		return null;
	};

	JsonML.raw = function(/*string*/ value) {
		return new Unparsed(value);
	};
})();<|endoftext|>


<commit_before>import React from 'react';
import cx from 'classnames';
import { skinnable, props, t } from '../utils';
import FlexView from '../flex/FlexView';
import Icon from '../Icon/Icon';

import './collapsableSection.scss';

@skinnable()
@props({
  children: t.ReactChildren,
  isOpen: t.Boolean,
  isSelected: t.maybe(t.Boolean),
  onChange: t.Function,
  onOpen: t.maybe(t.Function),
  onClose: t.maybe(t.Function),
  header: t.maybe(t.ReactChild),
  icons: t.maybe(t.struct({
    open: t.maybe(t.String),
    closed: t.maybe(t.String)
  })),
  className: t.maybe(t.String),
  id: t.maybe(t.String),
  style: t.maybe(t.Object)
})
export default class CollapsableSection extends React.Component {

  static defaultProps = {
    onOpen: () => {},
    onClose: () => {}
  };

  _onChange = () => {
    const { onChange, onOpen, onClose, isOpen } = this.props;
    onChange(!isOpen);
    if (isOpen) {
      onClose();
    } else {
      onOpen();
    }
  };

  getLocals() {
    const {
      props: {
        isOpen, isSelected,
        children, header, icons,
        className, id, style
      },
      _onChange: onChange
    } = this;

    return {
      isOpen,
      children,
      header,
      icons,
      onChange,
      wrapperProps: {
        id,
        style,
        className: cx(className, 'collapsable-section', { 'is-open': isOpen, 'is-selected': isSelected })
      }
    };
  }

  templateIcons = ({ isOpen, icons }) => (
    icons && <Icon icon={isOpen ? icons.open : icons.closed} />
  );

  template({ isOpen, children, onChange, header, icons, wrapperProps }) {
    return (
      <div {...wrapperProps}>
        <FlexView className='content' vAlignContent='center' onClick={onChange}>
          {header}
          <FlexView marginLeft='auto' shrink={false}>
            {this.templateIcons({ isOpen, icons })}
          </FlexView>
        </FlexView>
        {isOpen && children}
      </div>
    );
  }

}
<commit_msg>use FlexView column as main wrapper in CollapsableSection<commit_after>import React from 'react';
import cx from 'classnames';
import { skinnable, props, t } from '../utils';
import FlexView from '../flex/FlexView';
import Icon from '../Icon/Icon';

import './collapsableSection.scss';

@skinnable()
@props({
  children: t.ReactChildren,
  isOpen: t.Boolean,
  isSelected: t.maybe(t.Boolean),
  onChange: t.Function,
  onOpen: t.maybe(t.Function),
  onClose: t.maybe(t.Function),
  header: t.maybe(t.ReactChild),
  icons: t.maybe(t.struct({
    open: t.maybe(t.String),
    closed: t.maybe(t.String)
  })),
  className: t.maybe(t.String),
  id: t.maybe(t.String),
  style: t.maybe(t.Object)
})
export default class CollapsableSection extends React.Component {

  static defaultProps = {
    onOpen: () => {},
    onClose: () => {}
  };

  _onChange = () => {
    const { onChange, onOpen, onClose, isOpen } = this.props;
    onChange(!isOpen);
    if (isOpen) {
      onClose();
    } else {
      onOpen();
    }
  };

  getLocals() {
    const {
      props: {
        isOpen, isSelected,
        children, header, icons,
        className, id, style
      },
      _onChange: onChange
    } = this;

    return {
      isOpen,
      children,
      header,
      icons,
      onChange,
      wrapperProps: {
        id,
        style,
        className: cx(className, 'collapsable-section', { 'is-open': isOpen, 'is-selected': isSelected })
      }
    };
  }

  templateIcons = ({ isOpen, icons }) => (
    icons && <Icon icon={isOpen ? icons.open : icons.closed} />
  );

  template({ isOpen, children, onChange, header, icons, wrapperProps }) {
    return (
      <FlexView {...wrapperProps} column>
        <FlexView className='content' vAlignContent='center' onClick={onChange}>
          {header}
          <FlexView marginLeft='auto' shrink={false}>
            {this.templateIcons({ isOpen, icons })}
          </FlexView>
        </FlexView>
        {isOpen && children}
      </FlexView>
    );
  }

}
<|endoftext|>


<commit_before>const facetClass = 'b-facet-box__facet';
const facetTerm = `${facetClass}-term-toggle`;
const facetTermTick = `${facetTerm}-tick`;
const facetTextClass = `${facetClass}-text`;
const facetTermContainerClass = `${facetClass}-term-container`;

const termClass = 'b-facet-box__term';
const termListClass = `${termClass}-list`;
const termList = document.querySelector(`.${termListClass}`);
const termCheckboxClass = `${facetClass}-term-toggle-checkbox`;

const facetCloseClass = 'b-facet-box__close-button';

const facetsWithIndex = {};

const termButtonHTML = (facet, term) => `

        ${facet}: ${term}

`;

const dateFacetHTML = () => `

      Dates

          From year:

        <input class="b-facet-box__facet-date-input" placeholder="Year" type="number" name="after_year">

          To year:

        <input class="b-facet-box__facet-date-input" placeholder="Year" type="number" name="before_year">

        <button class="b-facet-box__facet-date-button">
          <svg class="b-facet-box__facet-date-button-icon" role="img">
            <use xlink:href="/svg/vamicons.svg#search"></use>
          </svg>
        </button>

`;

const termCheckbox = (facet, paramName, term, value, count) => {
  const checkbox = document.createElement('LI');
  checkbox.className = 'b-facet-box__facet-term-toggle';
  checkbox.dataset.id = `${paramName}-${value}`;
  checkbox.dataset.facet = facet;
  checkbox.dataset.paramName = paramName;
  checkbox.dataset.term = term;
  checkbox.dataset.value = value;
  checkbox.dataset.count = count;

  checkbox.innerHTML = `

      <svg class="b-facet-box__facet-term-toggle-tick" role="img">
        <use xlink:href="/svg/vamicons.svg#tick"></use>
      </svg>

      ${term}

      (${count})

  `;

  // const hiddenInput = `
  //   <input class="b-facet-box__hidden-input" type="checkbox" name="${paramName}" value="${value}">
  // `;

  const hiddenInput = document.createElement('INPUT');
  hiddenInput.type = 'checkbox';
  hiddenInput.className = 'b-facet-box__hidden-input';
  hiddenInput.name = paramName;
  hiddenInput.value = value;
  hiddenInput.id = `${paramName}=${value}`;

  checkbox.addEventListener('termToggle', (e) => {
    const existingHiddenInput = document.querySelector(`input[id="${`${paramName}=${value}`}"]`);

    // GOTTA ASSUME THERE'S A FORM ON THE PAGE FOR THIS TO WORK!!!
    // this is because formData has an order which is annoying to change

    if (existingHiddenInput) {
      document.querySelector('form').appendChild(hiddenInput).checked = false;
      existingHiddenInput.remove();
    } else {
      document.querySelector('form').appendChild(hiddenInput);
      document.querySelector('form').appendChild(hiddenInput).checked = true;
    }

    e.target.querySelector(`.${facetTermTick}`).classList.toggle(
      `${facetTermTick}--active`
    );
    document.querySelector('.b-facet-box').dispatchEvent(new Event('boxChecked', { bubbles: true }));
  });

  return checkbox;
};

const facetHTML = (facet, seeMore) => `

    ${facet}

${
  seeMore ? `
    See more
  ` : ''
}
`;

const revealMoreFacets = (e) => {
  e.preventDefault();
  const linkEl = e.target;
  const facetContainer = e.target.parentNode;
  const { terms, index, facet, paramName } = facetsWithIndex[e.target.dataset.facet];
  e.target.remove();
  terms.slice(index, index + 5).forEach(({ term, count, value }) => {
    facetContainer.appendChild(termCheckbox(facet, paramName, term, value, count));
  });
  facetsWithIndex[facet].index += 5;

  if (facetsWithIndex[facet].index !== terms.length) {
    facetContainer.appendChild(linkEl);
  }
};

const createFacets = (activeFacets) => {
  const facetBoxContainer = document.querySelector('.b-facet-box__facet-container');
  const facetToTerm = Array.from(activeFacets).reduce((res, termfacet) => {
    const facet = termfacet.split('-')[0];
    const term = termfacet.split('-')[1];

    if (res[facet]) {
      res[facet].push(term);
    } else {
      res[facet] = [term];
    }

    return res;
  }, {});

  Object.values(facetsWithIndex).forEach(({ facet, terms, paramName, index }) => {
    const newFacet = document.createElement('DIV');
    newFacet.className = 'b-facet-box__facet';
    newFacet.innerHTML = facetHTML(facet, terms.length > 5);

    newFacet.addEventListener('click', (e) => {
      if (e.target.classList.contains(facetTextClass)) {
        e.target.classList.toggle(`${e.target.classList[0]}--active`);
        e.target.parentNode.querySelector(`.${facetTermContainerClass}`).classList.toggle(`${facetTermContainerClass}--active`);
      }
    });

    const termValues = terms.map(t => t.value);

    let newIndex = (facetToTerm[paramName] && facetToTerm[paramName].reduce((current, term) => {
      const test = termValues.indexOf(term);
      return (current > test ? current : test);
    }, 5)) || 0;

    newIndex = ((Math.ceil(newIndex / 5) * 5));

    newIndex = newIndex > terms.length ? terms.length : (newIndex || 5);

    terms.slice(index, newIndex).forEach(({ term, count, value }) => {
      newFacet.querySelector(`.${facetTermContainerClass}`).appendChild(termCheckbox(facet, paramName, term, value, count));
    });

    facetsWithIndex[facet].index += (newIndex);

    if (terms.length > 5) {
      if (facetsWithIndex[facet].index < terms.length) {
        newFacet.querySelector(`.${facetTermContainerClass}`).appendChild(newFacet.querySelector('.b-facet-box__term-more'));
        newFacet.querySelector(`.${facetTermContainerClass} .b-facet-box__term-more`).onclick = e => revealMoreFacets(e);
      } else {
        newFacet.querySelector(`.${facetTermContainerClass}`).appendChild(newFacet.querySelector('.b-facet-box__term-more')).remove();
      }
    }

    facetBoxContainer.appendChild(newFacet);
  });
};

const newTermToggleEvent = (detail, bubbles = true) => new CustomEvent('termToggle', {
  detail,
  bubbles
});

const initialiseFacetOverlay = () => {
  const toggleTerm = ({ id, facet, term, paramName }) => {
    if (id) {
      // if term already exists, get rid of it
      if (document.querySelector(`div[data-id='${id}']`)) {
        Array.from(document.querySelectorAll(`div[data-id='${id}']`)).forEach(el => el.remove());
        if (!document.querySelector('.b-search-form__facets').children.length) {
          document.querySelector('.b-search-form__facet-pane').classList.remove('b-search-form__facet-pane--active');
        }
        window.dispatchEvent(new Event('resize'));
      } else {
        const newTermOnClick = () => {
          Array.from(document.querySelectorAll(`div[data-id='${id}']`)).forEach(el => el.dispatchEvent(newTermToggleEvent({ id, facet, term, paramName })));
          if (document.querySelector(`li[data-id='${id}']`)) {
            document.querySelector(`li[data-id='${id}']`).dispatchEvent(newTermToggleEvent({ id, facet, term, paramName }));
          }
        };

        const newTerm = document.createElement('DIV');
        newTerm.dataset.id = id;
        newTerm.className = 'b-facet-box__term';
        newTerm.innerHTML = termButtonHTML(facet, term);
        newTerm.onclick = e => newTermOnClick(e);
        termList.appendChild(newTerm);

        const newFormTerm = newTerm.cloneNode(true);
        newFormTerm.onclick = e => newTermOnClick(e);
        newFormTerm.classList.add('b-facet-box__term--form');
        if (document.querySelector('.b-search-form__facets')) {
          document.querySelector('.b-search-form__facets').appendChild(newFormTerm);
        }

        if (!document.querySelector('.b-search-form__facet-pane--active')) {
          document.querySelector('.b-search-form__facet-pane').classList.add('b-search-form__facet-pane--active');
        }

        window.dispatchEvent(new Event('resize'));
      }
    }
  };

  termList.addEventListener('termToggle', (e) => {
    e.stopPropagation();
    toggleTerm(e.detail);
  });

  document.querySelector('.b-facet-box').addEventListener('newFacets', (e) => {
    const { facets, activeFacets } = e.detail;
    const currentBeforeDate = document.querySelector('input[name="before_year"]') ? document.querySelector('input[name="before_year"]').value : '';
    const currentAfterDate = document.querySelector('input[name="after_year"]') ? document.querySelector('input[name="after_year"]').value : '';

    facets.forEach((facet) => {
      Object.assign(facetsWithIndex, {
        [facet.facet]: Object.assign(facet, { index: 0 })
      });
    });

    const facetBoxContainer = document.querySelector('.b-facet-box__facet-container');
    facetBoxContainer.innerHTML = '';
    termList.innerHTML = '';

    createFacets(activeFacets);

    const dateFacet = document.createElement('DIV');
    dateFacet.className = 'b-facet-box__facet b-facet-box__facet-date';
    dateFacet.innerHTML = dateFacetHTML();
    dateFacet.addEventListener('click', (ev) => {
      if (ev.target.classList.contains(facetTextClass)) {
        ev.target.classList.toggle(`${ev.target.classList[0]}--active`);
        ev.target.parentNode.querySelector(`.${facetTermContainerClass}`).classList.toggle(`${facetTermContainerClass}--active`);
      }
    });
    dateFacet.querySelector('input[name="before_year"]').value = currentBeforeDate;
    dateFacet.querySelector('input[name="after_year"]').value = currentAfterDate;

    facetBoxContainer.append(dateFacet);

    if (activeFacets) {
      // is a set...
      Array.from(activeFacets).forEach((facetId) => {
        const target = document.querySelector(`li[data-id='${facetId}'`);
        if (target) {
          target.dispatchEvent(newTermToggleEvent(target.dataset));
          document.querySelector(`.${termListClass}`).dispatchEvent(newTermToggleEvent(target.dataset));
        }
      });
    }

    window.dispatchEvent(new Event('resize'));
  }, true);

  document.onclick = (e) => {
    if (e.target.classList.contains(facetCloseClass)) {
      e.target.dispatchEvent(new Event('closeFacetOverlay', {
        bubbles: true
      }));
    }

    if (e.target.parentElement && e.target.parentElement.classList.contains(facetTerm)) {
      const parent = e.target.closest(facetTerm) || e.target.parentElement;
      termList.dispatchEvent(newTermToggleEvent(parent.dataset, false));
      parent.dispatchEvent(newTermToggleEvent(parent.dataset));
    }
  };
};

(() => {
  if (document.querySelector('.b-facet-box')) {
    initialiseFacetOverlay();

    if (document.querySelector('.b-facet-box__modal-button-open')) {
      document.querySelectorAll('.b-facet-box__modal-button-open').forEach(el => el.addEventListener('click', () => document.querySelector('.b-facet-box').classList.add('b-facet-box--active')));
    }
    if (document.querySelector('.b-facet-box__close-button')) {
      document.querySelector('.b-facet-box__close-button').addEventListener('click', () => document.querySelector('.b-facet-box').classList.remove('b-facet-box--active'));
    }

    window.onresize = () => {
      const facetFormTerms = Array.from(document.querySelectorAll('.b-facet-box__term.b-facet-box__term--form'));
      if (document.querySelector('.b-facet-box__term-text.b-facet-box__term-text--no-cross')) {
        if (window.innerWidth > 499 && window.innerWidth < 992) {
          const facetContainerWidth = document.querySelector('.b-search-form__facets').offsetWidth;
          let cutOffWidth = 0;
          let currentIndex = 1;
          facetFormTerms.forEach((el) => {
            cutOffWidth += el.offsetWidth + 10;
            if (cutOffWidth < facetContainerWidth) {
              currentIndex += 1;
            }
          });
          if ((facetFormTerms.length - currentIndex) > 0) {
            document.querySelector('.b-search-form__facets-mobile').style.display = 'block';
          } else {
            document.querySelector('.b-search-form__facets-mobile').style.display = 'none';
          }
          document.querySelector('.b-facet-box__term-text.b-facet-box__term-text--no-cross').innerHTML = `+${facetFormTerms.length - currentIndex}`;
        } else if (window.innerWidth < 500) {
          if ((facetFormTerms.length) > 0) {
            document.querySelector('.b-search-form__facets-mobile').style.display = 'block';
          } else {
            document.querySelector('.b-search-form__facets-mobile').style.display = 'none';
          }
          document.querySelector('.b-facet-box__term-text.b-facet-box__term-text--no-cross').innerHTML = `${facetFormTerms.length - 1} filters applied`;
        } else {
          document.querySelector('.b-search-form__facets-mobile').style.display = 'none';
        }
      }
    };
  }
})();
<commit_msg>creative solution to make sure terms remain in order selected...<commit_after>const facetClass = 'b-facet-box__facet';
const facetTerm = `${facetClass}-term-toggle`;
const facetTermTick = `${facetTerm}-tick`;
const facetTextClass = `${facetClass}-text`;
const facetTermContainerClass = `${facetClass}-term-container`;

const termClass = 'b-facet-box__term';
const termListClass = `${termClass}-list`;
const termList = document.querySelector(`.${termListClass}`);
const termCheckboxClass = `${facetClass}-term-toggle-checkbox`;

const facetCloseClass = 'b-facet-box__close-button';

const facetsWithIndex = {};

const termButtonHTML = (facet, term) => `

        ${facet}: ${term}

`;

const dateFacetHTML = () => `

      Dates

          From year:

        <input class="b-facet-box__facet-date-input" placeholder="Year" type="number" name="after_year">

          To year:

        <input class="b-facet-box__facet-date-input" placeholder="Year" type="number" name="before_year">

        <button class="b-facet-box__facet-date-button">
          <svg class="b-facet-box__facet-date-button-icon" role="img">
            <use xlink:href="/svg/vamicons.svg#search"></use>
          </svg>
        </button>

`;

const termCheckbox = (facet, paramName, term, value, count) => {
  const checkbox = document.createElement('LI');
  checkbox.className = 'b-facet-box__facet-term-toggle';
  checkbox.dataset.id = `${paramName}-${value}`;
  checkbox.dataset.facet = facet;
  checkbox.dataset.paramName = paramName;
  checkbox.dataset.term = term;
  checkbox.dataset.value = value;
  checkbox.dataset.count = count;

  checkbox.innerHTML = `

      <svg class="b-facet-box__facet-term-toggle-tick" role="img">
        <use xlink:href="/svg/vamicons.svg#tick"></use>
      </svg>

      ${term}

      (${count})

  `;

  // const hiddenInput = `
  //   <input class="b-facet-box__hidden-input" type="checkbox" name="${paramName}" value="${value}">
  // `;

  const hiddenInput = document.createElement('INPUT');
  hiddenInput.type = 'checkbox';
  hiddenInput.className = 'b-facet-box__hidden-input';
  hiddenInput.name = paramName;
  hiddenInput.value = value;
  hiddenInput.id = `${paramName}=${value}`;

  checkbox.addEventListener('termToggle', (e) => {
    const existingHiddenInput = document.querySelector(`input[id="${`${paramName}=${value}`}"]`);

    // GOTTA ASSUME THERE'S A FORM ON THE PAGE FOR THIS TO WORK!!!
    // this is because formData has an order which is annoying to change

    if (existingHiddenInput) {
      existingHiddenInput.checked = false;
      existingHiddenInput.remove();
    } else {
      document.querySelector('form').appendChild(hiddenInput);
      existingHiddenInput.checked = true;
    }

    e.target.querySelector(`.${facetTermTick}`).classList.toggle(
      `${facetTermTick}--active`
    );
    document.querySelector('.b-facet-box').dispatchEvent(new Event('boxChecked', { bubbles: true }));
  });

  return checkbox;
};

const facetHTML = (facet, seeMore) => `

    ${facet}

${
  seeMore ? `
    See more
  ` : ''
}
`;

const revealMoreFacets = (e) => {
  e.preventDefault();
  const linkEl = e.target;
  const facetContainer = e.target.parentNode;
  const { terms, index, facet, paramName } = facetsWithIndex[e.target.dataset.facet];
  e.target.remove();
  terms.slice(index, index + 5).forEach(({ term, count, value }) => {
    facetContainer.appendChild(termCheckbox(facet, paramName, term, value, count));
  });
  facetsWithIndex[facet].index += 5;

  if (facetsWithIndex[facet].index !== terms.length) {
    facetContainer.appendChild(linkEl);
  }
};

const createFacets = (activeFacets) => {
  const facetBoxContainer = document.querySelector('.b-facet-box__facet-container');
  const facetToTerm = Array.from(activeFacets).reduce((res, termfacet) => {
    const facet = termfacet.split('-')[0];
    const term = termfacet.split('-')[1];

    if (res[facet]) {
      res[facet].push(term);
    } else {
      res[facet] = [term];
    }

    return res;
  }, {});

  Object.values(facetsWithIndex).forEach(({ facet, terms, paramName, index }) => {
    const newFacet = document.createElement('DIV');
    newFacet.className = 'b-facet-box__facet';
    newFacet.innerHTML = facetHTML(facet, terms.length > 5);

    newFacet.addEventListener('click', (e) => {
      if (e.target.classList.contains(facetTextClass)) {
        e.target.classList.toggle(`${e.target.classList[0]}--active`);
        e.target.parentNode.querySelector(`.${facetTermContainerClass}`).classList.toggle(`${facetTermContainerClass}--active`);
      }
    });

    const termValues = terms.map(t => t.value);

    let newIndex = (facetToTerm[paramName] && facetToTerm[paramName].reduce((current, term) => {
      const test = termValues.indexOf(term);
      return (current > test ? current : test);
    }, 5)) || 0;

    newIndex = ((Math.ceil(newIndex / 5) * 5));

    newIndex = newIndex > terms.length ? terms.length : (newIndex || 5);

    terms.slice(index, newIndex).forEach(({ term, count, value }) => {
      newFacet.querySelector(`.${facetTermContainerClass}`).appendChild(termCheckbox(facet, paramName, term, value, count));
    });

    facetsWithIndex[facet].index += (newIndex);

    if (terms.length > 5) {
      if (facetsWithIndex[facet].index < terms.length) {
        newFacet.querySelector(`.${facetTermContainerClass}`).appendChild(newFacet.querySelector('.b-facet-box__term-more'));
        newFacet.querySelector(`.${facetTermContainerClass} .b-facet-box__term-more`).onclick = e => revealMoreFacets(e);
      } else {
        newFacet.querySelector(`.${facetTermContainerClass}`).appendChild(newFacet.querySelector('.b-facet-box__term-more')).remove();
      }
    }

    facetBoxContainer.appendChild(newFacet);
  });
};

const newTermToggleEvent = (detail, bubbles = true) => new CustomEvent('termToggle', {
  detail,
  bubbles
});

const initialiseFacetOverlay = () => {
  const toggleTerm = ({ id, facet, term, paramName }) => {
    if (id) {
      // if term already exists, get rid of it
      if (document.querySelector(`div[data-id='${id}']`)) {
        Array.from(document.querySelectorAll(`div[data-id='${id}']`)).forEach(el => el.remove());
        if (!document.querySelector('.b-search-form__facets').children.length) {
          document.querySelector('.b-search-form__facet-pane').classList.remove('b-search-form__facet-pane--active');
        }
        window.dispatchEvent(new Event('resize'));
      } else {
        const newTermOnClick = () => {
          Array.from(document.querySelectorAll(`div[data-id='${id}']`)).forEach(el => el.dispatchEvent(newTermToggleEvent({ id, facet, term, paramName })));
          if (document.querySelector(`li[data-id='${id}']`)) {
            document.querySelector(`li[data-id='${id}']`).dispatchEvent(newTermToggleEvent({ id, facet, term, paramName }));
          }
        };

        const newTerm = document.createElement('DIV');
        newTerm.dataset.id = id;
        newTerm.className = 'b-facet-box__term';
        newTerm.innerHTML = termButtonHTML(facet, term);
        newTerm.onclick = e => newTermOnClick(e);
        termList.appendChild(newTerm);

        const newFormTerm = newTerm.cloneNode(true);
        newFormTerm.onclick = e => newTermOnClick(e);
        newFormTerm.classList.add('b-facet-box__term--form');
        if (document.querySelector('.b-search-form__facets')) {
          document.querySelector('.b-search-form__facets').appendChild(newFormTerm);
        }

        if (!document.querySelector('.b-search-form__facet-pane--active')) {
          document.querySelector('.b-search-form__facet-pane').classList.add('b-search-form__facet-pane--active');
        }

        window.dispatchEvent(new Event('resize'));
      }
    }
  };

  termList.addEventListener('termToggle', (e) => {
    e.stopPropagation();
    toggleTerm(e.detail);
  });

  document.querySelector('.b-facet-box').addEventListener('newFacets', (e) => {
    const { facets, activeFacets } = e.detail;
    const currentBeforeDate = document.querySelector('input[name="before_year"]') ? document.querySelector('input[name="before_year"]').value : '';
    const currentAfterDate = document.querySelector('input[name="after_year"]') ? document.querySelector('input[name="after_year"]').value : '';

    facets.forEach((facet) => {
      Object.assign(facetsWithIndex, {
        [facet.facet]: Object.assign(facet, { index: 0 })
      });
    });

    const facetBoxContainer = document.querySelector('.b-facet-box__facet-container');
    facetBoxContainer.innerHTML = '';
    termList.innerHTML = '';

    createFacets(activeFacets);

    const dateFacet = document.createElement('DIV');
    dateFacet.className = 'b-facet-box__facet b-facet-box__facet-date';
    dateFacet.innerHTML = dateFacetHTML();
    dateFacet.addEventListener('click', (ev) => {
      if (ev.target.classList.contains(facetTextClass)) {
        ev.target.classList.toggle(`${ev.target.classList[0]}--active`);
        ev.target.parentNode.querySelector(`.${facetTermContainerClass}`).classList.toggle(`${facetTermContainerClass}--active`);
      }
    });
    dateFacet.querySelector('input[name="before_year"]').value = currentBeforeDate;
    dateFacet.querySelector('input[name="after_year"]').value = currentAfterDate;

    facetBoxContainer.append(dateFacet);

    if (activeFacets) {
      // is a set...
      Array.from(activeFacets).forEach((facetId) => {
        const target = document.querySelector(`li[data-id='${facetId}'`);
        if (target) {
          target.dispatchEvent(newTermToggleEvent(target.dataset));
          document.querySelector(`.${termListClass}`).dispatchEvent(newTermToggleEvent(target.dataset));
        }
      });
    }

    window.dispatchEvent(new Event('resize'));
  }, true);

  document.onclick = (e) => {
    if (e.target.classList.contains(facetCloseClass)) {
      e.target.dispatchEvent(new Event('closeFacetOverlay', {
        bubbles: true
      }));
    }

    if (e.target.parentElement && e.target.parentElement.classList.contains(facetTerm)) {
      const parent = e.target.closest(facetTerm) || e.target.parentElement;
      termList.dispatchEvent(newTermToggleEvent(parent.dataset, false));
      parent.dispatchEvent(newTermToggleEvent(parent.dataset));
    }
  };
};

(() => {
  if (document.querySelector('.b-facet-box')) {
    initialiseFacetOverlay();

    if (document.querySelector('.b-facet-box__modal-button-open')) {
      document.querySelectorAll('.b-facet-box__modal-button-open').forEach(el => el.addEventListener('click', () => document.querySelector('.b-facet-box').classList.add('b-facet-box--active')));
    }
    if (document.querySelector('.b-facet-box__close-button')) {
      document.querySelector('.b-facet-box__close-button').addEventListener('click', () => document.querySelector('.b-facet-box').classList.remove('b-facet-box--active'));
    }

    window.onresize = () => {
      const facetFormTerms = Array.from(document.querySelectorAll('.b-facet-box__term.b-facet-box__term--form'));
      if (document.querySelector('.b-facet-box__term-text.b-facet-box__term-text--no-cross')) {
        if (window.innerWidth > 499 && window.innerWidth < 992) {
          const facetContainerWidth = document.querySelector('.b-search-form__facets').offsetWidth;
          let cutOffWidth = 0;
          let currentIndex = 1;
          facetFormTerms.forEach((el) => {
            cutOffWidth += el.offsetWidth + 10;
            if (cutOffWidth < facetContainerWidth) {
              currentIndex += 1;
            }
          });
          if ((facetFormTerms.length - currentIndex) > 0) {
            document.querySelector('.b-search-form__facets-mobile').style.display = 'block';
          } else {
            document.querySelector('.b-search-form__facets-mobile').style.display = 'none';
          }
          document.querySelector('.b-facet-box__term-text.b-facet-box__term-text--no-cross').innerHTML = `+${facetFormTerms.length - currentIndex}`;
        } else if (window.innerWidth < 500) {
          if ((facetFormTerms.length) > 0) {
            document.querySelector('.b-search-form__facets-mobile').style.display = 'block';
          } else {
            document.querySelector('.b-search-form__facets-mobile').style.display = 'none';
          }
          document.querySelector('.b-facet-box__term-text.b-facet-box__term-text--no-cross').innerHTML = `${facetFormTerms.length - 1} filters applied`;
        } else {
          document.querySelector('.b-search-form__facets-mobile').style.display = 'none';
        }
      }
    };
  }
})();
<|endoftext|>


<commit_before>/* eslint-disable no-underscore-dangle */
document.addEventListener('DOMContentLoaded', () => {
  const imgRows = document.querySelectorAll('.b-image-row');
  if (imgRows.length) {
    let rowId = 0;
    Array.from(imgRows, (imgRow) => {
      rowId += 1;
      imgRow.classList.add(`b-image-row-${rowId}`);
      imgRow._imageRowId = rowId;

      /* use a style tag so we have media-queries */
      const imgRowStyle = imgRow.parentNode.insertBefore(document.createElement('style'), imgRow);

      const rowStyle = () => {
        const imgsLL = imgRow.querySelectorAll('.b-image-row__img');  // images, including those failed and replaced by LazyLoad
        const aspectRatios = [];
        const parentRow = imgsLL[0].closest('.b-image-row');
        const parentRowCS = getComputedStyle(parentRow);
        const rowWidth = parentRow.getBoundingClientRect().width
                          - parseInt(parentRowCS.paddingLeft, 10)
                          - parseInt(parentRowCS.paddingRight, 10);
        let imgRowStyleMobile = '';
        let imgId = 0;
        Array.from(imgsLL, (img) => {
          if (img.naturalWidth) {
            aspectRatios.push(img.naturalWidth / img.naturalHeight);
          } else {
            // failed image
            aspectRatios.push(1);
            img.closest('.b-image-row__item').classList.add('b-image-row__item--failed');
          }
          imgId += 1;
          if (imgId % 2 === 0) {
            /* apply the same height calculation for mobile as the general rule below,
             * but in batches of 2 images, and with rounding and a tiny reduction to
             * ensure no pair is wider than a wrapped row */
            imgRowStyleMobile += `
              .b-image-row-${parentRow._imageRowId} .b-image-row__item:nth-child(${imgId - 1}),
              .b-image-row-${parentRow._imageRowId} .b-image-row__item:nth-child(${imgId}) {
                height: ${Math.round((rowWidth / (aspectRatios[imgId - 2] + aspectRatios[imgId - 1])) - 1.5)}px;
              }
            `;
          }
          return true;
        });
        imgRowStyle.innerHTML = `
          @media screen and (max-width: 768px) {
            .b-image-row-${parentRow._imageRowId} {
              flex-wrap: wrap;
            }
            ${imgRowStyleMobile}
          }
          .b-image-row-${parentRow._imageRowId} .b-image-row__item {
            height: ${rowWidth / aspectRatios.reduce((a, b) => a + b)}px;
          }
        `;
      };

      const imgs = imgRow.querySelectorAll('.b-image-row__img');
      const complete = [];
      const loaded = (anyImg) => {
        complete.push(anyImg);
        if (complete.length === imgs.length) rowStyle();
      };

      Array.from(imgs, (img) => {
        /* wait for LazyLoad to set src attribute for each img */
        const observer = new MutationObserver(() => {
          observer.disconnect();
          /* then wait for each img to load, or fail */
          if (img.complete) {
            loaded(img);
          } else {
            document.addEventListener('load', (e) => {
              if (e.target === img) loaded(img);
            }, false);
            img.addEventListener('error', () => { loaded(img); }, false);
          }
        });
        observer.observe(img, { attributes: true });
        return true;
      });

      window.addEventListener('resize', rowStyle, false);
      return true;
    });
  }
}, true);

/* eslint-enable no-underscore-dangle */
<commit_msg>image-row: observer needs to stay alive!<commit_after>/* eslint-disable no-underscore-dangle */
document.addEventListener('DOMContentLoaded', () => {
  const imgRows = document.querySelectorAll('.b-image-row');
  if (imgRows.length) {
    let rowId = 0;
    Array.from(imgRows, (imgRow) => {
      rowId += 1;
      imgRow.classList.add(`b-image-row-${rowId}`);
      imgRow._imageRowId = rowId;

      /* use a style tag so we have media-queries */
      const imgRowStyle = imgRow.parentNode.insertBefore(document.createElement('style'), imgRow);

      const rowStyle = () => {
        const imgsLL = imgRow.querySelectorAll('.b-image-row__img');  // images, including those failed and replaced by LazyLoad
        const aspectRatios = [];
        const parentRow = imgsLL[0].closest('.b-image-row');
        const parentRowCS = getComputedStyle(parentRow);
        const rowWidth = parentRow.getBoundingClientRect().width
                          - parseInt(parentRowCS.paddingLeft, 10)
                          - parseInt(parentRowCS.paddingRight, 10);
        let imgRowStyleMobile = '';
        let imgId = 0;
        Array.from(imgsLL, (img) => {
          if (img.naturalWidth) {
            aspectRatios.push(img.naturalWidth / img.naturalHeight);
          } else {
            // failed image
            aspectRatios.push(1);
            img.closest('.b-image-row__item').classList.add('b-image-row__item--failed');
          }
          imgId += 1;
          if (imgId % 2 === 0) {
            /* apply the same height calculation for mobile as the general rule below,
             * but in batches of 2 images, and with rounding and a tiny reduction to
             * ensure no pair is wider than a wrapped row */
            imgRowStyleMobile += `
              .b-image-row-${parentRow._imageRowId} .b-image-row__item:nth-child(${imgId - 1}),
              .b-image-row-${parentRow._imageRowId} .b-image-row__item:nth-child(${imgId}) {
                height: ${Math.round((rowWidth / (aspectRatios[imgId - 2] + aspectRatios[imgId - 1])) - 1.5)}px;
              }
            `;
          }
          return true;
        });
        imgRowStyle.innerHTML = `
          @media screen and (max-width: 768px) {
            .b-image-row-${parentRow._imageRowId} {
              flex-wrap: wrap;
            }
            ${imgRowStyleMobile}
          }
          .b-image-row-${parentRow._imageRowId} .b-image-row__item {
            height: ${rowWidth / aspectRatios.reduce((a, b) => a + b)}px;
          }
        `;
      };

      const imgs = imgRow.querySelectorAll('.b-image-row__img');
      const complete = [];
      const loaded = (anyImg) => {
        complete.push(anyImg);
        if (complete.length === imgs.length) rowStyle();
      };

      Array.from(imgs, (img) => {
        /* wait for LazyLoad to set src attribute for each img */
        const observer = new MutationObserver(() => {
          /* then wait for each img to load, or fail */
          if (img.complete) {
            loaded(img);
          } else {
            document.addEventListener('load', (e) => {
              if (e.target === img) loaded(img);
            }, false);
            img.addEventListener('error', () => { loaded(img); }, false);
          }
        });
        observer.observe(img, { attributes: true });
        return true;
      });

      window.addEventListener('resize', rowStyle, false);
      return true;
    });
  }
}, true);

/* eslint-enable no-underscore-dangle */
<|endoftext|>


<commit_before>import React from 'react';

export default function ProgressControl(props) {
  return (
    <div className="video-react-progress-control">
    </div>
  );
}
<commit_msg>Implemented ProgressControl component.<commit_after>import React, { Component, PropTypes } from 'react';
import { findDOMNode } from 'react-dom';
import * as Dom from '../../lib/dom';
import SeekBar from './SeekBar';

const propTypes = {
  player: PropTypes.object.isRequired,
};

export default class ProgressControl extends Component {

  constructor(props, context) {
    super(props, context);

    this.state = {
      mouseTime: {
        time: null,
        position: 0,
      },
    };

    this.handleMouseMoveThrottle = this.handleMouseMove.bind(this);
  }

  handleMouseMove(event) {
    if (!event.pageX) {
      return;
    }
    const { player: { duration } } = this.props;
    const node = findDOMNode(this.seekBar);
    const newTime = Dom.getPointerPosition(node, event).x * duration;
    const position = event.pageX - Dom.findElPosition(node).left;

    this.setState({
      mouseTime: {
        time: newTime,
        position,
      },
    });
  }

  render() {
    return (
      <div
        onMouseMove={this.handleMouseMoveThrottle}
        className="video-react-progress-control video-react-control"
      >
        <SeekBar
          mouseTime={this.state.mouseTime}
          ref={
            (c) => {
              this.seekBar = c;
            }
          }
          {...this.props}
        />
      </div>
    );
  }
}

ProgressControl.propTypes = propTypes;
<|endoftext|>


<commit_before>import React from 'react'
import Input from './input'

export default class DatePicker extends Input {
  getValue(target) {
    return target.valueAsDate.toISOString()
  }

  get dateValue(){
    return new Date(this.state.value).toLocaleDateString()
  }

  render() {
    return (
      <input
        className={`ct-input ct-date-picker`}
        id={this.props.property.id}
        onBlur={() => this.onBlur()}
        onChange={e => this.onChange(e)}
        type='date'
        value={this.dateValue} />
    )
  }
}
<commit_msg>chore: Fixed lint error<commit_after>import React from 'react'
import Input from './input'

export default class DatePicker extends Input {
  getValue(target) {
    return target.valueAsDate.toISOString()
  }

  get dateValue() {
    return new Date(this.state.value).toLocaleDateString()
  }

  render() {
    return (
      <input
        className={`ct-input ct-date-picker`}
        id={this.props.property.id}
        onBlur={() => this.onBlur()}
        onChange={e => this.onChange(e)}
        type='date'
        value={this.dateValue} />
    )
  }
}
<|endoftext|>