<commit_before>// Generics
var ChopChop = (function($, ChopChop) {
    var api = ChopChop.api = {},
        plugins = ChopChop.plugins = {};

    // Exception
    var Exception = ChopChop.Exception = function(message) {
        this.message = message;
    };

    Exception.prototype = {
        toString: function() {
            return 'EXCEPTION: ' + this.message;
        }
    };

    // Initialisation
    var defaultInitOptions = {
        'toggle': {},
        'collapse': {},
        'priorityNav': {}
    };

    ChopChop.init = api.init = function(options) {
        options = options || defaultInitOptions;

        for (var plugin in options) {
            if (!options.hasOwnProperty(plugin)) {
                continue;
            }

            var name = plugin.charAt(0).toUpperCase() + plugin.substr(1);

            if (!plugins[plugin] && ChopChop[name]) {
                plugins[plugin] = new ChopChop[name](options);
            }
        }
    };

    // jQuery goodness
    $.fn.chopchop = function() {
        var args = Array.prototype.slice.call(arguments),
            numArgs = args.length;

        if (numArgs === 0) {
            return api;
        }

        var method = args.shift();

        if (api.hasOwnProperty(method)) {
            return this.each(function() {
                var params = args.slice(0);
                params.unshift(this);
                api[method].apply(api, params);
            });
        }
    };

    return ChopChop;
})(jQuery, ChopChop || {});

// Utilities
var ChopChop = (function($, ChopChop) {
    ChopChop.util = {
        debounce: function(func, wait, immediate) {
            var timeout;

            return function() {
                var context = this,
                    args = arguments;

                var later = function() {
                    timeout = null;

                    if (!immediate) {
                        func.apply(context, args);
                    }
                };

                var callNow = immediate && !timeout;
                clearTimeout(timeout);
                timeout = setTimeout(later, wait);

                if (callNow) {
                    func.apply(context, args);
                }
            };
        }
    };

    return ChopChop;
})(jQuery, ChopChop || {});

// Toggles
var ChopChop = (function($, ChopChop) {
    var defaultOptions = {
        classActive: 'is-active',
        classInactive: 'is-inactive'
    };

    // Toggle
    var Toggle = ChopChop.Toggle = function(options) {
        this.options = $.extend({}, defaultOptions, options);
        this.init();
    };

	var Action = {
		TOGGLE: 'toggle',
		ACTIVATE: 'activate',
		DEACTIVATE: 'deactivate'
	};

    var Trigger = {
        DIRECT_ONLY: 'direct-only',
        INDIRECT_ONLY: 'indirect-only',     // Why would you want this? Oh well.
        BOTH: 'both'    // Default
    };

    Toggle.prototype = {
        init: function() {
            var self = this;

            $(document).on('click', '[data-cc-action]', function(e) {
                var $this = $(this), t,
                    $target = (t = $this.data('cc-target')) ? $('#' + t) : false,
                    mode = $this.data('cc-trigger-on') || Trigger.BOTH;

                if (!$target) {
                    return;
                }

                if (mode === Trigger.DIRECT_ONLY && e.target !== this) {
                    return;
                } else if (mode === Trigger.INDIRECT_ONLY && e.target === this) {
                    return;
                }

                e.preventDefault();
				self.performAction($target, $this.data('cc-action') || Action.TOGGLE);
            });
        },
        toggle: function(target, action) {
            var $target = typeof target === 'string' ? $('#' + target) : $(target);

            if (!$target.size()) {
                return;
            }

            this.performAction($target, action || Action.TOGGLE);
        },
		performAction: function($el, action) {
			if (action === Action.TOGGLE) {
				if ($el.hasClass('is-active')) {
					action = Action.DEACTIVATE;
				} else {
					action = Action.ACTIVATE;
				}
			}

			var pending = [$el], processed = [],
				id, dependants, group, $current,
                iterations = 0,
                el, chain, $other, i;

			while (pending.length > 0) {
				$current = pending.shift();

				// Skip already processed nodes
				if (processed.indexOf($current) !== -1) {
					continue;
				}

				id = $current.attr('id');
				group = $current.data('cc-group');

				// NOTE: Perhaps don't process these; add to some kind of queue first (with activate or deactivate action)
				for (i = 0, all = $('[ data-cc-group="' + group + '"]'), l = all.length; i < l; ++i) {
                    el = all[i];
    			    $other = $(el);

                    // Skip element itself
					if (el === $current[0]) {
                        continue;
                    }

                    // Just a group member, not the target: deactivate
                    $other.addClass('is-inactive').removeClass('is-active');
				}

                // Process target
                if (action === Action.ACTIVATE) {
                    $current.addClass('is-active').removeClass('is-inactive');
                    $current.trigger('chopchop:activate');
                } else {
                    $current.addClass('is-inactive').removeClass('is-active');
                    $current.trigger('chopchop:deactivate');
                }

                // Chain onto targets
                chain = ($current.data('cc-cascade-' + action) || '').split(',')
                    .concat(($current.data('cc-cascade') || '').split(','));

                for (i = 0, l = chain.length; i < l; ++i) {
                    $other = $('#' + chain[i]);

                    if (processed.indexOf($other[0]) !== -1 || pending.indexOf($other[0]) !== -1) {
                        continue;
                    }

                    pending.push($other);
                }

				processed.push($current[0]);
                iterations += 1;

                if (iterations > 100) {
                    throw new ChopChop.Exception("Too many iterations, aborting");
                }
			}
		}
    };

    // Expose toggle api
    ChopChop.api.toggle = function() {
        return ChopChop.plugins.toggle.toggle.apply(ChopChop.plugins.toggle, arguments);
    };

    return ChopChop;
})(jQuery, ChopChop || {});

// Collapse
var ChopChop = (function($, ChopChop) {
    // Tabordion types
    var Types = {
        ONE_OPEN: 'one-open',
        ONE_COLLAPSIBLE: 'one-collapsible',
        ALL_COLLAPSIBLE: 'all-collapsible'
    };

    var defaultOptions = {
        classActive: 'is-active',
        classInactive: 'is-inactive',
        headerSelector: '[class*="__header"]',
        bodySelector: '[class*="__body"]',
        type: Types.ONE_OPEN
    };

    // Collapse
    var Collapse = ChopChop.Collapse = function(options) {
        this.options = $.extend({}, defaultOptions, options);
        this.id = 0;
        this.init();
    };

    // Attribute creators and appliers for all collapse types
    var attributors = {};
    attributors[Types.ONE_OPEN] = function($header, $body, name, i) {
        $header.attr({
            'id': name + '-header-' + i,
            'data-cc-group': name + '-headers',
            'data-cc-action': 'activate',
            'data-cc-target': name + '-body-' + i
        });
        $body.attr({
            'id': name + '-body-' + i,
            'data-cc-group': name + '-bodies',
            'data-cc-cascade-activate': name + '-header-' + i
        });
    };
    attributors[Types.ONE_COLLAPSIBLE] = function($header, $body, name, i) {
        $header.attr({
            'id': name + '-header-' + i,
            'data-cc-group': name + '-headers',
            'data-cc-action': 'toggle',
            'data-cc-target': name + '-body-' + i
        });
        $body.attr({
            'id': name + '-body-' + i,
            'data-cc-group': name + '-bodies',
            'data-cc-cascade': name + '-header-' + i
        });
    };
    attributors[Types.ALL_COLLAPSIBLE] = function($header, $body, name, i) {
        $header.attr({
            'id': name + '-header-' + i,
            'data-cc-action': 'toggle',
            'data-cc-target': name + '-body-' + i
        });
        $body.attr({
            'id': name + '-body-' + i,
            'data-cc-cascade': name + '-header-' + i
        });
    };

    Collapse.prototype = {
        init: function() {
            var self = this;

            $('[data-cc-collapse]').each(function() {
                self.applyToContainer(this);
            });
        },
        getNextName: function() {
            return 'collapse' + (++this.id);
        },
        applyToContainer: function(root, options) {
            options = $.extend({}, this.options, options);

            var $root = $(root),
                name = options.name || $root.data('cc-collapse-name') || this.getNextName(),
                type =  $root.data('cc-collapse') || options.type,
                headers = $root.find(options.headerSelector),
                bodies = $root.find(options.bodySelector);

            if (headers.size() !== bodies.size()) {
                throw new ChopChop.Exception(
                    "Collapse: number of headers does not match number of bodies (" + headers.size() + " vs " + bodies.size()+ ")"
                );
            }

            for (var i = 0, l = headers.size(); i < l; ++i) {
                attributors[type]($(headers[i]), $(bodies[i]), name, i);
            }
        }
    };

    // Expose api
    ChopChop.api.collapse = function() {
        return ChopChop.plugins.collapse.applyToContainer.apply(ChopChop.plugins.collapse, arguments);
    };

    return ChopChop;
})(jQuery, ChopChop || {});

// Priority nav
var ChopChop = (function($, ChopChop) {
    var defaultOptions = {
    };

    // Priority nav
    var PriorityNav = ChopChop.Collapse = function(options) {
        this.options = $.extend({}, defaultOptions, options);
        this.init();
    };

    PriorityNav.prototype = {
        init: function() {
            var self = this;

            $('[data-cc-priority-subnav]').each(function() {
                self.applyToContainer(this);
            });
        },
        applyToContainer: function(root, options) {
            options = $.extend({}, this.options, options);

            var $source = $(root),
                $target = $('#' + $source.data('cc-priority-subnav'));

            if ($target.size() !== 1) {
                throw new ChopChop.Exception('Cannot find/found more than one priority subnav target: ' + $source.data('cc-priority-subnav'));
            }

            var $sourceContainer = $source.find('.priority-nav__container'),
                $sourceItems = $sourceContainer.find('> ul > .nav__item'),
                $targetItems = $sourceItems.clone(),
                widths = [],
                $sourceList = $sourceContainer.find('.nav'),
                $targetList = $target.find('.nav');

            // Clone full nav from source to target list
            $targetItems.addClass('u-hidden');
            $targetList.append($targetItems);

            var resizer = function() {
                var containerWidth = $sourceContainer.width(),
                    $sourceItem, $targetItem,
                    totalWidth = 0,
                    $t = $sourceList;

                if (widths.length === 0 || $sourceItems.eq(0).outerWidth() !== widths[0]) {
                    widths = [];

                    // Unhide all source items, as hidden sources items will have a width of 0
                    // Any items that won't fit will be hidden again in the next section
                    $sourceItems.removeClass('u-hidden');
                    $sourceItems.outerWidth(function(i, w) {
                        widths.push(w);
                    });
                }

                // Iterate over all items, hiding and showing items selectively based on fit
                for (var i = 0, l = $sourceItems.size(); i < l; ++i) {
                    $sourceItem = $sourceItems.eq(i);
                    $targetItem = $targetItems.eq(i);
                    totalWidth += widths[i];

                    if (totalWidth >= containerWidth) {
                        $sourceItem.addClass('u-hidden');
                        $targetItem.removeClass('u-hidden');
                    } else {
                        $sourceItem.removeClass('u-hidden');
                        $targetItem.addClass('u-hidden');
                    }
                }
            };

            $(window).resize(ChopChop.util.debounce(resizer, 50));

            resizer();
        }
    };

    // Expose api
    ChopChop.api.priorityNav = function() {
        return ChopChop.plugins.priorityNav.applyToContainer.apply(ChopChop.plugins.priorityNav, arguments);
    };

    return ChopChop;
})(jQuery, ChopChop || {});
<commit_msg>Show/hide more button + trigger event when prio nav is resized<commit_after>// Generics
var ChopChop = (function($, ChopChop) {
    var api = ChopChop.api = {},
        plugins = ChopChop.plugins = {};

    // Exception
    var Exception = ChopChop.Exception = function(message) {
        this.message = message;
    };

    Exception.prototype = {
        toString: function() {
            return 'EXCEPTION: ' + this.message;
        }
    };

    // Initialisation
    var defaultInitOptions = {
        'toggle': {},
        'collapse': {},
        'priorityNav': {}
    };

    ChopChop.init = api.init = function(options) {
        options = options || defaultInitOptions;

        for (var plugin in options) {
            if (!options.hasOwnProperty(plugin)) {
                continue;
            }

            var name = plugin.charAt(0).toUpperCase() + plugin.substr(1);

            if (!plugins[plugin] && ChopChop[name]) {
                plugins[plugin] = new ChopChop[name](options);
            }
        }
    };

    // jQuery goodness
    $.fn.chopchop = function() {
        var args = Array.prototype.slice.call(arguments),
            numArgs = args.length;

        if (numArgs === 0) {
            return api;
        }

        var method = args.shift();

        if (api.hasOwnProperty(method)) {
            return this.each(function() {
                var params = args.slice(0);
                params.unshift(this);
                api[method].apply(api, params);
            });
        }
    };

    return ChopChop;
})(jQuery, ChopChop || {});

// Utilities
var ChopChop = (function($, ChopChop) {
    ChopChop.util = {
        debounce: function(func, wait, immediate) {
            var timeout;

            return function() {
                var context = this,
                    args = arguments;

                var later = function() {
                    timeout = null;

                    if (!immediate) {
                        func.apply(context, args);
                    }
                };

                var callNow = immediate && !timeout;
                clearTimeout(timeout);
                timeout = setTimeout(later, wait);

                if (callNow) {
                    func.apply(context, args);
                }
            };
        }
    };

    return ChopChop;
})(jQuery, ChopChop || {});

// Toggles
var ChopChop = (function($, ChopChop) {
    var defaultOptions = {
        classActive: 'is-active',
        classInactive: 'is-inactive'
    };

    // Toggle
    var Toggle = ChopChop.Toggle = function(options) {
        this.options = $.extend({}, defaultOptions, options);
        this.init();
    };

	var Action = {
		TOGGLE: 'toggle',
		ACTIVATE: 'activate',
		DEACTIVATE: 'deactivate'
	};

    var Trigger = {
        DIRECT_ONLY: 'direct-only',
        INDIRECT_ONLY: 'indirect-only',     // Why would you want this? Oh well.
        BOTH: 'both'    // Default
    };

    Toggle.prototype = {
        init: function() {
            var self = this;

            $(document).on('click', '[data-cc-action]', function(e) {
                var $this = $(this), t,
                    $target = (t = $this.data('cc-target')) ? $('#' + t) : false,
                    mode = $this.data('cc-trigger-on') || Trigger.BOTH;

                if (!$target) {
                    return;
                }

                if (mode === Trigger.DIRECT_ONLY && e.target !== this) {
                    return;
                } else if (mode === Trigger.INDIRECT_ONLY && e.target === this) {
                    return;
                }

                e.preventDefault();
				self.performAction($target, $this.data('cc-action') || Action.TOGGLE);
            });
        },
        toggle: function(target, action) {
            var $target = typeof target === 'string' ? $('#' + target) : $(target);

            if (!$target.size()) {
                return;
            }

            this.performAction($target, action || Action.TOGGLE);
        },
		performAction: function($el, action) {
			if (action === Action.TOGGLE) {
				if ($el.hasClass('is-active')) {
					action = Action.DEACTIVATE;
				} else {
					action = Action.ACTIVATE;
				}
			}

			var pending = [$el], processed = [],
				id, dependants, group, $current,
                iterations = 0,
                el, chain, $other, i;

			while (pending.length > 0) {
				$current = pending.shift();

				// Skip already processed nodes
				if (processed.indexOf($current) !== -1) {
					continue;
				}

				id = $current.attr('id');
				group = $current.data('cc-group');

				// NOTE: Perhaps don't process these; add to some kind of queue first (with activate or deactivate action)
				for (i = 0, all = $('[ data-cc-group="' + group + '"]'), l = all.length; i < l; ++i) {
                    el = all[i];
    			    $other = $(el);

                    // Skip element itself
					if (el === $current[0]) {
                        continue;
                    }

                    // Just a group member, not the target: deactivate
                    $other.addClass('is-inactive').removeClass('is-active');
				}

                // Process target
                if (action === Action.ACTIVATE) {
                    $current.addClass('is-active').removeClass('is-inactive');
                    $current.trigger('chopchop:activate');
                } else {
                    $current.addClass('is-inactive').removeClass('is-active');
                    $current.trigger('chopchop:deactivate');
                }

                // Chain onto targets
                chain = ($current.data('cc-cascade-' + action) || '').split(',')
                    .concat(($current.data('cc-cascade') || '').split(','));

                for (i = 0, l = chain.length; i < l; ++i) {
                    $other = $('#' + chain[i]);

                    if (processed.indexOf($other[0]) !== -1 || pending.indexOf($other[0]) !== -1) {
                        continue;
                    }

                    pending.push($other);
                }

				processed.push($current[0]);
                iterations += 1;

                if (iterations > 100) {
                    throw new ChopChop.Exception("Too many iterations, aborting");
                }
			}
		}
    };

    // Expose toggle api
    ChopChop.api.toggle = function() {
        return ChopChop.plugins.toggle.toggle.apply(ChopChop.plugins.toggle, arguments);
    };

    return ChopChop;
})(jQuery, ChopChop || {});

// Collapse
var ChopChop = (function($, ChopChop) {
    // Tabordion types
    var Types = {
        ONE_OPEN: 'one-open',
        ONE_COLLAPSIBLE: 'one-collapsible',
        ALL_COLLAPSIBLE: 'all-collapsible'
    };

    var defaultOptions = {
        classActive: 'is-active',
        classInactive: 'is-inactive',
        headerSelector: '[class*="__header"]',
        bodySelector: '[class*="__body"]',
        type: Types.ONE_OPEN
    };

    // Collapse
    var Collapse = ChopChop.Collapse = function(options) {
        this.options = $.extend({}, defaultOptions, options);
        this.id = 0;
        this.init();
    };

    // Attribute creators and appliers for all collapse types
    var attributors = {};
    attributors[Types.ONE_OPEN] = function($header, $body, name, i) {
        $header.attr({
            'id': name + '-header-' + i,
            'data-cc-group': name + '-headers',
            'data-cc-action': 'activate',
            'data-cc-target': name + '-body-' + i
        });
        $body.attr({
            'id': name + '-body-' + i,
            'data-cc-group': name + '-bodies',
            'data-cc-cascade-activate': name + '-header-' + i
        });
    };
    attributors[Types.ONE_COLLAPSIBLE] = function($header, $body, name, i) {
        $header.attr({
            'id': name + '-header-' + i,
            'data-cc-group': name + '-headers',
            'data-cc-action': 'toggle',
            'data-cc-target': name + '-body-' + i
        });
        $body.attr({
            'id': name + '-body-' + i,
            'data-cc-group': name + '-bodies',
            'data-cc-cascade': name + '-header-' + i
        });
    };
    attributors[Types.ALL_COLLAPSIBLE] = function($header, $body, name, i) {
        $header.attr({
            'id': name + '-header-' + i,
            'data-cc-action': 'toggle',
            'data-cc-target': name + '-body-' + i
        });
        $body.attr({
            'id': name + '-body-' + i,
            'data-cc-cascade': name + '-header-' + i
        });
    };

    Collapse.prototype = {
        init: function() {
            var self = this;

            $('[data-cc-collapse]').each(function() {
                self.applyToContainer(this);
            });
        },
        getNextName: function() {
            return 'collapse' + (++this.id);
        },
        applyToContainer: function(root, options) {
            options = $.extend({}, this.options, options);

            var $root = $(root),
                name = options.name || $root.data('cc-collapse-name') || this.getNextName(),
                type =  $root.data('cc-collapse') || options.type,
                headers = $root.find(options.headerSelector),
                bodies = $root.find(options.bodySelector);

            if (headers.size() !== bodies.size()) {
                throw new ChopChop.Exception(
                    "Collapse: number of headers does not match number of bodies (" + headers.size() + " vs " + bodies.size()+ ")"
                );
            }

            for (var i = 0, l = headers.size(); i < l; ++i) {
                attributors[type]($(headers[i]), $(bodies[i]), name, i);
            }
        }
    };

    // Expose api
    ChopChop.api.collapse = function() {
        return ChopChop.plugins.collapse.applyToContainer.apply(ChopChop.plugins.collapse, arguments);
    };

    return ChopChop;
})(jQuery, ChopChop || {});

// Priority nav
var ChopChop = (function($, ChopChop) {
    var defaultOptions = {
    };

    // Priority nav
    var PriorityNav = ChopChop.Collapse = function(options) {
        this.options = $.extend({}, defaultOptions, options);
        this.init();
    };

    PriorityNav.prototype = {
        init: function() {
            var self = this;

            $('[data-cc-priority-subnav]').each(function() {
                self.applyToContainer(this);
            });
        },
        applyToContainer: function(root, options) {
            options = $.extend({}, this.options, options);

            var $source = $(root),
                $target = $('#' + $source.data('cc-priority-subnav'));

            if ($target.size() !== 1) {
                throw new ChopChop.Exception('Cannot find/found more than one priority subnav target: ' + $source.data('cc-priority-subnav'));
            }

            var $sourceContainer = $source.find('.priority-nav__container'),
                $sourceItems = $sourceContainer.find('> ul > .nav__item'),
                $targetItems = $sourceItems.clone(),
                widths = [],
                $sourceList = $sourceContainer.find('.nav'),
                $targetList = $target.find('.nav'),
                $showMore = $source.find('.priority-nav__toggle');

            // Clone full nav from source to target list
            $targetItems.addClass('u-hidden');
            $targetList.append($targetItems);

            var resizer = function() {
                var containerWidth = $sourceContainer.width(),
                    $sourceItem, $targetItem,
                    totalWidth = 0,
                    $t = $sourceList;

                if (widths.length === 0 || $sourceItems.eq(0).outerWidth() !== widths[0]) {
                    widths = [];

                    // Unhide all source items, as hidden sources items will have a width of 0
                    // Any items that won't fit will be hidden again in the next section
                    $sourceItems.removeClass('u-hidden');
                    $sourceItems.outerWidth(function(i, w) {
                        widths.push(w);
                    });
                }

                // Iterate over all items, hiding and showing items selectively based on fit
                var visible = 0, hidden = 0;

                for (var i = 0, l = $sourceItems.size(); i < l; ++i) {
                    $sourceItem = $sourceItems.eq(i);
                    $targetItem = $targetItems.eq(i);
                    totalWidth += widths[i];

                    if (totalWidth >= containerWidth) {
                        $sourceItem.addClass('u-hidden');
                        $targetItem.removeClass('u-hidden');
                        hidden += 1;
                    } else {
                        $sourceItem.removeClass('u-hidden');
                        $targetItem.addClass('u-hidden');
                        visible += 1;
                    }
                }

                // If no items are hidden, disable the more button
                if (hidden === 0) {
                    $showMore.addClass('is-disabled');
                } else {
                    $showMore.removeClass('is-disabled');
                }

                $source.trigger('chopchop:priority-nav', [visible, hidden]);
            };

            $(window).resize(ChopChop.util.debounce(resizer, 50));

            resizer();
        }
    };

    // Expose api
    ChopChop.api.priorityNav = function() {
        return ChopChop.plugins.priorityNav.applyToContainer.apply(ChopChop.plugins.priorityNav, arguments);
    };

    return ChopChop;
})(jQuery, ChopChop || {});
<|endoftext|>


<commit_before>var BaseElement = require('base-element')
var inherits = require('inherits')
var config = require('../config')[process.env.NODE_ENV]

module.exports = Landing
inherits(Landing, BaseElement)

function Landing (options) {
  if (!(this instanceof Landing)) return new Landing(options)
  BaseElement.call(this)
}

Landing.prototype.render = function (state) {
  var h = this.html.bind(this)
  var elements = []

  var url = 'https://github.com/login/oauth/authorize?client_id=' + config.client_id + '&scope=gist&redirect_uri=' + config.redirect_uri

  var button = h('a.button.large.button-blue', { href: url }, [
    h('i.fa.fa-github-square'),
    ' Sign in with GitHub'
  ])

  elements.push(h('div.welcome', [
    h('h2', 'Hello! Let\'s edit some data!'),
    h('p', 'editdata.org is a simple version of flatsheet, a tool for curating data as editorial content.'),
    button,
    h('p', h('a', { href: 'http://flatsheet.io', target: '_blank' }, 'learn more about flatsheet'))
  ]))

  var vtree = h('div.landing', elements)
  return this.afterRender(vtree)
}
<commit_msg>add link to github<commit_after>var BaseElement = require('base-element')
var inherits = require('inherits')
var config = require('../config')[process.env.NODE_ENV]

module.exports = Landing
inherits(Landing, BaseElement)

function Landing (options) {
  if (!(this instanceof Landing)) return new Landing(options)
  BaseElement.call(this)
}

Landing.prototype.render = function (state) {
  var h = this.html.bind(this)
  var elements = []

  var url = 'https://github.com/login/oauth/authorize?client_id=' + config.client_id + '&scope=gist&redirect_uri=' + config.redirect_uri

  var button = h('a.button.large.button-blue', { href: url }, [
    h('i.fa.fa-github-square'),
    ' Sign in with GitHub'
  ])

  elements.push(h('div.welcome', [
    h('h2', 'Hello! Let\'s edit some data!'),
    h('p', 'editdata.org is a simple version of flatsheet, a tool for curating data as editorial content.'),
    button,
    h('p', h('a', { href: 'http://flatsheet.io', target: '_blank' }, 'learn more about flatsheet')),
    h('p', h('a', { href: 'http://github.com/flatsheet/editdata.org', target: '_blank' }, 'editdata.org on github'))
  ]))

  var vtree = h('div.landing', elements)
  return this.afterRender(vtree)
}
<|endoftext|>


<commit_before>var composebox_typeahead = (function () {

//************************************
// AN IMPORTANT NOTE ABOUT TYPEAHEADS
//************************************
// They do not do any HTML escaping, at all.
// And your input to them is rendered as though it were HTML by
// the default highlighter.
//
// So if you are not using trusted input, you MUST use the a
// highlighter that escapes (i.e. one that calls
// typeahead_helper.highlight_with_escaping).

var exports = {};

function get_pm_recipients(query_string) {
    // Assumes email addresses don't have commas or semicolons in them
    return query_string.split(/\s*[,;]\s*/);
}

// Returns an array of private message recipients, removing empty elements.
// For example, "a,,b, " => ["a", "b"]
exports.get_cleaned_pm_recipients = function (query_string) {
    var recipients = get_pm_recipients(query_string);
    recipients = $.grep(recipients, function (elem, idx) {
        return elem.match(/\S/);
    });
    return recipients;
};

function get_last_recipient_in_pm(query_string) {
    var recipients = get_pm_recipients(query_string);
    return recipients[recipients.length-1];
}

function composebox_typeahead_highlighter(item) {
    var query = this.query;
    if ($(this.$element).attr('id') === 'private_message_recipient') {
        // There could be multiple recipients in a private message,
        // we want to decide what to highlight based only on the most
        // recent one we're entering.
        query = get_last_recipient_in_pm(this.query);
    }
    return typeahead_helper.highlight_with_escaping(query, item);
}

// nextFocus is set on a keydown event to indicate where we should focus on keyup.
// We can't focus at the time of keydown because we need to wait for typeahead.
// And we can't compute where to focus at the time of keyup because only the keydown
// has reliable information about whether it was a tab or a shift+tab.
var nextFocus = false;

function handle_keydown(e) {
    var code = e.keyCode || e.which;

    if (code === 13 || (code === 9 && !e.shiftKey)) { // Enter key or tab key
        if (e.target.id === "stream" || e.target.id === "subject" || e.target.id === "private_message_recipient") {
            // For enter, prevent the form from submitting
            // For tab, prevent the focus from changing again
            e.preventDefault();
        }

        // In the new_message_content box, preventDefault() for tab but not for enter
        if (e.target.id === "new_message_content" && code !== 13) {
            e.preventDefault();
        }

        if (e.target.id === "stream") {
            nextFocus = "subject";
        } else if (e.target.id === "subject") {
            if (code === 13) e.preventDefault();
            nextFocus = "new_message_content";
        } else if (e.target.id === "private_message_recipient") {
            nextFocus = "new_message_content";
        } else if (e.target.id === "new_message_content") {
            if (code === 13) {
                nextFocus = false;
            } else {
                nextFocus = "compose-send-button";
            }
        } else {
            nextFocus = false;
        }

        // If no typeaheads are shown...
        if (!($("#subject").data().typeahead.shown ||
              $("#stream").data().typeahead.shown ||
              $("#private_message_recipient").data().typeahead.shown ||
              $("#new_message_content").data().typeahead.shown)) {

            // If no typeaheads are shown and the user is tabbing from the message content box,
            // then there's no need to wait and we can change the focus right away.
            // Without this code to change the focus right away, if the user presses enter
            // before they fully release the tab key, the tab will be lost.  Note that we don't
            // want to change focus right away in the private_message_recipient box since it
            // takes the typeaheads a little time to open after the user finishes typing, which
            // can lead to the focus moving without the autocomplete having a chance to happen.
            if ((page_params.domain === "humbughq.com" && nextFocus === "compose-send-button") ||
                (page_params.domain !== "humbughq.com" && nextFocus)) {
                ui.focus_on(nextFocus);
                nextFocus = false;
            }

            // If no typeaheads are shown and the user has configured enter to send,
            // then make enter send instead of inserting a line break.
            // (Unless shift is being held down, which we *do* want to insert a linebreak)
            if (e.target.id === "new_message_content"
                && code === 13 && !e.shiftKey
                && page_params.enter_sends) {
                e.preventDefault();
                if ($("#compose-send-button").attr('disabled') !== "disabled") {
                    $("#compose-send-button").attr('disabled', 'disabled');
                    compose.finish();
                }
            }
        }

        return false;
    }
}

function handle_keyup(e) {
    var code = e.keyCode || e.which;
    if (code === 13 || (code === 9 && !e.shiftKey)) { // Enter key or tab key
        if (nextFocus) {
            ui.focus_on(nextFocus);
            nextFocus = false;
        }
    }
}

// http://stackoverflow.com/questions/3380458/looking-for-a-better-workaround-to-chrome-select-on-focus-bug
function select_on_focus(field_id) {
    $("#" + field_id).focus(function(e) {
        $("#" + field_id).select().one('mouseup', function (e) {
            e.preventDefault();
        });
    });
}

exports.split_at_cursor = function(query) {
    var cursor = $('#new_message_content').caret().start;
    return [query.slice(0, cursor), query.slice(cursor)];
};

exports.initialize = function () {
    select_on_focus("stream");
    select_on_focus("subject");
    select_on_focus("private_message_recipient");

    // These handlers are at the "form" level so that they are called after typeahead
    $("form#send_message_form").keydown(function(e) {
        handle_keydown(e);
    });
    $("form#send_message_form").keyup(function(e) {
        handle_keyup(e);
    });

    $("#enter_sends").click(function () {
        var send_button = $("#compose-send-button");
        page_params.enter_sends = $("#enter_sends").is(":checked");
        if (page_params.enter_sends) {
            send_button.fadeOut();
        } else {
            send_button.fadeIn();
        }
        return $.ajax({
            dataType: 'json',
            url: '/json/change_enter_sends',
            type: 'POST',
            data: {'enter_sends': page_params.enter_sends}
        });
    });
    $("#enter_sends").prop('checked', page_params.enter_sends);
    if (page_params.enter_sends) $("#compose-send-button").hide();

    // limit number of items so the list doesn't fall off the screen
    $( "#stream" ).typeahead({
        source: function (query, process) {
            return subs.subscribed_streams();
        },
        items: 3,
        highlighter: composebox_typeahead_highlighter,
        matcher: function (item) {
            // The matcher for "stream" is strictly prefix-based,
            // because we want to avoid mixing up streams.
            var q = this.query.trim().toLowerCase();
            return (item.toLowerCase().indexOf(q) === 0);
        }
    });

    $( "#subject" ).typeahead({
        source: function (query, process) {
            var stream_name = $("#stream").val();
            if (subject_dict.hasOwnProperty(stream_name)) {
                return subject_dict[stream_name];
            }
            return [];
        },
        items: 2,
        highlighter: composebox_typeahead_highlighter,
        sorter: typeahead_helper.sort_subjects
    });

    $( "#private_message_recipient" ).typeahead({
        source: typeahead_helper.private_message_typeahead_list,
        items: 2,
        highlighter: composebox_typeahead_highlighter,
        matcher: function (item) {
            var current_recipient = get_last_recipient_in_pm(this.query);
            // If the name is only whitespace (does not contain any non-whitespace),
            // we're between typing names; don't autocomplete anything for us.
            if (! current_recipient.match(/\S/)) {
                return false;
            }

            // Case-insensitive.
            return (item.toLowerCase().indexOf(current_recipient.toLowerCase()) !== -1);
        },
        sorter: typeahead_helper.sort_recipientbox_typeahead,
        updater: function (item) {
            var previous_recipients = exports.get_cleaned_pm_recipients(this.query);
            previous_recipients.pop();
            previous_recipients = previous_recipients.join(", ");
            if (previous_recipients.length !== 0) {
                previous_recipients += ", ";
            }
            return previous_recipients + typeahead_helper.private_message_mapped[item].email + ", ";
        },
        stopAdvance: true // Do not advance to the next field on a tab or enter
    });

    $( "#new_message_content" ).typeahead({
        source: typeahead_helper.private_message_typeahead_list,
        items: 2,
        highlighter: composebox_typeahead_highlighter,
        matcher: function (item) {
            var query = exports.split_at_cursor(this.query)[0];

            var strings = query.split(/[\s*(){}\[\]]/);
            if (strings.length < 1) {
                return false;
            }
            var current_recipient = strings[strings.length-1];
            if (current_recipient.length < 2 || current_recipient.charAt(0) !== "@") {
                return false;
            }
            current_recipient = current_recipient.substring(1);

            // Case-insensitive.
            return (item.toLowerCase().indexOf(current_recipient.toLowerCase()) !== -1);
        },
        sorter: typeahead_helper.sort_textbox_typeahead,
        updater: function (item) {
            var pieces = exports.split_at_cursor(this.query);
            var beginning = pieces[0];
            var rest = pieces[1];

            beginning = beginning.replace(/@\S+$/, "") + "@**" + typeahead_helper.private_message_mapped[item].full_name + "**";
            // Keep the cursor after the newly inserted name, as Bootstrap will call textbox.change() to overwrite the text
            // in the textbox.
            setTimeout(function () {
                $('#new_message_content').caret(beginning.length, beginning.length);
            }, 0);
            return beginning + rest;
        },
        stopAdvance: true // Do not advance to the next field on a tab or enter
    });

    $( "#private_message_recipient" ).blur(function (event) {
        var val = $(this).val();
        var recipients = exports.get_cleaned_pm_recipients(val);
        $(this).val(recipients.join(", "));
    });

    typeahead_helper.update_autocomplete();
};

return exports;

}());
<commit_msg>Fix highlighting for stream typeahead in compose box.<commit_after>var composebox_typeahead = (function () {

//************************************
// AN IMPORTANT NOTE ABOUT TYPEAHEADS
//************************************
// They do not do any HTML escaping, at all.
// And your input to them is rendered as though it were HTML by
// the default highlighter.
//
// So if you are not using trusted input, you MUST use the a
// highlighter that escapes (i.e. one that calls
// typeahead_helper.highlight_with_escaping).

var exports = {};

function get_pm_recipients(query_string) {
    // Assumes email addresses don't have commas or semicolons in them
    return query_string.split(/\s*[,;]\s*/);
}

// Returns an array of private message recipients, removing empty elements.
// For example, "a,,b, " => ["a", "b"]
exports.get_cleaned_pm_recipients = function (query_string) {
    var recipients = get_pm_recipients(query_string);
    recipients = $.grep(recipients, function (elem, idx) {
        return elem.match(/\S/);
    });
    return recipients;
};

function get_last_recipient_in_pm(query_string) {
    var recipients = get_pm_recipients(query_string);
    return recipients[recipients.length-1];
}

function composebox_typeahead_highlighter(item) {
    var query = this.query;
    if ($(this.$element).attr('id') === 'private_message_recipient') {
        // There could be multiple recipients in a private message,
        // we want to decide what to highlight based only on the most
        // recent one we're entering.
        query = get_last_recipient_in_pm(this.query);
    }
    return typeahead_helper.highlight_with_escaping(query, item);
}

// nextFocus is set on a keydown event to indicate where we should focus on keyup.
// We can't focus at the time of keydown because we need to wait for typeahead.
// And we can't compute where to focus at the time of keyup because only the keydown
// has reliable information about whether it was a tab or a shift+tab.
var nextFocus = false;

function handle_keydown(e) {
    var code = e.keyCode || e.which;

    if (code === 13 || (code === 9 && !e.shiftKey)) { // Enter key or tab key
        if (e.target.id === "stream" || e.target.id === "subject" || e.target.id === "private_message_recipient") {
            // For enter, prevent the form from submitting
            // For tab, prevent the focus from changing again
            e.preventDefault();
        }

        // In the new_message_content box, preventDefault() for tab but not for enter
        if (e.target.id === "new_message_content" && code !== 13) {
            e.preventDefault();
        }

        if (e.target.id === "stream") {
            nextFocus = "subject";
        } else if (e.target.id === "subject") {
            if (code === 13) e.preventDefault();
            nextFocus = "new_message_content";
        } else if (e.target.id === "private_message_recipient") {
            nextFocus = "new_message_content";
        } else if (e.target.id === "new_message_content") {
            if (code === 13) {
                nextFocus = false;
            } else {
                nextFocus = "compose-send-button";
            }
        } else {
            nextFocus = false;
        }

        // If no typeaheads are shown...
        if (!($("#subject").data().typeahead.shown ||
              $("#stream").data().typeahead.shown ||
              $("#private_message_recipient").data().typeahead.shown ||
              $("#new_message_content").data().typeahead.shown)) {

            // If no typeaheads are shown and the user is tabbing from the message content box,
            // then there's no need to wait and we can change the focus right away.
            // Without this code to change the focus right away, if the user presses enter
            // before they fully release the tab key, the tab will be lost.  Note that we don't
            // want to change focus right away in the private_message_recipient box since it
            // takes the typeaheads a little time to open after the user finishes typing, which
            // can lead to the focus moving without the autocomplete having a chance to happen.
            if ((page_params.domain === "humbughq.com" && nextFocus === "compose-send-button") ||
                (page_params.domain !== "humbughq.com" && nextFocus)) {
                ui.focus_on(nextFocus);
                nextFocus = false;
            }

            // If no typeaheads are shown and the user has configured enter to send,
            // then make enter send instead of inserting a line break.
            // (Unless shift is being held down, which we *do* want to insert a linebreak)
            if (e.target.id === "new_message_content"
                && code === 13 && !e.shiftKey
                && page_params.enter_sends) {
                e.preventDefault();
                if ($("#compose-send-button").attr('disabled') !== "disabled") {
                    $("#compose-send-button").attr('disabled', 'disabled');
                    compose.finish();
                }
            }
        }

        return false;
    }
}

function handle_keyup(e) {
    var code = e.keyCode || e.which;
    if (code === 13 || (code === 9 && !e.shiftKey)) { // Enter key or tab key
        if (nextFocus) {
            ui.focus_on(nextFocus);
            nextFocus = false;
        }
    }
}

// http://stackoverflow.com/questions/3380458/looking-for-a-better-workaround-to-chrome-select-on-focus-bug
function select_on_focus(field_id) {
    $("#" + field_id).focus(function(e) {
        $("#" + field_id).select().one('mouseup', function (e) {
            e.preventDefault();
        });
    });
}

exports.split_at_cursor = function(query) {
    var cursor = $('#new_message_content').caret().start;
    return [query.slice(0, cursor), query.slice(cursor)];
};

exports.initialize = function () {
    select_on_focus("stream");
    select_on_focus("subject");
    select_on_focus("private_message_recipient");

    // These handlers are at the "form" level so that they are called after typeahead
    $("form#send_message_form").keydown(function(e) {
        handle_keydown(e);
    });
    $("form#send_message_form").keyup(function(e) {
        handle_keyup(e);
    });

    $("#enter_sends").click(function () {
        var send_button = $("#compose-send-button");
        page_params.enter_sends = $("#enter_sends").is(":checked");
        if (page_params.enter_sends) {
            send_button.fadeOut();
        } else {
            send_button.fadeIn();
        }
        return $.ajax({
            dataType: 'json',
            url: '/json/change_enter_sends',
            type: 'POST',
            data: {'enter_sends': page_params.enter_sends}
        });
    });
    $("#enter_sends").prop('checked', page_params.enter_sends);
    if (page_params.enter_sends) $("#compose-send-button").hide();

    // limit number of items so the list doesn't fall off the screen
    $( "#stream" ).typeahead({
        source: function (query, process) {
            return subs.subscribed_streams();
        },
        items: 3,
        highlighter: function (item) {
            var query = this.query;
            return typeahead_helper.highlight_query_in_phrase(query, item);
        },
        matcher: function (item) {
            // The matcher for "stream" is strictly prefix-based,
            // because we want to avoid mixing up streams.
            var q = this.query.trim().toLowerCase();
            return (item.toLowerCase().indexOf(q) === 0);
        }
    });

    $( "#subject" ).typeahead({
        source: function (query, process) {
            var stream_name = $("#stream").val();
            if (subject_dict.hasOwnProperty(stream_name)) {
                return subject_dict[stream_name];
            }
            return [];
        },
        items: 2,
        highlighter: composebox_typeahead_highlighter,
        sorter: typeahead_helper.sort_subjects
    });

    $( "#private_message_recipient" ).typeahead({
        source: typeahead_helper.private_message_typeahead_list,
        items: 2,
        highlighter: composebox_typeahead_highlighter,
        matcher: function (item) {
            var current_recipient = get_last_recipient_in_pm(this.query);
            // If the name is only whitespace (does not contain any non-whitespace),
            // we're between typing names; don't autocomplete anything for us.
            if (! current_recipient.match(/\S/)) {
                return false;
            }

            // Case-insensitive.
            return (item.toLowerCase().indexOf(current_recipient.toLowerCase()) !== -1);
        },
        sorter: typeahead_helper.sort_recipientbox_typeahead,
        updater: function (item) {
            var previous_recipients = exports.get_cleaned_pm_recipients(this.query);
            previous_recipients.pop();
            previous_recipients = previous_recipients.join(", ");
            if (previous_recipients.length !== 0) {
                previous_recipients += ", ";
            }
            return previous_recipients + typeahead_helper.private_message_mapped[item].email + ", ";
        },
        stopAdvance: true // Do not advance to the next field on a tab or enter
    });

    $( "#new_message_content" ).typeahead({
        source: typeahead_helper.private_message_typeahead_list,
        items: 2,
        highlighter: composebox_typeahead_highlighter,
        matcher: function (item) {
            var query = exports.split_at_cursor(this.query)[0];

            var strings = query.split(/[\s*(){}\[\]]/);
            if (strings.length < 1) {
                return false;
            }
            var current_recipient = strings[strings.length-1];
            if (current_recipient.length < 2 || current_recipient.charAt(0) !== "@") {
                return false;
            }
            current_recipient = current_recipient.substring(1);

            // Case-insensitive.
            return (item.toLowerCase().indexOf(current_recipient.toLowerCase()) !== -1);
        },
        sorter: typeahead_helper.sort_textbox_typeahead,
        updater: function (item) {
            var pieces = exports.split_at_cursor(this.query);
            var beginning = pieces[0];
            var rest = pieces[1];

            beginning = beginning.replace(/@\S+$/, "") + "@**" + typeahead_helper.private_message_mapped[item].full_name + "**";
            // Keep the cursor after the newly inserted name, as Bootstrap will call textbox.change() to overwrite the text
            // in the textbox.
            setTimeout(function () {
                $('#new_message_content').caret(beginning.length, beginning.length);
            }, 0);
            return beginning + rest;
        },
        stopAdvance: true // Do not advance to the next field on a tab or enter
    });

    $( "#private_message_recipient" ).blur(function (event) {
        var val = $(this).val();
        var recipients = exports.get_cleaned_pm_recipients(val);
        $(this).val(recipients.join(", "));
    });

    typeahead_helper.update_autocomplete();
};

return exports;

}());
<|endoftext|>


<commit_before>/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*/
window.addEventListener("load", function(e) {
  startup();
}, false);

function startup() {
  var myPanel = document.getElementById("my-panel");
  myPanel.label = "Tor Enabled";
  myPanel.style.color = "red";
}

function onClickHandler(event) {
    switch(event.which) {
        case 1:
            alert("Go to Tools > Add-ons to disable Torbutton for Thunderbird.");
            break;
    }
}
<commit_msg>A better message for Tor Enabled button<commit_after>/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*/
window.addEventListener("load", function(e) {
  startup();
}, false);

function startup() {
  var myPanel = document.getElementById("my-panel");
  myPanel.label = "Tor Enabled";
  myPanel.style.color = "red";
}

function onClickHandler(event) {
  switch(event.which) {
    case 1:
      alert("Torbutton for Thunderbird is currently enabled and is helping protect your anonymity.\n\nTo disable Torbutton, go to Tools > Add-ons.");
      break;
}
}
<|endoftext|>


<commit_before>/* eslint no-var: 0 */
/* eslint babel/object-shorthand: 0 */

// Karma configuration
require('coffee-script/register');
var webpackConfig = require('./webpack/test.coffee');
const ibrik = require('ibrik');
delete webpackConfig.entry;

module.exports = function (config) {
  config.set({

    plugins: [
      // fraworks
      'karma-mocha',
      'karma-chai',
      'karma-sinon-chai',
      'karma-phantomjs-shim',

      // environment
      'karma-webpack',

      // launchers
      'karma-chrome-launcher',
      'karma-phantomjs-launcher',

      // reporters
      'karma-notify-reporter',
      'karma-nyan-reporter',
      'karma-coverage',
    ],

    basePath: '',

    frameworks: ['mocha', 'chai', 'sinon-chai', 'phantomjs-shim'],

    // list of files / patterns to load in the browser
    files: [
      {pattern: 'app/scripts/**/__test__/*.js'},
      {pattern: 'app/scripts/**/*.js', included: false},
    ],

    exclude: [
      'app/scripts/main.js',
    ],

    preprocessors: {
      '**/__test__/*.js': ['webpack'],
    },

    client: {
      mocha: {
        reporter: 'html',
      },
    },

    reporters: ['notify', 'nyan'],
    // reporters: ['notify', 'nyan', 'coverage'],

    coverageReporter: {
      instrumenters: {ibrik: ibrik},
      type: 'text',
      includeAllSources: true,
      instrumenter: {
        '**/*.coffee': 'ibrik',
      },
    },

    notifyReporter: {
      reportEachFailure: true,
      reportSuccess: false,
    },

    webpack: webpackConfig,

    webpackMiddleware: {
      stats: {
        colors: true,
      },
    },

    port: 9876,

    colors: true,

    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_ERROR,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,

    browsers: ['PhantomJS'],

    singleRun: false,
  });
};
<commit_msg>Add babel-polyfill to files list in karma.conf<commit_after>/* eslint no-var: 0 */
/* eslint babel/object-shorthand: 0 */

// Karma configuration
require('coffee-script/register');
var webpackConfig = require('./webpack/test.coffee');
const ibrik = require('ibrik');
delete webpackConfig.entry;

module.exports = function (config) {
  config.set({

    plugins: [
      // fraworks
      'karma-mocha',
      'karma-chai',
      'karma-sinon-chai',
      'karma-phantomjs-shim',

      // environment
      'karma-webpack',

      // launchers
      'karma-chrome-launcher',
      'karma-phantomjs-launcher',

      // reporters
      'karma-notify-reporter',
      'karma-nyan-reporter',
      'karma-coverage',
    ],

    basePath: '',

    frameworks: ['mocha', 'chai', 'sinon-chai', 'phantomjs-shim'],

    // list of files / patterns to load in the browser
    files: [
      'node_modules/babel-polyfill/dist/polyfill.js',
      {pattern: 'app/scripts/**/__test__/*.js'},
      {pattern: 'app/scripts/**/*.js', included: false},
    ],

    exclude: [
      'app/scripts/main.js',
    ],

    preprocessors: {
      '**/__test__/*.js': ['webpack'],
    },

    client: {
      mocha: {
        reporter: 'html',
      },
    },

    reporters: ['notify', 'nyan'],
    // reporters: ['notify', 'nyan', 'coverage'],

    coverageReporter: {
      instrumenters: {ibrik: ibrik},
      type: 'text',
      includeAllSources: true,
      instrumenter: {
        '**/*.coffee': 'ibrik',
      },
    },

    notifyReporter: {
      reportEachFailure: true,
      reportSuccess: false,
    },

    webpack: webpackConfig,

    webpackMiddleware: {
      stats: {
        colors: true,
      },
    },

    port: 9876,

    colors: true,

    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_ERROR,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,

    browsers: ['PhantomJS'],

    singleRun: false,
  });
};
<|endoftext|>


<commit_before>import React,{Component} from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';

class Viewer extends Component {

    constructor(props){
        super(props);
        this.state = {};
        this.renderViewers = this.renderViewers.bind(this);
    }

    renderViewers(data,index){
        return (

{data.name}

        )
    }

    render() {
        return (

                    {this.props.tabs.map(this.renderViewers)}

        );
    }
}

function mapStateToProps({tabs}){
    return {tabs};
}

function mapDispatchToProps(dispatch){
    return bindActionCreators({},dispatch);
}

export default connect (mapStateToProps,mapDispatchToProps)(Viewer);
<commit_msg>change component behavior<commit_after>import React,{Component} from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';

class Viewer extends Component {

    constructor(props){
        super(props);
        this.state = {};
    }

    render() {
        return (

                    json!

        );
    }
}

function mapStateToProps({tabs}){
    return {tabs};
}

function mapDispatchToProps(dispatch){
    return bindActionCreators({},dispatch);
}

export default connect (mapStateToProps,mapDispatchToProps)(Viewer);
<|endoftext|>


<commit_before>set_global('$', global.make_zjquery());
set_global('i18n', global.stub_i18n);

set_global('narrow_state', {});
set_global('unread', {});
set_global('muting', {});
set_global('stream_popover', {});
set_global('message_list', {});

zrequire('hash_util');
zrequire('stream_data');
zrequire('unread');
zrequire('topic_data');
zrequire('topic_list');

const devel = {
    stream_id: 555,
    name: 'devel',
};

stream_data.add_sub('devel', devel);

run_test('topic_list_build_widget', () => {

    topic_data.reset();
    topic_data.add_message({
        stream_id: devel.stream_id,
        topic_name: 'coding',
        message_id: 400,
    });

    stream_popover.hide_topic_popover = function () {};

    narrow_state.topic = function () {
        return 'testing';
    };

    unread.num_unread_for_topic = function () {
        return 3;
    };

    let checked_mutes;
    let rendered;

    global.stub_templates(function (name, info) {
        assert.equal(name, 'topic_list_item');
        const expected = {
            topic_name: 'coding',
            unread: 3,
            is_zero: false,
            is_muted: false,
            is_active_topic: false,
            url: '#narrow/stream/555-devel/topic/coding',
        };
        assert.deepEqual(info, expected);
        rendered = true;
        return '<topic list item>';
    });

    muting.is_topic_muted = function (stream_id, topic_name) {
        assert.equal(stream_id, devel.stream_id);
        assert.equal(topic_name, 'coding');
        checked_mutes = true;
        return false;
    };

    const ul = $('
');

    const list_items = [];

    ul.append = function (item) {
        list_items.push(item);
    };

    const parent_elem = $.create('parent_elem');
    let attached_to_parent;

    parent_elem.append = function (child) {
        assert.equal(child, ul);
        attached_to_parent = true;
    };

    assert.equal(topic_list.active_stream_id(), undefined);

    const widget = topic_list.widget(parent_elem, devel.stream_id);

    widget.build_more_topics_section = function () {
        return $('<more topics>');
    };

    widget.build();

    assert(widget.get_stream_id(), devel.stream_id);
    assert.equal(widget.get_parent(), parent_elem);

    assert(checked_mutes);
    assert(rendered);
    assert.equal(list_items[0].html(), '<topic list item>');
    assert.equal(list_items[1].html(), '<more topics>');
    assert(attached_to_parent);

});
<commit_msg>tests: Add coverage for topic_list.get_list_info().<commit_after>set_global('$', global.make_zjquery());
set_global('i18n', global.stub_i18n);

set_global('narrow_state', {});
set_global('unread', {});
set_global('muting', {});
set_global('stream_popover', {});
set_global('message_list', {});

zrequire('hash_util');
zrequire('stream_data');
zrequire('unread');
zrequire('topic_data');
zrequire('topic_list');

const devel = {
    stream_id: 555,
    name: 'devel',
};

const general = {
    stream_id: 556,
    name: 'general',
};

stream_data.add_sub('devel', devel);
stream_data.add_sub('general', general);

function clear() {
    narrow_state.topic = () => undefined;
    topic_data.reset();
    stream_popover.hide_topic_popover = function () {};
    muting.is_topic_muted = () => false;
}

function get_list_info() {
    const stream_id = general.stream_id;
    const widget = topic_list.widget(undefined, stream_id);
    return widget.get_list_info();
}

run_test('get_list_info w/real topic_data', () => {
    clear();

    const empty_list_info = get_list_info();

    assert.deepEqual(empty_list_info, {
        items: [],
        more_topics_unreads: 0,
        num_possible_topics: 0,
    });

    _.each(_.range(7), (i) => {
        const topic_name = 'topic ' + i;
        topic_data.add_message({
            stream_id: general.stream_id,
            topic_name: topic_name,
            message_id: 1000 + i,
        });
    });

    const list_info = get_list_info();
    assert.equal(list_info.items.length, 5);
    assert.equal(list_info.more_topics_unreads, 0);
    assert.equal(list_info.num_possible_topics, 7);

    assert.deepEqual(list_info.items[0], {
        is_active_topic: false,
        is_muted: false,
        is_zero: true,
        topic_name: 'topic 6',
        unread: 0,
        url: '#narrow/stream/556-general/topic/topic.206',
    });
});

run_test('get_list_info unreads', () => {
    clear();

    let list_info;

    // Going forward, we just stub get_recent_names
    // for simpler test setup.
    topic_data.get_recent_names = () => {
        return _.map(_.range(15), (i) => {
            return 'topic ' + i;
        });
    };

    const unread_cnt = {};
    unread.num_unread_for_topic = (stream_id, topic_name) => {
        assert.equal(stream_id, general.stream_id);
        return unread_cnt[topic_name] || 0;
    };

    /*
        We have 15 topics, but we only show up
        to 8 topics, depending on how many have
        unread counts.  We only show a max of 5
        fully-read topics.

        So first we'll get 7 topics, where 2 are
        unread.
    */
    unread_cnt['topic 8'] = 8;
    unread_cnt['topic 9'] = 9;

    list_info = get_list_info();
    assert.equal(list_info.items.length, 7);
    assert.equal(list_info.more_topics_unreads, 0);
    assert.equal(list_info.num_possible_topics, 15);

    assert.deepEqual(
        _.map(list_info.items, (li) => li.topic_name),
        [
            'topic 0',
            'topic 1',
            'topic 2',
            'topic 3',
            'topic 4',
            'topic 8',
            'topic 9',
        ]);

    unread_cnt['topic 6'] = 6;
    unread_cnt['topic 7'] = 7;

    list_info = get_list_info();
    assert.equal(list_info.items.length, 8);
    assert.equal(list_info.more_topics_unreads, 9);
    assert.equal(list_info.num_possible_topics, 15);

    assert.deepEqual(
        _.map(list_info.items, (li) => li.topic_name),
        [
            'topic 0',
            'topic 1',
            'topic 2',
            'topic 3',
            'topic 4',
            'topic 6',
            'topic 7',
            'topic 8',
        ]);
});

run_test('topic_list_build_widget', () => {
    clear();

    topic_data.get_recent_names = () => {
        return ['coding'];
    };

    narrow_state.topic = function () {
        return 'testing';
    };

    unread.num_unread_for_topic = function () {
        return 3;
    };

    let checked_mutes;
    let rendered;

    global.stub_templates(function (name, info) {
        assert.equal(name, 'topic_list_item');
        const expected = {
            topic_name: 'coding',
            unread: 3,
            is_zero: false,
            is_muted: false,
            is_active_topic: false,
            url: '#narrow/stream/555-devel/topic/coding',
        };
        assert.deepEqual(info, expected);
        rendered = true;
        return '<topic list item>';
    });

    muting.is_topic_muted = function (stream_id, topic_name) {
        assert.equal(stream_id, devel.stream_id);
        assert.equal(topic_name, 'coding');
        checked_mutes = true;
        return false;
    };

    const ul = $('
');

    const list_items = [];

    ul.append = function (item) {
        list_items.push(item);
    };

    const parent_elem = $.create('parent_elem');
    let attached_to_parent;

    parent_elem.append = function (child) {
        assert.equal(child, ul);
        attached_to_parent = true;
    };

    assert.equal(topic_list.active_stream_id(), undefined);

    const widget = topic_list.widget(parent_elem, devel.stream_id);

    widget.build_more_topics_section = function () {
        return $('<more topics>');
    };

    widget.build();

    assert(widget.get_stream_id(), devel.stream_id);
    assert.equal(widget.get_parent(), parent_elem);

    assert(checked_mutes);
    assert(rendered);
    assert.equal(list_items[0].html(), '<topic list item>');
    assert.equal(list_items[1].html(), '<more topics>');
    assert(attached_to_parent);

});
<|endoftext|>


<commit_before>const {dirname} = require("path")

const fs = require("promise-fs")

const yaml = require("js-yaml")

const isNumber = require("./util/isNumber")
const isPlainObject = require("./util/isPlainObject")

const {normalizePath, normalizePathSync} = require("./util/normalizePath")

let base = process.cwd()
if (module.parent && typeof module.parent.filename === "string") {
  base = dirname(module.parent.filename)

  // eslint-disable-next-line no-underscore-dangle
  delete require.cache[__filename]
}

const DEFAULT_OPTIONS = {
  schema: yaml.DEFAULT_SAFE_SCHEMA,
  encoding: "utf8"
}

/**
 * @api private
 */
function normalizeOptions(options) {
  switch (true) {
    case isPlainObject(options):
      return {
        ...DEFAULT_OPTIONS, ...options
      }

    case options && typeof options === "string":
      return {
        ...DEFAULT_OPTIONS, encoding: options
      }

    default:
      return DEFAULT_OPTIONS
  }
}

/**
 * @api public
 */
async function read(path, options = {}) {
  const {encoding, ...opts} = normalizeOptions(options)

  if (!isNumber(path)) {
    path = await normalizePath(base, path)
  }

  const content = await fs.readFile(path, encoding)

  return yaml.load(content, opts)
}

/**
 * @api public
 */
function readSync(path, options = {}) {
  const {encoding, ...opts} = normalizeOptions(options)

  if (!isNumber(path)) {
    path = normalizePathSync(base, path)
  }

  const content = fs.readFileSync(path, encoding)

  return yaml.load(content, opts)
}

/**
 * @api public
 */
async function write(path, content, options = {}) {
  const {encoding, ...opts} = normalizeOptions(options)

  if (!isNumber(path)) {
    path = await normalizePath(base, path)
  }

  await fs.writeFile(path, yaml.dump(content, opts), encoding)
}

/**
 * @api public
 */
function writeSync(path, content, options = {}) {
  const {encoding, ...opts} = normalizeOptions(options)

  if (!isNumber(path)) {
    path = normalizePathSync(base, path)
  }

  fs.writeFileSync(path, yaml.dump(content, opts), encoding)
}

exports.read = read
exports.readSync = readSync
exports.write = write
exports.writeSync = writeSync
<commit_msg>Small improvement<commit_after>const {dirname} = require("path")

const fs = require("promise-fs")

const yaml = require("js-yaml")

const isNumber = require("./util/isNumber")
const isPlainObject = require("./util/isPlainObject")

const {normalizePath, normalizePathSync} = require("./util/normalizePath")

let base = process.cwd()
if (module.parent && typeof module.parent.filename === "string") {
  base = dirname(module.parent.filename)

  // eslint-disable-next-line no-underscore-dangle
  delete require.cache[__filename]
}

const defaults = {
  schema: yaml.DEFAULT_SAFE_SCHEMA,
  encoding: "utf8"
}

/**
 * @api private
 */
function normalizeOptions(options) {
  switch (true) {
    case isPlainObject(options):
      return {
        ...defaults, ...options
      }

    case options && typeof options === "string":
      return {
        ...defaults, encoding: options
      }

    default:
      return defaults
  }
}

/**
 * @api public
 */
async function read(path, options = {}) {
  const {encoding, ...opts} = normalizeOptions(options)

  if (!isNumber(path)) {
    path = await normalizePath(base, path)
  }

  const content = await fs.readFile(path, encoding)

  return yaml.load(content, opts)
}

/**
 * @api public
 */
function readSync(path, options = {}) {
  const {encoding, ...opts} = normalizeOptions(options)

  if (!isNumber(path)) {
    path = normalizePathSync(base, path)
  }

  const content = fs.readFileSync(path, encoding)

  return yaml.load(content, opts)
}

/**
 * @api public
 */
async function write(path, content, options = {}) {
  const {encoding, ...opts} = normalizeOptions(options)

  if (!isNumber(path)) {
    path = await normalizePath(base, path)
  }

  await fs.writeFile(path, yaml.dump(content, opts), encoding)
}

/**
 * @api public
 */
function writeSync(path, content, options = {}) {
  const {encoding, ...opts} = normalizeOptions(options)

  if (!isNumber(path)) {
    path = normalizePathSync(base, path)
  }

  fs.writeFileSync(path, yaml.dump(content, opts), encoding)
}

exports.read = read
exports.readSync = readSync
exports.write = write
exports.writeSync = writeSync
<|endoftext|>


<commit_before>// Generics
var ChopChop = (function($, ChopChop) {
    var api = ChopChop.api = {},
        plugins = ChopChop.plugins = {};

    // Exception
    var Exception = ChopChop.Exception = function(message) {
        this.message = message;
    };

    Exception.prototype = {
        toString: function() {
            return 'EXCEPTION: ' + this.message;
        }
    };

    // Initialisation
    var defaultInitOptions = {
        'toggle': {},
        'collapse': {},
        'priorityNav': {}
    };

    ChopChop.init = api.init = function(options) {
        options = options || defaultInitOptions;

        for (var plugin in options) {
            if (!options.hasOwnProperty(plugin)) {
                continue;
            }

            var name = plugin.charAt(0).toUpperCase() + plugin.substr(1);

            if (!plugins[plugin] && ChopChop[name]) {
                plugins[plugin] = new ChopChop[name](options);
            }
        }
    };

    // jQuery goodness
    $.fn.chopchop = function() {
        var args = Array.prototype.slice.call(arguments),
            numArgs = args.length;

        if (numArgs === 0) {
            return api;
        }

        var method = args.shift();

        if (api.hasOwnProperty(method)) {
            return this.each(function() {
                var params = args.slice(0);
                params.unshift(this);
                api[method].apply(api, params);
            });
        }
    };

    return ChopChop;
})(jQuery, ChopChop || {});

// Utilities
var ChopChop = (function($, ChopChop) {
    ChopChop.util = {
        debounce: function(func, wait, immediate) {
            var timeout;

            return function() {
                var context = this,
                    args = arguments;

                var later = function() {
                    timeout = null;

                    if (!immediate) {
                        func.apply(context, args);
                    }
                };

                var callNow = immediate && !timeout;
                clearTimeout(timeout);
                timeout = setTimeout(later, wait);

                if (callNow) {
                    func.apply(context, args);
                }
            };
        }
    };

    return ChopChop;
})(jQuery, ChopChop || {});

// Toggles
var ChopChop = (function($, ChopChop) {
    var defaultOptions = {
        classActive: 'is-active',
        classInactive: 'is-inactive'
    };

    // Toggle
    var Toggle = ChopChop.Toggle = function(options) {
        this.options = $.extend({}, defaultOptions, options);
        this.pending = [];
        this.processed = [];
        this.init();
    };

    var Action = {
        TOGGLE: 'toggle',
        ACTIVATE: 'activate',
        DEACTIVATE: 'deactivate'
    };

    var Trigger = {
        DIRECT_ONLY: 'direct-only',
        INDIRECT_ONLY: 'indirect-only',     // Why would you want this? Oh well.
        BOTH: 'both'    // Default
    };

    Toggle.prototype = {
        init: function() {
            var self = this;

            $(document).on('click', '[data-cc-action]', function(e) {
                var $this = $(this), t,
                    $target = (t = $this.data('cc-target')) ? $('#' + t) : false,
                    mode = $this.data('cc-trigger-on') || Trigger.BOTH;

                if (!$target) {
                    return;
                }

                if (mode === Trigger.DIRECT_ONLY && e.target !== this) {
                    return;
                } else if (mode === Trigger.INDIRECT_ONLY && e.target === this) {
                    return;
                }

                e.preventDefault();
                self.performAction($target, $this.data('cc-action') || Action.TOGGLE);
            });
        },
        toggle: function(target, action) {
            var $target = typeof target === 'string' ? $('#' + target) : $(target);

            if (!$target.size()) {
                return;
            }

            this.performAction($target, action || Action.TOGGLE);
        },
        cascade: function($el, action) {
            var i, l, $other,
                chain = ($el.data('cc-cascade-' + action) || '').split(',')
                        .concat(($el.data('cc-cascade') || '').split(','));

            for (i = 0, l = chain.length; i < l; ++i) {
                $other = $('#' + chain[i]);

                if (this.processed.indexOf($other[0]) !== -1 || this.pending.indexOf($other[0]) !== -1) {
                    continue;
                }

                this.pending.push({
                    action: action,
                    target: $other
                });
            }
        },
        performAction: function($el, action) {
            this.pending.length = 0;
            this.processed.length = 0;

            if (action === Action.TOGGLE) {
                if ($el.hasClass('is-active')) {
                    action = Action.DEACTIVATE;
                } else {
                    action = Action.ACTIVATE;
                }
            }

            this.pending.push({
                action: action,
                target:$el
            });

            var id, dependants, group, $current, item,
                iterations = 0,
                el, chain, $other, i;

            while (this.pending.length > 0) {
                item = this.pending.shift();
                action = item.action;
                $current = item.target;

                // Skip already processed nodes
                if (this.processed.indexOf($current) !== -1) {
                    continue;
                }

                id = $current.attr('id');
                group = $current.data('cc-group');

                // Process group members, if a group is present
                for (i = 0, all = $('[data-cc-group="' + group + '"]'), l = all.length; i < l; ++i) {
                    el = all[i];
                    $other = $(el);

                    // Skip element itself
                    if (el === $current[0]) {
                        continue;
                    }

                    // Just a group member, not the target: deactivate
                    $other.addClass('is-inactive').removeClass('is-active');
                    this.cascade($other, Action.DEACTIVATE);
                }

                // Process target
                if (action === Action.ACTIVATE) {
                    $current.addClass('is-active').removeClass('is-inactive');
                    $current.trigger('chopchop:activate');
                } else {
                    $current.addClass('is-inactive').removeClass('is-active');
                    $current.trigger('chopchop:deactivate');
                }

                // Chain onto targets
                this.cascade($current, action);
                this.processed.push($current[0]);
                iterations += 1;

                if (iterations > 100) {
                    throw new ChopChop.Exception("Too many iterations, aborting");
                }
            }
        }
    };

    // Expose toggle api
    ChopChop.api.toggle = function() {
        return ChopChop.plugins.toggle.toggle.apply(ChopChop.plugins.toggle, arguments);
    };

    return ChopChop;
})(jQuery, ChopChop || {});

// Collapse
var ChopChop = (function($, ChopChop) {
    // Tabordion types
    var Types = {
        ONE_OPEN: 'one-open',
        ONE_COLLAPSIBLE: 'one-collapsible',
        ALL_COLLAPSIBLE: 'all-collapsible'
    };

    var defaultOptions = {
        classActive: 'is-active',
        classInactive: 'is-inactive',
        headerSelector: '> [class*="__header"]',
        bodySelector: '> [class*="__body"]',
        type: Types.ONE_OPEN
    };

    // Collapse
    var Collapse = ChopChop.Collapse = function(options) {
        this.options = $.extend({}, defaultOptions, options);
        this.id = 0;
        this.init();
    };

    // Attribute creators and appliers for all collapse types
    var attributors = {};
    attributors[Types.ONE_OPEN] = function($header, $body, name, i) {
        $header.attr({
            'id': name + '-header-' + i,
            'data-cc-group': name + '-headers',
            'data-cc-action': 'activate',
            'data-cc-target': name + '-body-' + i
        });
        $body.attr({
            'id': name + '-body-' + i,
            'data-cc-group': name + '-bodies',
            'data-cc-cascade-activate': name + '-header-' + i
        });
    };
    attributors[Types.ONE_COLLAPSIBLE] = function($header, $body, name, i) {
        $header.attr({
            'id': name + '-header-' + i,
            'data-cc-group': name + '-headers',
            'data-cc-action': 'toggle',
            'data-cc-target': name + '-body-' + i
        });
        $body.attr({
            'id': name + '-body-' + i,
            'data-cc-group': name + '-bodies',
            'data-cc-cascade': name + '-header-' + i
        });
    };
    attributors[Types.ALL_COLLAPSIBLE] = function($header, $body, name, i) {
        $header.attr({
            'id': name + '-header-' + i,
            'data-cc-action': 'toggle',
            'data-cc-target': name + '-body-' + i
        });
        $body.attr({
            'id': name + '-body-' + i,
            'data-cc-cascade': name + '-header-' + i
        });
    };

    Collapse.prototype = {
        init: function() {
            var self = this;

            $('[data-cc-collapse]').each(function() {
                self.applyToContainer(this);
            });
        },
        getNextName: function() {
            return 'collapse' + (++this.id);
        },
        applyToContainer: function(root, options) {
            options = $.extend({}, this.options, options);

            var $root = $(root),
                name = options.name || $root.data('cc-collapse-name') || this.getNextName(),
                type =  $root.data('cc-collapse') || options.type,
                headers = $root.find(options.headerSelector),
                bodies = $root.find(options.bodySelector);

            if (headers.size() !== bodies.size()) {
                throw new ChopChop.Exception(
                    "Collapse: number of headers does not match number of bodies (" + headers.size() + " vs " + bodies.size()+ ")"
                );
            }

            for (var i = 0, l = headers.size(); i < l; ++i) {
                attributors[type]($(headers[i]), $(bodies[i]), name, i);
            }
        }
    };

    // Expose api
    ChopChop.api.collapse = function() {
        return ChopChop.plugins.collapse.applyToContainer.apply(ChopChop.plugins.collapse, arguments);
    };

    return ChopChop;
})(jQuery, ChopChop || {});
<commit_msg>Allow multiple, comma-separated targets on toggles (#27)<commit_after>// Generics
var ChopChop = (function($, ChopChop) {
    var api = ChopChop.api = {},
        plugins = ChopChop.plugins = {};

    // Exception
    var Exception = ChopChop.Exception = function(message) {
        this.message = message;
    };

    Exception.prototype = {
        toString: function() {
            return 'EXCEPTION: ' + this.message;
        }
    };

    // Initialisation
    var defaultInitOptions = {
        'toggle': {},
        'collapse': {},
        'priorityNav': {}
    };

    ChopChop.init = api.init = function(options) {
        options = options || defaultInitOptions;

        for (var plugin in options) {
            if (!options.hasOwnProperty(plugin)) {
                continue;
            }

            var name = plugin.charAt(0).toUpperCase() + plugin.substr(1);

            if (!plugins[plugin] && ChopChop[name]) {
                plugins[plugin] = new ChopChop[name](options);
            }
        }
    };

    // jQuery goodness
    $.fn.chopchop = function() {
        var args = Array.prototype.slice.call(arguments),
            numArgs = args.length;

        if (numArgs === 0) {
            return api;
        }

        var method = args.shift();

        if (api.hasOwnProperty(method)) {
            return this.each(function() {
                var params = args.slice(0);
                params.unshift(this);
                api[method].apply(api, params);
            });
        }
    };

    return ChopChop;
})(jQuery, ChopChop || {});

// Utilities
var ChopChop = (function($, ChopChop) {
    ChopChop.util = {
        debounce: function(func, wait, immediate) {
            var timeout;

            return function() {
                var context = this,
                    args = arguments;

                var later = function() {
                    timeout = null;

                    if (!immediate) {
                        func.apply(context, args);
                    }
                };

                var callNow = immediate && !timeout;
                clearTimeout(timeout);
                timeout = setTimeout(later, wait);

                if (callNow) {
                    func.apply(context, args);
                }
            };
        }
    };

    return ChopChop;
})(jQuery, ChopChop || {});

// Toggles
var ChopChop = (function($, ChopChop) {
    var defaultOptions = {
        classActive: 'is-active',
        classInactive: 'is-inactive'
    };

    // Toggle
    var Toggle = ChopChop.Toggle = function(options) {
        this.options = $.extend({}, defaultOptions, options);
        this.pending = [];
        this.processed = [];
        this.init();
    };

    var Action = {
        TOGGLE: 'toggle',
        ACTIVATE: 'activate',
        DEACTIVATE: 'deactivate'
    };

    var Trigger = {
        DIRECT_ONLY: 'direct-only',
        INDIRECT_ONLY: 'indirect-only',     // Why would you want this? Oh well.
        BOTH: 'both'    // Default
    };

    Toggle.prototype = {
        init: function() {
            var self = this;

            $(document).on('click', '[data-cc-action]', function(e) {
                var $this = $(this),
                    targets = $this.data('cc-target').split(','),
                    mode = $this.data('cc-trigger-on') || Trigger.BOTH,
                    $target;

                for (var i = 0, l = targets.length; i < l; ++i) {
                    $target = $('#' + targets[i]);

                    if ($target.size() === 0) {
                        continue;
                    }

                    if (mode === Trigger.DIRECT_ONLY && e.target !== this) {
                        continue;
                    } else if (mode === Trigger.INDIRECT_ONLY && e.target === this) {
                        continue;
                    }

                    e.preventDefault();
                    self.performAction($target, $this.data('cc-action') || Action.TOGGLE);
                }
            });
        },
        toggle: function(target, action) {
            var $target = typeof target === 'string' ? $('#' + target) : $(target);

            if (!$target.size()) {
                return;
            }

            this.performAction($target, action || Action.TOGGLE);
        },
        cascade: function($el, action) {
            var i, l, $other,
                chain = ($el.data('cc-cascade-' + action) || '').split(',')
                        .concat(($el.data('cc-cascade') || '').split(','));

            for (i = 0, l = chain.length; i < l; ++i) {
                $other = $('#' + chain[i]);

                if (this.processed.indexOf($other[0]) !== -1 || this.pending.indexOf($other[0]) !== -1) {
                    continue;
                }

                this.pending.push({
                    action: action,
                    target: $other
                });
            }
        },
        performAction: function($el, action) {
            this.pending.length = 0;
            this.processed.length = 0;

            if (action === Action.TOGGLE) {
                if ($el.hasClass('is-active')) {
                    action = Action.DEACTIVATE;
                } else {
                    action = Action.ACTIVATE;
                }
            }

            this.pending.push({
                action: action,
                target:$el
            });

            var id, dependants, group, $current, item,
                iterations = 0,
                el, chain, $other, i;

            while (this.pending.length > 0) {
                item = this.pending.shift();
                action = item.action;
                $current = item.target;

                // Skip already processed nodes
                if (this.processed.indexOf($current) !== -1) {
                    continue;
                }

                id = $current.attr('id');
                group = $current.data('cc-group');

                // Process group members, if a group is present
                for (i = 0, all = $('[data-cc-group="' + group + '"]'), l = all.length; i < l; ++i) {
                    el = all[i];
                    $other = $(el);

                    // Skip element itself
                    if (el === $current[0]) {
                        continue;
                    }

                    // Just a group member, not the target: deactivate
                    $other.addClass('is-inactive').removeClass('is-active');
                    this.cascade($other, Action.DEACTIVATE);
                }

                // Process target
                if (action === Action.ACTIVATE) {
                    $current.addClass('is-active').removeClass('is-inactive');
                    $current.trigger('chopchop:activate');
                } else {
                    $current.addClass('is-inactive').removeClass('is-active');
                    $current.trigger('chopchop:deactivate');
                }

                // Chain onto targets
                this.cascade($current, action);
                this.processed.push($current[0]);
                iterations += 1;

                if (iterations > 100) {
                    throw new ChopChop.Exception("Too many iterations, aborting");
                }
            }
        }
    };

    // Expose toggle api
    ChopChop.api.toggle = function() {
        return ChopChop.plugins.toggle.toggle.apply(ChopChop.plugins.toggle, arguments);
    };

    return ChopChop;
})(jQuery, ChopChop || {});

// Collapse
var ChopChop = (function($, ChopChop) {
    // Tabordion types
    var Types = {
        ONE_OPEN: 'one-open',
        ONE_COLLAPSIBLE: 'one-collapsible',
        ALL_COLLAPSIBLE: 'all-collapsible'
    };

    var defaultOptions = {
        classActive: 'is-active',
        classInactive: 'is-inactive',
        headerSelector: '> [class*="__header"]',
        bodySelector: '> [class*="__body"]',
        type: Types.ONE_OPEN
    };

    // Collapse
    var Collapse = ChopChop.Collapse = function(options) {
        this.options = $.extend({}, defaultOptions, options);
        this.id = 0;
        this.init();
    };

    // Attribute creators and appliers for all collapse types
    var attributors = {};
    attributors[Types.ONE_OPEN] = function($header, $body, name, i) {
        $header.attr({
            'id': name + '-header-' + i,
            'data-cc-group': name + '-headers',
            'data-cc-action': 'activate',
            'data-cc-target': name + '-body-' + i
        });
        $body.attr({
            'id': name + '-body-' + i,
            'data-cc-group': name + '-bodies',
            'data-cc-cascade-activate': name + '-header-' + i
        });
    };
    attributors[Types.ONE_COLLAPSIBLE] = function($header, $body, name, i) {
        $header.attr({
            'id': name + '-header-' + i,
            'data-cc-group': name + '-headers',
            'data-cc-action': 'toggle',
            'data-cc-target': name + '-body-' + i
        });
        $body.attr({
            'id': name + '-body-' + i,
            'data-cc-group': name + '-bodies',
            'data-cc-cascade': name + '-header-' + i
        });
    };
    attributors[Types.ALL_COLLAPSIBLE] = function($header, $body, name, i) {
        $header.attr({
            'id': name + '-header-' + i,
            'data-cc-action': 'toggle',
            'data-cc-target': name + '-body-' + i
        });
        $body.attr({
            'id': name + '-body-' + i,
            'data-cc-cascade': name + '-header-' + i
        });
    };

    Collapse.prototype = {
        init: function() {
            var self = this;

            $('[data-cc-collapse]').each(function() {
                self.applyToContainer(this);
            });
        },
        getNextName: function() {
            return 'collapse' + (++this.id);
        },
        applyToContainer: function(root, options) {
            options = $.extend({}, this.options, options);

            var $root = $(root),
                name = options.name || $root.data('cc-collapse-name') || this.getNextName(),
                type =  $root.data('cc-collapse') || options.type,
                headers = $root.find(options.headerSelector),
                bodies = $root.find(options.bodySelector);

            if (headers.size() !== bodies.size()) {
                throw new ChopChop.Exception(
                    "Collapse: number of headers does not match number of bodies (" + headers.size() + " vs " + bodies.size()+ ")"
                );
            }

            for (var i = 0, l = headers.size(); i < l; ++i) {
                attributors[type]($(headers[i]), $(bodies[i]), name, i);
            }
        }
    };

    // Expose api
    ChopChop.api.collapse = function() {
        return ChopChop.plugins.collapse.applyToContainer.apply(ChopChop.plugins.collapse, arguments);
    };

    return ChopChop;
})(jQuery, ChopChop || {});
<|endoftext|>