<commit_before>
define([
    'flight/lib/component',
    'flight/lib/registry',
    'data',
    'tpl!./list',
    'tpl!./item',
    'util/previews',
    'util/video/scrubber',
    'util/jquery.withinScrollable',
    'util/jquery.ui.draggable.multiselect'
], function(defineComponent, registry, appData, template, vertexTemplate, previews, VideoScrubber) {
    'use strict';

    return defineComponent(List);

    function hasPreview(vertex) {
        return /^(image|video)$/.test(vertex.concept.displayType) ||
            vertex.properties._glyphIcon;
    }

    function List() {

        this.defaultAttrs({
            itemSelector: '.vertex-item',
            infiniteScrolling: false
        });

        this.stateForVertex = function(vertex) {
            var inWorkspace = appData.inWorkspace(vertex);
            return {
                inGraph: inWorkspace,
                inMap: inWorkspace && !!(
                        vertex.properties.geoLocation ||
                        (vertex.location || (vertex.locations && vertex.locations.length)) ||
                        (vertex.properties.latitude && vertex.properties.longitude)
                )
            };
        };

        this.classNameMapForVertices = function(vertices) {
            var self = this,
                classNamesForVertex = {};

            vertices.forEach(function(v) {

                // Check if this vertex is in the graph/map
                var classes = ['gId' + encodeURIComponent(v.id)];
                var vertexState = self.stateForVertex(v);
                if ( vertexState.inGraph ) classes.push('graph-displayed');
                if ( vertexState.inMap ) classes.push('map-displayed');

                if (hasPreview(v)) {
                    classes.push('has_preview');
                }

                classNamesForVertex[v.id] = classes.join(' ');
            });

            return classNamesForVertex;
        };

        this.after('initialize', function() {
            var self = this;

            this.$node
                .addClass('vertex-list')
                .html(template({
                    vertices:this.attr.vertices,
                    infiniteScrolling: this.attr.infiniteScrolling && this.attr.total !== this.attr.vertices.length,
                    classNamesForVertex: this.classNameMapForVertices(this.attr.vertices)
                }));

            this.attachEvents();

            this.loadVisibleResultPreviews = _.debounce(this.loadVisibleResultPreviews.bind(this), 1000);
            this.loadVisibleResultPreviews();

            this.triggerInfiniteScrollRequest = _.debounce(this.triggerInfiniteScrollRequest.bind(this), 1000);
            this.triggerInfiniteScrollRequest();

            this.setupDraggables();

            this.onObjectsSelected(null, { edges:[], vertices:appData.selectedVertices});

            this.on('selectAll', this.onSelectAll);
            this.on('down', this.move);
            this.on('up', this.move);
        });

        this.move = function(e, data) {
            var previousSelected = this.$node.find('.active')[e.type === 'up' ? 'first' : 'last'](),
                moveTo = previousSelected[e.type === 'up' ? 'prev' : 'next']('.vertex-item');

            if (moveTo.length) {

                var selected = [];

                if (data.shiftKey) {
                    selected = selected.concat(appData.selectedVertices);
                    selected.push(appData.vertex(moveTo.data('vertexId')));
                } else {
                    selected.push(appData.vertex(moveTo.data('vertexId')));
                }

                this.trigger(document, 'defocusVertices');
                this.trigger('selectObjects', { vertices:selected });
            }
        };

        this.onSelectAll = function(e) {
            e.stopPropagation();

            var items = this.$node.find('.vertex-item').addClass('active');
            this.selectItems(items);
        };

        this.after('teardown', function() {
            this.$node.off('mouseenter mouseleave');
            this.scrollNode.off('scroll.vertexList');
            this.$node.empty();
        });

        this.attachEvents = function() {
            this.scrollNode = this.$node;
            while (this.scrollNode.length && this.scrollNode.css('overflow') !== 'auto') {
                this.scrollNode = this.scrollNode.parent();
            }
            this.scrollNode.on('scroll.vertexList', this.onResultsScroll.bind(this));

            this.$node.on('mouseenter mouseleave', '.vertex-item', this.onHoverItem.bind(this));

            this.on(document, 'verticesAdded', this.onVerticesUpdated);
            this.on(document, 'verticesUpdated', this.onVerticesUpdated);
            this.on(document, 'verticesDeleted', this.onVerticesDeleted);
            this.on(document, 'objectsSelected', this.onObjectsSelected);
            this.on(document, 'switchWorkspace', this.onWorkspaceClear);
            this.on(document, 'workspaceDeleted', this.onWorkspaceClear);
            this.on(document, 'workspaceLoaded', this.onWorkspaceLoaded);
            this.on('addInfiniteVertices', this.onAddInfiniteVertices);
        };

        this.setupDraggables = function() {
            this.applyDraggable(this.$node.find('a'));
            this.$node.droppable({ accept:'*', tolerance:'pointer' });
        };

        this.onHoverItem = function(evt) {
            if (this.disableHover === 'defocused') {
                return;
            } else if (this.disableHover) {
                this.disableHover = 'defocused';
                return this.trigger(document, 'defocusVertices');
            }

            var id = $(evt.target).closest('.vertex-item').data('vertexId');
            if (evt.type == 'mouseenter' && id) {
                this.trigger(document, 'focusVertices', { vertexIds:[id] });
            } else {
                this.trigger(document, 'defocusVertices');
            }
        };

        this.onResultsScroll = function(e) {
            if (!this.disableHover) {
                this.disableHover = true;
            }

            this.loadVisibleResultPreviews();

            if (this.attr.infiniteScrolling) {
                this.triggerInfiniteScrollRequest();
            }
        };

        this.triggerInfiniteScrollRequest = function() {
            if (!this.attr.infiniteScrolling) return;

            var loadingListElement = this.$node.find('.infinite-loading');

            if (this.scrollNode.length) {
                loadingListElement = loadingListElement.withinScrollable(this.scrollNode);
            }

            if (loadingListElement.length) {
                var data = { conceptType: this.attr.verticesConceptId };
                if (!this.offset) this.offset = this.attr.vertices.length;
                data.paging = {
                    offset: this.offset
                };
                this.trigger('infiniteScrollRequest', data);
            }
        };

        this.onAddInfiniteVertices = function(evt, data) {
            var loading = this.$node.find('.infinite-loading');

            if (data.vertices.length === 0) {
                loading.remove();
                this.attr.infiniteScrolling = false;
            } else {
                this.offset += data.vertices.length;
                var clsMap = this.classNameMapForVertices(data.vertices),
                    added = data.vertices.map(function(vertex) {
                        return vertexTemplate({
                            vertex:vertex,
                            classNamesForVertex: clsMap
                        });
                    });

                var lastItem = loading.prev();

                loading.before(added);

                var total = data.total || this.attr.total || 0;
                if (total === this.$node.find('.vertex-item').length) {
                    loading.remove();
                    this.attr.infiniteScrolling = false;
                } else {
                    this.triggerInfiniteScrollRequest();
                }

                this.loadVisibleResultPreviews();

                this.applyDraggable(this.$node.find('a'));
            }
        };

        this.loadVisibleResultPreviews = function() {
            var self = this;

            this.disableHover = false;
            if ( !this.previewQueue ) {
                this.previewQueue = previews.createQueue('vertexList', { maxConcurrent: 1 });
            }

            var lisVisible = this.$node.find('.nav-list').children('li');
            if (this.scrollNode.length) {
                lisVisible = lisVisible.withinScrollable(this.scrollNode);
            }

            lisVisible.each(function() {
                var li = $(this),
                    vertex = appData.vertex(li.data('vertexId'));

                if (!vertex) return;

                if (hasPreview(vertex) && !li.data('preview-loaded')) {

                        if (li.data('previewloaded')) return;

                        li.data('previewloaded', true).addClass('preview-loading');

                        if (vertex.properties._glyphIcon) {
                            li.removeClass('preview-loading')
                                .data('preview-loaded', true)
                                .find('.preview').html("");
                        } else {
                            previews.generatePreview(vertex, { width: 200 }, function(poster, frames) {
                                li.removeClass('preview-loading')
                                  .data('preview-loaded', true);

                                if(vertex.concept.displayType === 'video') {
                                    VideoScrubber.attachTo(li.find('.preview'), {
                                        posterFrameUrl: poster,
                                        videoPreviewImageUrl: frames
                                    });
                                } else if(vertex.concept.displayType === 'image') {
                                    li.find('.preview').html("");
                                }
                            });
                        }
                    }
            });
        };

        this.applyDraggable = function(el) {
            var self = this;

            el.draggable({
                helper:'clone',
                appendTo: 'body',
                revert: 'invalid',
                revertDuration: 250,
                scroll: false,
                zIndex: 100,
                distance: 10,
                multi: true,
                otherDraggablesClass: 'vertex-dragging',
                start: function(ev, ui) {
                    $(ui.helper).addClass('vertex-dragging');
                },
                selection: function(ev, ui) {
                    self.selectItems(ui.selected);
                }
            });
        };

        this.selectItems = function(items) {
            var vertices = appData.vertices(items.map(function() {
                    return $(this).data('vertexId');
                }).toArray());

            if (vertices.length > 1) {
                vertices.forEach (function (vertex) {
                    vertex.workspace = {
                        selected: true
                    };
                });
            }
            this.trigger(document, 'defocusVertices');
            this.trigger('selectObjects', { vertices:vertices });
        };

        this.onWorkspaceLoaded = function(evt, workspace) {
            this.onVerticesUpdated(evt, workspace.data || {});
        };

        // Track changes to vertices so we display the "Displayed in Graph" icon
        // in search results
        this.toggleItemIcons = function(id, data) {
            this.$node
                .find('li.gId' + encodeURIComponent(id))
                .toggleClass('graph-displayed', data.inGraph)
                .toggleClass('map-displayed', data.inMap);
        };

        // Switching workspaces should clear the icon state and vertices
        this.onWorkspaceClear = function() {
            this.$node.find('li.graph-displayed').removeClass('graph-displayed');
            this.$node.find('li.map-displayed').removeClass('map-displayed');
        };

        this.onVerticesUpdated = function(event, data) {
            var self = this;
            (data.vertices || []).forEach(function(vertex) {
                self.toggleItemIcons(vertex.id, self.stateForVertex(vertex));
                var currentAnchor = self.$node.find('li.gId' + encodeURIComponent(vertex.id)).children('a'),
                    newAnchor = $(vertexTemplate({
                        vertex: vertex,
                        classNamesForVertex: self.classNameMapForVertices([vertex]),
                    })).children('a'),
                    currentHtml = currentAnchor.html();

                if (vertex.properties._glyphIcon) {
                    $('').attr('src', vertex.properties._glyphIcon).appendTo(newAnchor.find('.preview'));
                }

                var newHtml = newAnchor.html();
                if (currentAnchor.length && newHtml !== currentHtml) {
                    currentAnchor.html(newHtml).closest('.vertex-item').toggleClass('has_preview', hasPreview(vertex));
                }
            });
        };

        this.onVerticesDeleted = function(event, data) {
            var self = this;
            (data.vertices || []).forEach(function(vertex) {
                self.toggleItemIcons(vertex.id, { inGraph:false, inMap:false });
            });
        };

        this.onObjectsSelected = function(event, data) {
            this.$node.find('.active').removeClass('active');

            var ids = _.chain(data.vertices)
                .map(function(v) { return '.gId' + v.id; })
                .value().join(',');

            $(ids).addClass('active');
        };
    }
});
<commit_msg>Fix jslint warning<commit_after>
define([
    'flight/lib/component',
    'flight/lib/registry',
    'data',
    'tpl!./list',
    'tpl!./item',
    'util/previews',
    'util/video/scrubber',
    'util/jquery.withinScrollable',
    'util/jquery.ui.draggable.multiselect'
], function(defineComponent, registry, appData, template, vertexTemplate, previews, VideoScrubber) {
    'use strict';

    return defineComponent(List);

    function hasPreview(vertex) {
        return (/^(image|video)$/).test(vertex.concept.displayType) ||
            vertex.properties._glyphIcon;
    }

    function List() {

        this.defaultAttrs({
            itemSelector: '.vertex-item',
            infiniteScrolling: false
        });

        this.stateForVertex = function(vertex) {
            var inWorkspace = appData.inWorkspace(vertex);
            return {
                inGraph: inWorkspace,
                inMap: inWorkspace && !!(
                        vertex.properties.geoLocation ||
                        (vertex.location || (vertex.locations && vertex.locations.length)) ||
                        (vertex.properties.latitude && vertex.properties.longitude)
                )
            };
        };

        this.classNameMapForVertices = function(vertices) {
            var self = this,
                classNamesForVertex = {};

            vertices.forEach(function(v) {

                // Check if this vertex is in the graph/map
                var classes = ['gId' + encodeURIComponent(v.id)];
                var vertexState = self.stateForVertex(v);
                if ( vertexState.inGraph ) classes.push('graph-displayed');
                if ( vertexState.inMap ) classes.push('map-displayed');

                if (hasPreview(v)) {
                    classes.push('has_preview');
                }

                classNamesForVertex[v.id] = classes.join(' ');
            });

            return classNamesForVertex;
        };

        this.after('initialize', function() {
            var self = this;

            this.$node
                .addClass('vertex-list')
                .html(template({
                    vertices:this.attr.vertices,
                    infiniteScrolling: this.attr.infiniteScrolling && this.attr.total !== this.attr.vertices.length,
                    classNamesForVertex: this.classNameMapForVertices(this.attr.vertices)
                }));

            this.attachEvents();

            this.loadVisibleResultPreviews = _.debounce(this.loadVisibleResultPreviews.bind(this), 1000);
            this.loadVisibleResultPreviews();

            this.triggerInfiniteScrollRequest = _.debounce(this.triggerInfiniteScrollRequest.bind(this), 1000);
            this.triggerInfiniteScrollRequest();

            this.setupDraggables();

            this.onObjectsSelected(null, { edges:[], vertices:appData.selectedVertices});

            this.on('selectAll', this.onSelectAll);
            this.on('down', this.move);
            this.on('up', this.move);
        });

        this.move = function(e, data) {
            var previousSelected = this.$node.find('.active')[e.type === 'up' ? 'first' : 'last'](),
                moveTo = previousSelected[e.type === 'up' ? 'prev' : 'next']('.vertex-item');

            if (moveTo.length) {

                var selected = [];

                if (data.shiftKey) {
                    selected = selected.concat(appData.selectedVertices);
                    selected.push(appData.vertex(moveTo.data('vertexId')));
                } else {
                    selected.push(appData.vertex(moveTo.data('vertexId')));
                }

                this.trigger(document, 'defocusVertices');
                this.trigger('selectObjects', { vertices:selected });
            }
        };

        this.onSelectAll = function(e) {
            e.stopPropagation();

            var items = this.$node.find('.vertex-item').addClass('active');
            this.selectItems(items);
        };

        this.after('teardown', function() {
            this.$node.off('mouseenter mouseleave');
            this.scrollNode.off('scroll.vertexList');
            this.$node.empty();
        });

        this.attachEvents = function() {
            this.scrollNode = this.$node;
            while (this.scrollNode.length && this.scrollNode.css('overflow') !== 'auto') {
                this.scrollNode = this.scrollNode.parent();
            }
            this.scrollNode.on('scroll.vertexList', this.onResultsScroll.bind(this));

            this.$node.on('mouseenter mouseleave', '.vertex-item', this.onHoverItem.bind(this));

            this.on(document, 'verticesAdded', this.onVerticesUpdated);
            this.on(document, 'verticesUpdated', this.onVerticesUpdated);
            this.on(document, 'verticesDeleted', this.onVerticesDeleted);
            this.on(document, 'objectsSelected', this.onObjectsSelected);
            this.on(document, 'switchWorkspace', this.onWorkspaceClear);
            this.on(document, 'workspaceDeleted', this.onWorkspaceClear);
            this.on(document, 'workspaceLoaded', this.onWorkspaceLoaded);
            this.on('addInfiniteVertices', this.onAddInfiniteVertices);
        };

        this.setupDraggables = function() {
            this.applyDraggable(this.$node.find('a'));
            this.$node.droppable({ accept:'*', tolerance:'pointer' });
        };

        this.onHoverItem = function(evt) {
            if (this.disableHover === 'defocused') {
                return;
            } else if (this.disableHover) {
                this.disableHover = 'defocused';
                return this.trigger(document, 'defocusVertices');
            }

            var id = $(evt.target).closest('.vertex-item').data('vertexId');
            if (evt.type == 'mouseenter' && id) {
                this.trigger(document, 'focusVertices', { vertexIds:[id] });
            } else {
                this.trigger(document, 'defocusVertices');
            }
        };

        this.onResultsScroll = function(e) {
            if (!this.disableHover) {
                this.disableHover = true;
            }

            this.loadVisibleResultPreviews();

            if (this.attr.infiniteScrolling) {
                this.triggerInfiniteScrollRequest();
            }
        };

        this.triggerInfiniteScrollRequest = function() {
            if (!this.attr.infiniteScrolling) return;

            var loadingListElement = this.$node.find('.infinite-loading');

            if (this.scrollNode.length) {
                loadingListElement = loadingListElement.withinScrollable(this.scrollNode);
            }

            if (loadingListElement.length) {
                var data = { conceptType: this.attr.verticesConceptId };
                if (!this.offset) this.offset = this.attr.vertices.length;
                data.paging = {
                    offset: this.offset
                };
                this.trigger('infiniteScrollRequest', data);
            }
        };

        this.onAddInfiniteVertices = function(evt, data) {
            var loading = this.$node.find('.infinite-loading');

            if (data.vertices.length === 0) {
                loading.remove();
                this.attr.infiniteScrolling = false;
            } else {
                this.offset += data.vertices.length;
                var clsMap = this.classNameMapForVertices(data.vertices),
                    added = data.vertices.map(function(vertex) {
                        return vertexTemplate({
                            vertex:vertex,
                            classNamesForVertex: clsMap
                        });
                    });

                var lastItem = loading.prev();

                loading.before(added);

                var total = data.total || this.attr.total || 0;
                if (total === this.$node.find('.vertex-item').length) {
                    loading.remove();
                    this.attr.infiniteScrolling = false;
                } else {
                    this.triggerInfiniteScrollRequest();
                }

                this.loadVisibleResultPreviews();

                this.applyDraggable(this.$node.find('a'));
            }
        };

        this.loadVisibleResultPreviews = function() {
            var self = this;

            this.disableHover = false;
            if ( !this.previewQueue ) {
                this.previewQueue = previews.createQueue('vertexList', { maxConcurrent: 1 });
            }

            var lisVisible = this.$node.find('.nav-list').children('li');
            if (this.scrollNode.length) {
                lisVisible = lisVisible.withinScrollable(this.scrollNode);
            }

            lisVisible.each(function() {
                var li = $(this),
                    vertex = appData.vertex(li.data('vertexId'));

                if (!vertex) return;

                if (hasPreview(vertex) && !li.data('preview-loaded')) {

                        if (li.data('previewloaded')) return;

                        li.data('previewloaded', true).addClass('preview-loading');

                        if (vertex.properties._glyphIcon) {
                            li.removeClass('preview-loading')
                                .data('preview-loaded', true)
                                .find('.preview').html("");
                        } else {
                            previews.generatePreview(vertex, { width: 200 }, function(poster, frames) {
                                li.removeClass('preview-loading')
                                  .data('preview-loaded', true);

                                if(vertex.concept.displayType === 'video') {
                                    VideoScrubber.attachTo(li.find('.preview'), {
                                        posterFrameUrl: poster,
                                        videoPreviewImageUrl: frames
                                    });
                                } else if(vertex.concept.displayType === 'image') {
                                    li.find('.preview').html("");
                                }
                            });
                        }
                    }
            });
        };

        this.applyDraggable = function(el) {
            var self = this;

            el.draggable({
                helper:'clone',
                appendTo: 'body',
                revert: 'invalid',
                revertDuration: 250,
                scroll: false,
                zIndex: 100,
                distance: 10,
                multi: true,
                otherDraggablesClass: 'vertex-dragging',
                start: function(ev, ui) {
                    $(ui.helper).addClass('vertex-dragging');
                },
                selection: function(ev, ui) {
                    self.selectItems(ui.selected);
                }
            });
        };

        this.selectItems = function(items) {
            var vertices = appData.vertices(items.map(function() {
                    return $(this).data('vertexId');
                }).toArray());

            if (vertices.length > 1) {
                vertices.forEach (function (vertex) {
                    vertex.workspace = {
                        selected: true
                    };
                });
            }
            this.trigger(document, 'defocusVertices');
            this.trigger('selectObjects', { vertices:vertices });
        };

        this.onWorkspaceLoaded = function(evt, workspace) {
            this.onVerticesUpdated(evt, workspace.data || {});
        };

        // Track changes to vertices so we display the "Displayed in Graph" icon
        // in search results
        this.toggleItemIcons = function(id, data) {
            this.$node
                .find('li.gId' + encodeURIComponent(id))
                .toggleClass('graph-displayed', data.inGraph)
                .toggleClass('map-displayed', data.inMap);
        };

        // Switching workspaces should clear the icon state and vertices
        this.onWorkspaceClear = function() {
            this.$node.find('li.graph-displayed').removeClass('graph-displayed');
            this.$node.find('li.map-displayed').removeClass('map-displayed');
        };

        this.onVerticesUpdated = function(event, data) {
            var self = this;
            (data.vertices || []).forEach(function(vertex) {
                self.toggleItemIcons(vertex.id, self.stateForVertex(vertex));
                var currentAnchor = self.$node.find('li.gId' + encodeURIComponent(vertex.id)).children('a'),
                    newAnchor = $(vertexTemplate({
                        vertex: vertex,
                        classNamesForVertex: self.classNameMapForVertices([vertex]),
                    })).children('a'),
                    currentHtml = currentAnchor.html();

                if (vertex.properties._glyphIcon) {
                    $('').attr('src', vertex.properties._glyphIcon).appendTo(newAnchor.find('.preview'));
                }

                var newHtml = newAnchor.html();
                if (currentAnchor.length && newHtml !== currentHtml) {
                    currentAnchor.html(newHtml).closest('.vertex-item').toggleClass('has_preview', hasPreview(vertex));
                }
            });
        };

        this.onVerticesDeleted = function(event, data) {
            var self = this;
            (data.vertices || []).forEach(function(vertex) {
                self.toggleItemIcons(vertex.id, { inGraph:false, inMap:false });
            });
        };

        this.onObjectsSelected = function(event, data) {
            this.$node.find('.active').removeClass('active');

            var ids = _.chain(data.vertices)
                .map(function(v) { return '.gId' + v.id; })
                .value().join(',');

            $(ids).addClass('active');
        };
    }
});
<|endoftext|>


<commit_before>
// usage: log('inside coolFunc', this, arguments);
// paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
window.log = function(){
  log.history = log.history || [];   // store logs to an array for reference
  log.history.push(arguments);
  if(this.console) {
    arguments.callee = arguments.callee.caller;
    if(typeof console.log === 'object'){
      Function.prototype.apply.call(console.log, console, Array.prototype.slice.call(arguments))
    } else {
      console.log.apply(console, Array.prototype.slice.call(arguments));
    }
  }
};
// make it safe to use console.log always
(function(b){function c(){}for(var d="assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,time,timeEnd,trace,warn".split(","),a;a=d.pop();)b[a]=b[a]||c})(window.console=window.console||{});

// place any jQuery/helper plugins in here, instead of separate, slower script files.
<commit_msg>improve the console.log nerfing for ff3.6. thx @drewcovi. fixes #455<commit_after>
// usage: log('inside coolFunc', this, arguments);
// paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
window.log = function(){
  log.history = log.history || [];   // store logs to an array for reference
  log.history.push(arguments);
  if(this.console) {
    arguments.callee = arguments.callee.caller;
    if(typeof console.log === 'object'){
      Function.prototype.apply.call(console.log, console, Array.prototype.slice.call(arguments))
    } else {
      console.log.apply(console, Array.prototype.slice.call(arguments));
    }
  }
};
// make it safe to use console.log always
(function(b){function c(){}for(var d="assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,time,timeEnd,trace,warn".split(","),a;a=d.pop();){b[a]=b[a]||c}})((function(){try
{console.log();return window.console;}catch(err){return window.console={};}})());

// place any jQuery/helper plugins in here, instead of separate, slower script files.
<|endoftext|>


<commit_before>"use strict";

function addMockTimeoutService(module) {
    module.factory("$timeout", null, function() {
        function timeoutGroup() {
            var timeouts = [];

            function remove(id) {
                recurve.forEach(timeouts, function(timeout, index) {
                    if (timeout.id !== id) {
                        return;
                    }

                    removeAtIndex(timeout.id, index);
                    return false;
                });
            }

            function removeAtIndex(id, index) {
                window.clearTimeout(id);
                timeouts.splice(index, 1);
            }

            return {
                add: function(id, fn) {
                    timeouts.push({id: id, fn: fn});
                },

                call: function(id) {
                    recurve.forEach(timeouts, function(timeout, index) {
                        if (timeout.id !== id) {
                            return;
                        }

                        timeout.fn();
                        removeAtIndex(timeout.id, index);

                        return false;
                    });
                },

                callAll: function() {
                    recurve.forEach(timeouts, function(timeout) {
                        timeout.fn();
                        window.clearTimeout(timeout.id);
                    });

                    timeouts = [];
                },

                remove: remove
            }
        }

        var timeoutGroupByTime = {};

        function addTimeout(id, fn, time) {
            var group = timeoutGroupByTime[time];
            if (!group) {
                group = timeoutGroup();
                timeoutGroupByTime[time] = group;
            }

            group.remove(id);
            group.add(id, fn);
        }

        function callTimeout(id, time) {
            var group = timeoutGroupByTime[time];
            if (group) {
                group.call(id);
            }
        }

        var $timeout = function(fn, time) {
            var id = window.setTimeout(function() {
                callTimeout(id, time);
            }, time);

            addTimeout(id, fn, time);
        };

        return recurve.extend($timeout, {
            cancel: function(id) {
                recurve.forEach(timeoutGroupByTime, function(group) {
                    group.remove(id);
                });
            },

            flush: function(maxTime) {
                recurve.forEach(timeoutGroupByTime, function(group, time) {
                    if (maxTime <= time) {
                        return false;
                    }

                    group.callAll();
                    delete timeoutGroupByTime[time];
                });
            }
        });
    });
}<commit_msg>updated naming to avoid reserved js names<commit_after>"use strict";

function addMockTimeoutService(module) {
    module.factory("$timeout", null, function() {
        function timeoutGroup() {
            var timeouts = [];

            function remove(id) {
                recurve.forEach(timeouts, function(timeout, index) {
                    if (timeout.id !== id) {
                        return;
                    }

                    removeAtIndex(timeout.id, index);
                    return false;
                });
            }

            function removeAtIndex(id, index) {
                window.clearTimeout(id);
                timeouts.splice(index, 1);
            }

            return {
                add: function(id, fn) {
                    timeouts.push({id: id, fn: fn});
                },

                remove: remove,

                invoke: function(id) {
                    recurve.forEach(timeouts, function(timeout, index) {
                        if (timeout.id !== id) {
                            return;
                        }

                        timeout.fn();
                        removeAtIndex(timeout.id, index);

                        return false;
                    });
                },

                invokeAll: function() {
                    recurve.forEach(timeouts, function(timeout) {
                        timeout.fn();
                        window.clearTimeout(timeout.id);
                    });

                    timeouts = [];
                }
            }
        }

        var timeoutGroupByTime = {};

        function addTimeout(id, fn, time) {
            var group = timeoutGroupByTime[time];
            if (!group) {
                group = timeoutGroup();
                timeoutGroupByTime[time] = group;
            }

            group.remove(id);
            group.add(id, fn);
        }

        function invokeTimeout(id, time) {
            var group = timeoutGroupByTime[time];
            if (group) {
                group.invoke(id);
            }
        }

        var $timeout = function(fn, time) {
            var id = window.setTimeout(function() {
                invokeTimeout(id, time);
            }, time);

            addTimeout(id, fn, time);
        };

        return recurve.extend($timeout, {
            cancel: function(id) {
                recurve.forEach(timeoutGroupByTime, function(group) {
                    group.remove(id);
                });
            },

            flush: function(maxTime) {
                recurve.forEach(timeoutGroupByTime, function(group, time) {
                    if (maxTime <= time) {
                        return false;
                    }

                    group.invokeAll();
                    delete timeoutGroupByTime[time];
                });
            }
        });
    });
}<|endoftext|>


<commit_before>/* global describe, afterEach, it, expect, beforeEach */

var expect = require('chai').expect;  // jshint ignore:line
var StubGenerator = require('../lib/stub-generator');
var CachingBrowserify = require('../lib/caching-browserify');
var RSVP = require('rsvp');
RSVP.on('error', function(err){throw err;});
var fs = require('fs');
var path = require('path');
var broccoli = require('broccoli');
var quickTemp = require('quick-temp');
var copy = require('copy-dereference').sync;

var src = {};
var builder;
var readTrees;

describe('Stub Generator', function() {

  beforeEach(function() {
    quickTemp.makeOrRemake(src, 'tmp');
    src.inputTree = path.join(src.tmp, 'inputTree');
    copy(path.join(__dirname, 'fixtures', 'stubs'), src.inputTree);
  });

  afterEach(function() {
    if (src.tmp) {
      quickTemp.remove(src, 'tmp');
    }
    if (builder) {
      return builder.cleanup();
    }
  });

  it('generates stub file', function() {
    var tree = new StubGenerator(src.inputTree);
    builder = new broccoli.Builder(tree);
    return builder.build().then(function(result) {
      expectFile('browserify_stubs.js').toMatch('first.js').in(result);
    });
  });

  it('adds deps from new file', function() {
    var tree = new StubGenerator(src.inputTree);
    builder = new broccoli.Builder(tree);
    return builder.build().then(function(result) {
      expectFile('browserify_stubs.js').toMatch('first.js').in(result);
      fs.writeFileSync(path.join(src.inputTree, 'new.js'), "import SomethingNew from \"npm:something-new\"\n");
      return builder.build();
    }).then(function(result){
      expectFile('browserify_stubs.js').toMatch('second.js').in(result);
    });
  });

  it('removes deps from deleted file', function() {
    var tree = new StubGenerator(src.inputTree);
    builder = new broccoli.Builder(tree);
    return builder.build().then(function(result) {
      expectFile('browserify_stubs.js').toMatch('first.js').in(result);
      fs.unlinkSync(path.join(src.inputTree, 'sample.js'));
      return builder.build();
    }).then(function(result){
      expectFile('browserify_stubs.js').toMatch('third.js').in(result);
    });
  });

  it('adds deps in modified file', function() {
    var tree = new StubGenerator(src.inputTree);
    builder = new broccoli.Builder(tree);
    return builder.build().then(function(result) {
      expectFile('browserify_stubs.js').toMatch('first.js').in(result);
      var was = fs.readFileSync(path.join(src.inputTree, 'sample.js'));
      was += "\nimport Additional from 'npm:additional-thing';";
      fs.writeFileSync(path.join(src.inputTree, 'sample.js'), was);
      return builder.build();
    }).then(function(result){
      expectFile('browserify_stubs.js').toMatch('fourth.js').in(result);
    });
  });

  it('removes deps in modified file', function() {
    var tree = new StubGenerator(src.inputTree);
    builder = new broccoli.Builder(tree);
    return builder.build().then(function(result) {
      expectFile('browserify_stubs.js').toMatch('first.js').in(result);
      var was = fs.readFileSync(path.join(src.inputTree, 'inner', 'other.js'), 'utf-8');
      was = was.split("\n").slice(1).join("\n");
      fs.writeFileSync(path.join(src.inputTree, 'inner', 'other.js'), was);
      return builder.build();
    }).then(function(result){
      expectFile('browserify_stubs.js').toMatch('fifth.js').in(result);
    });
  });

});

describe('CachingBrowserify', function() {
  beforeEach(function() {
    quickTemp.makeOrRemake(src, 'tmp');
    src.inputTree = path.join(src.tmp, 'inputTree');
    copy(path.join(__dirname, 'fixtures', 'modules'), src.inputTree);
    src.entryTree = path.join(src.inputTree, 'src');
    readTrees = {};
  });

  afterEach(function() {
    if (src.tmp) {
      quickTemp.remove(src, 'tmp');
    }
    if (builder) {
      return builder.cleanup();
    }
  });

  function recordReadTrees(tree) {
    readTrees[tree] = true;
  }

  it('builds successfully', function() {
    var tree = new CachingBrowserify(src.entryTree);
    builder = new broccoli.Builder(tree);
    return builder.build().then(function(result){
      expectFile('browserify/browserify.js').toMatch('bundle1.js').in(result);
    });
  });

  it('rebuilds when an npm module changes', function(){
    var tree = new CachingBrowserify(src.entryTree);
    builder = new broccoli.Builder(tree);
    return builder.build(recordReadTrees).then(function(result){
      expectFile('browserify/browserify.js').toMatch('bundle1.js').in(result);
      var module = path.join(src.inputTree, 'node_modules', 'my-module');
      var target = path.join(module, 'index.js');
      expect(readTrees[module]).to.equal(true, 'should be watching npm module');
      var code = fs.readFileSync(target, 'utf-8');
      code = code.replace('other.something()', 'other.something()+1');
      fs.writeFileSync(target, code);
      return builder.build();
    }).then(function(result){
      expectFile('browserify/browserify.js').toMatch('bundle2.js').in(result);
    });
  });

  it('rebuilds when the entry file changes', function(){
    var tree = new CachingBrowserify(src.entryTree);
    builder = new broccoli.Builder(tree);
    return builder.build(recordReadTrees).then(function(result){
      expectFile('browserify/browserify.js').toMatch('bundle1.js').in(result);
      expect(readTrees[src.entryTree]).to.equal(true, 'should be watching stubs file');
      fs.unlinkSync(path.join(src.entryTree, 'browserify_stubs.js'));
      copy(path.join(src.entryTree, 'second_stubs.js'), path.join(src.entryTree, 'browserify_stubs.js'));
      return builder.build();
    }).then(function(result){
      expectFile('browserify/browserify.js').toMatch('bundle3.js').in(result);
    });
  });

});

function expectFile(filename) {
  var stripURL = false;
  var expectedFilename = filename;
  return {
      in: function(result) {
        var actualContent = fs.readFileSync(path.join(result.directory, filename), 'utf-8');
        fs.writeFileSync(path.join(__dirname, 'actual', expectedFilename), actualContent);

        var expectedContent;
        try {
          expectedContent = fs.readFileSync(path.join(__dirname, 'expected', expectedFilename), 'utf-8');
          if (stripURL) {
            expectedContent = expectedContent.replace(/\/\/# sourceMappingURL=.*$/, '');
          }

        } catch (err) {
          console.warn("Missing expcted file: " + path.join(__dirname, 'expected', filename));
        }
        expectSameFiles(actualContent, expectedContent, filename);
        return this;
      },
    notIn: function(result) {
      expect(fs.existsSync(path.join(result.directory, filename))).to.equal(false, filename + ' should not have been present');
      return this;
    },
    toMatch: function(expectedName) {
      expectedFilename = expectedName;
      return this;
    },
    withoutSrcURL: function() {
      stripURL = true;
      return this;
    }
  };
}

function expectSameFiles(actualContent, expectedContent, filename) {
  if (/\.map$/.test(filename)) {
    expect(JSON.parse(actualContent)).to.deep.equal(JSON.parse(expectedContent), 'discrepancy in ' + filename);
  } else {
    expect(actualContent).to.equal(expectedContent, 'discrepancy in ' + filename);
  }
}
<commit_msg>localize test vars<commit_after>/* global describe, afterEach, it, expect, beforeEach */

var expect = require('chai').expect;  // jshint ignore:line
var StubGenerator = require('../lib/stub-generator');
var CachingBrowserify = require('../lib/caching-browserify');
var RSVP = require('rsvp');
RSVP.on('error', function(err){throw err;});
var fs = require('fs');
var path = require('path');
var broccoli = require('broccoli');
var quickTemp = require('quick-temp');
var copy = require('copy-dereference').sync;

describe('Stub Generator', function() {
  var src = {};
  var builder;

  beforeEach(function() {
    quickTemp.makeOrRemake(src, 'tmp');
    src.inputTree = path.join(src.tmp, 'inputTree');
    copy(path.join(__dirname, 'fixtures', 'stubs'), src.inputTree);
  });

  afterEach(function() {
    if (src.tmp) {
      quickTemp.remove(src, 'tmp');
    }
    if (builder) {
      return builder.cleanup();
    }
  });

  it('generates stub file', function() {
    var tree = new StubGenerator(src.inputTree);
    builder = new broccoli.Builder(tree);
    return builder.build().then(function(result) {
      expectFile('browserify_stubs.js').toMatch('first.js').in(result);
    });
  });

  it('adds deps from new file', function() {
    var tree = new StubGenerator(src.inputTree);
    builder = new broccoli.Builder(tree);
    return builder.build().then(function(result) {
      expectFile('browserify_stubs.js').toMatch('first.js').in(result);
      fs.writeFileSync(path.join(src.inputTree, 'new.js'), "import SomethingNew from \"npm:something-new\"\n");
      return builder.build();
    }).then(function(result){
      expectFile('browserify_stubs.js').toMatch('second.js').in(result);
    });
  });

  it('removes deps from deleted file', function() {
    var tree = new StubGenerator(src.inputTree);
    builder = new broccoli.Builder(tree);
    return builder.build().then(function(result) {
      expectFile('browserify_stubs.js').toMatch('first.js').in(result);
      fs.unlinkSync(path.join(src.inputTree, 'sample.js'));
      return builder.build();
    }).then(function(result){
      expectFile('browserify_stubs.js').toMatch('third.js').in(result);
    });
  });

  it('adds deps in modified file', function() {
    var tree = new StubGenerator(src.inputTree);
    builder = new broccoli.Builder(tree);
    return builder.build().then(function(result) {
      expectFile('browserify_stubs.js').toMatch('first.js').in(result);
      var was = fs.readFileSync(path.join(src.inputTree, 'sample.js'));
      was += "\nimport Additional from 'npm:additional-thing';";
      fs.writeFileSync(path.join(src.inputTree, 'sample.js'), was);
      return builder.build();
    }).then(function(result){
      expectFile('browserify_stubs.js').toMatch('fourth.js').in(result);
    });
  });

  it('removes deps in modified file', function() {
    var tree = new StubGenerator(src.inputTree);
    builder = new broccoli.Builder(tree);
    return builder.build().then(function(result) {
      expectFile('browserify_stubs.js').toMatch('first.js').in(result);
      var was = fs.readFileSync(path.join(src.inputTree, 'inner', 'other.js'), 'utf-8');
      was = was.split("\n").slice(1).join("\n");
      fs.writeFileSync(path.join(src.inputTree, 'inner', 'other.js'), was);
      return builder.build();
    }).then(function(result){
      expectFile('browserify_stubs.js').toMatch('fifth.js').in(result);
    });
  });

});

describe('CachingBrowserify', function() {
  var src = {};
  var builder;
  var readTrees;

  beforeEach(function() {
    quickTemp.makeOrRemake(src, 'tmp');
    src.inputTree = path.join(src.tmp, 'inputTree');
    copy(path.join(__dirname, 'fixtures', 'modules'), src.inputTree);
    src.entryTree = path.join(src.inputTree, 'src');
    readTrees = {};
  });

  afterEach(function() {
    if (src.tmp) {
      quickTemp.remove(src, 'tmp');
    }
    if (builder) {
      return builder.cleanup();
    }
  });

  function recordReadTrees(tree) {
    readTrees[tree] = true;
  }

  it('builds successfully', function() {
    var tree = new CachingBrowserify(src.entryTree);
    builder = new broccoli.Builder(tree);
    return builder.build().then(function(result){
      expectFile('browserify/browserify.js').toMatch('bundle1.js').in(result);
    });
  });

  it('rebuilds when an npm module changes', function(){
    var tree = new CachingBrowserify(src.entryTree);
    builder = new broccoli.Builder(tree);
    return builder.build(recordReadTrees).then(function(result){
      expectFile('browserify/browserify.js').toMatch('bundle1.js').in(result);
      var module = path.join(src.inputTree, 'node_modules', 'my-module');
      var target = path.join(module, 'index.js');
      expect(readTrees[module]).to.equal(true, 'should be watching npm module');
      var code = fs.readFileSync(target, 'utf-8');
      code = code.replace('other.something()', 'other.something()+1');
      fs.writeFileSync(target, code);
      return builder.build();
    }).then(function(result){
      expectFile('browserify/browserify.js').toMatch('bundle2.js').in(result);
    });
  });

  it('rebuilds when the entry file changes', function(){
    var tree = new CachingBrowserify(src.entryTree);
    builder = new broccoli.Builder(tree);
    return builder.build(recordReadTrees).then(function(result){
      expectFile('browserify/browserify.js').toMatch('bundle1.js').in(result);
      expect(readTrees[src.entryTree]).to.equal(true, 'should be watching stubs file');
      fs.unlinkSync(path.join(src.entryTree, 'browserify_stubs.js'));
      copy(path.join(src.entryTree, 'second_stubs.js'), path.join(src.entryTree, 'browserify_stubs.js'));
      return builder.build();
    }).then(function(result){
      expectFile('browserify/browserify.js').toMatch('bundle3.js').in(result);
    });
  });

});

function expectFile(filename) {
  var stripURL = false;
  var expectedFilename = filename;
  return {
      in: function(result) {
        var actualContent = fs.readFileSync(path.join(result.directory, filename), 'utf-8');
        fs.writeFileSync(path.join(__dirname, 'actual', expectedFilename), actualContent);

        var expectedContent;
        try {
          expectedContent = fs.readFileSync(path.join(__dirname, 'expected', expectedFilename), 'utf-8');
          if (stripURL) {
            expectedContent = expectedContent.replace(/\/\/# sourceMappingURL=.*$/, '');
          }

        } catch (err) {
          console.warn("Missing expcted file: " + path.join(__dirname, 'expected', filename));
        }
        expectSameFiles(actualContent, expectedContent, filename);
        return this;
      },
    notIn: function(result) {
      expect(fs.existsSync(path.join(result.directory, filename))).to.equal(false, filename + ' should not have been present');
      return this;
    },
    toMatch: function(expectedName) {
      expectedFilename = expectedName;
      return this;
    },
    withoutSrcURL: function() {
      stripURL = true;
      return this;
    }
  };
}

function expectSameFiles(actualContent, expectedContent, filename) {
  if (/\.map$/.test(filename)) {
    expect(JSON.parse(actualContent)).to.deep.equal(JSON.parse(expectedContent), 'discrepancy in ' + filename);
  } else {
    expect(actualContent).to.equal(expectedContent, 'discrepancy in ' + filename);
  }
}
<|endoftext|>


<commit_before>/**
 * Mouseover help popup for EVOLaunchClientPopup
 */

var EVOLaunchClientHelpPopup = function(event) {
    IndicoUI.Widgets.Generic.tooltip(this, event,
        '
' +
            $T('If you are using Internet Explorer, Indico cannot load the EVO client directly; '  +
            'you need to click on the link. ' +
            'You can avoid this by using another browser. Sorry for the inconvenience.') +
        '<\/div>');
};

type("EVOLaunchClientPopup", ["ExclusivePopup"],
    {
        draw: function() {
            var self = this;

            var linkClicked = function(){
                self.close();
                return true;
            };

            var clientLink = Html.a({href: this.bookingUrl, onclick : linkClicked, style:{display: 'block'}},
                    $T("Click here to launch the EVO client"));

            var infoLink = Html.span({className: 'fakeLink', style: {display: 'block', fontSize: 'smaller', paddingTop: pixels(10)}},
                $T('(Why am I getting this popup?)'));
            infoLink.dom.onmouseover = EVOLaunchClientHelpPopup;

            var cancelButton = Html.button({style: {marginTop: pixels(10)}}, $T("Cancel"));
            cancelButton.observeClick(function(){
                self.close();
            });

            return this.ExclusivePopup.prototype.draw.call(this,
                    Html.div({style:{textAlign: 'center'}}, clientLink, infoLink, cancelButton)
                    );
        }
    },
    function(bookingUrl) {
        this.bookingUrl = bookingUrl;
        this.ExclusivePopup($T('Launch EVO client'), positive);
    }
);

/**
 * Mouseover help popup for the 'Start date' field
 */
var EVOStartDateHelpPopup = function(event) {
    IndicoUI.Widgets.Generic.tooltip(this, event,
        '
' +
            $T('Please create your booking between ${ MinStartDate } and ${ MaxEndDate } ' +
            "(Allowed dates \/ times based on your event's start date and end date). " +
            'Also, please remember the start date cannot be more than ${ AllowedStartMinutes } minutes in the past.') +
        '<\/div>');
};

/**
 * Mouseover help popup for the 'End date' field
 */
var EVOEndDateHelpPopup = function(event) {
    IndicoUI.Widgets.Generic.tooltip(this, event,
        '
' +
            $T('Please create your booking between ${ MinStartDate } and ${ MaxEndDate } ' +
            "(Allowed dates \/ times based on your event's start date and end date).") +
        '<\/div>');
};

/**
 * Mouseover help popup for the 'password' field
 */
var EVOPasswordHelpPopup = function(event) {
    IndicoUI.Widgets.Generic.tooltip(this, event,
        '
' +
            $T('If you want to protect<\/strong> your EVO meeting with a password, please input it here. ' +
                    'Otherwise, leave this field empty.') +
        '<\/div>');
};

/**
 * Mouseover help popup for the 'displayPassword' field
 */
var EVODisplayPasswordHelpPopup = function(event) {
    IndicoUI.Widgets.Generic.tooltip(this, event,
        '
' +
            $T('The EVO Meeting password will be displayed in the event page. '+
               'Any person that can see the event page will see the password. Please use this option carefully.') +
        '<\/div>');
};

/**
 * Mouseover help popup for the 'displayPhonePassword' field
 */
var EVODisplayPhonePasswordHelpPopup = function(event) {
    IndicoUI.Widgets.Generic.tooltip(this, event,
        '
' +
        $T('The Phone bridge password will be displayed in the event page. '+
           'Any person that can see the event page will see the password. Please use this option carefully.') +
        '<\/div>');
};

/**
 * Mouseover help popup for the 'displayURL' field
 */
var EVODisplayURLHelpPopup = function(event) {
    IndicoUI.Widgets.Generic.tooltip(this, event,
        '
' +
            $T('Please note that regardless of this option, when the EVO meeting starts, '+
               'a Join Now! link will appear.') +
        '<\/div>');
};

/**
 * Draws the context help icons and assigns the appropiate popups to each one.
 */
var EVODrawContextHelpIcons = function() {

    $E('startDateHelpImg').dom.onmouseover = EVOStartDateHelpPopup;
    $E('endDateHelpImg').dom.onmouseover = EVOEndDateHelpPopup;
    $E('passwordHelpImg').dom.onmouseover = EVOPasswordHelpPopup;
    $E('displayPasswordHelpImg').dom.onmouseover = EVODisplayPasswordHelpPopup;
    $E('displayPhonePasswordHelpImg').dom.onmouseover = EVODisplayPhonePasswordHelpPopup;
    $E('displayURLHelpImg').dom.onmouseover = EVODisplayURLHelpPopup;
}
<commit_msg>[FIX] fix EVOLaunchClientPopup dialog<commit_after>/**
 * Mouseover help popup for EVOLaunchClientPopup
 */

var EVOLaunchClientHelpPopup = function(event) {
    IndicoUI.Widgets.Generic.tooltip(this, event,
        '
' +
            $T('If you are using Internet Explorer, Indico cannot load the EVO client directly; '  +
            'you need to click on the link. ' +
            'You can avoid this by using another browser. Sorry for the inconvenience.') +
        '<\/div>');
};

type("EVOLaunchClientPopup", ["AlertPopup"],
    {
        __getContent: function() {
            var self = this;

            var linkClicked = function(){
                self.close();
                return true;
            };

            var clientLink = $('', {href: this.bookingUrl})
                .click(linkClicked)
                .css('display', 'block')
                .text($T("Click here to launch the EVO client"));

            var infoLink = $('', {'class': 'fakeLink'})
                .mouseover(EVOLaunchClientHelpPopup)
                .css({
                    display: 'block',
                    fontSize: 'smaller',
                    paddingTop: '10px'
                })
                .text($T('(Why am I getting this popup?)'));

            return $('
').append(clientLink).append(infoLink);
        }
    },
    function(bookingUrl) {
        this.bookingUrl = bookingUrl;
        this.AlertPopup($T('Launch EVO client'), this.__getContent());
    }
);

/**
 * Mouseover help popup for the 'Start date' field
 */
var EVOStartDateHelpPopup = function(event) {
    IndicoUI.Widgets.Generic.tooltip(this, event,
        '
' +
            $T('Please create your booking between ${ MinStartDate } and ${ MaxEndDate } ' +
            "(Allowed dates \/ times based on your event's start date and end date). " +
            'Also, please remember the start date cannot be more than ${ AllowedStartMinutes } minutes in the past.') +
        '<\/div>');
};

/**
 * Mouseover help popup for the 'End date' field
 */
var EVOEndDateHelpPopup = function(event) {
    IndicoUI.Widgets.Generic.tooltip(this, event,
        '
' +
            $T('Please create your booking between ${ MinStartDate } and ${ MaxEndDate } ' +
            "(Allowed dates \/ times based on your event's start date and end date).") +
        '<\/div>');
};

/**
 * Mouseover help popup for the 'password' field
 */
var EVOPasswordHelpPopup = function(event) {
    IndicoUI.Widgets.Generic.tooltip(this, event,
        '
' +
            $T('If you want to protect<\/strong> your EVO meeting with a password, please input it here. ' +
                    'Otherwise, leave this field empty.') +
        '<\/div>');
};

/**
 * Mouseover help popup for the 'displayPassword' field
 */
var EVODisplayPasswordHelpPopup = function(event) {
    IndicoUI.Widgets.Generic.tooltip(this, event,
        '
' +
            $T('The EVO Meeting password will be displayed in the event page. '+
               'Any person that can see the event page will see the password. Please use this option carefully.') +
        '<\/div>');
};

/**
 * Mouseover help popup for the 'displayPhonePassword' field
 */
var EVODisplayPhonePasswordHelpPopup = function(event) {
    IndicoUI.Widgets.Generic.tooltip(this, event,
        '
' +
        $T('The Phone bridge password will be displayed in the event page. '+
           'Any person that can see the event page will see the password. Please use this option carefully.') +
        '<\/div>');
};

/**
 * Mouseover help popup for the 'displayURL' field
 */
var EVODisplayURLHelpPopup = function(event) {
    IndicoUI.Widgets.Generic.tooltip(this, event,
        '
' +
            $T('Please note that regardless of this option, when the EVO meeting starts, '+
               'a Join Now! link will appear.') +
        '<\/div>');
};

/**
 * Draws the context help icons and assigns the appropiate popups to each one.
 */
var EVODrawContextHelpIcons = function() {

    $E('startDateHelpImg').dom.onmouseover = EVOStartDateHelpPopup;
    $E('endDateHelpImg').dom.onmouseover = EVOEndDateHelpPopup;
    $E('passwordHelpImg').dom.onmouseover = EVOPasswordHelpPopup;
    $E('displayPasswordHelpImg').dom.onmouseover = EVODisplayPasswordHelpPopup;
    $E('displayPhonePasswordHelpImg').dom.onmouseover = EVODisplayPhonePasswordHelpPopup;
    $E('displayURLHelpImg').dom.onmouseover = EVODisplayURLHelpPopup;
}
<|endoftext|>


<commit_before><commit_msg>DevTools: allow deleting entries from localStorage<commit_after><|endoftext|>


<commit_before>// app.js

window.AudioContext = window.AudioContext||window.webkitAudioContext;
var gAudioContext = new AudioContext();
var gSoundSources = {};
var gSounds = {};
var gSoundProgress = 0;
var gSoundTotal = 0;

function getArrayBuffer(url) {
  // Return a new promise.
  return new Promise(function(resolve, reject) {
    // Do the usual XHR stuff
    var req = new XMLHttpRequest();
    req.open('GET', url);

    req.onload = function() {
      // This is called even on 404 etc so check the status
      if (req.status == 200) {
        // Resolve the promise with the response body
        resolve(req.response);
      }
      else {
        // Otherwise reject with the status text which will hopefully be a meaningful error
        reject(Error(req.statusText));
      }
    };

    // Handle network errors
    req.onerror = function() {
      reject(Error("Network Error"));
    };

    // Make the request
	req.responseType = 'arraybuffer';
    req.send();
  });
}

function decodeAudioDataAsync(data){
	// TODO may not work in nightly
    return new Promise(function(resolve, reject){
         gAudioContext.decodeAudioData(data, resolve, reject);
    });
}

function loadSound(inSoundDataURL) {
	return getArrayBuffer(inSoundDataURL).then(function(response) {
		gSoundProgress++;
		$('.progress-bar').css("width", Math.floor(100 * gSoundProgress / gSoundTotal) + "%");
	    return decodeAudioDataAsync(response);
	}).then(function(decodedBuffer) {
		gSoundProgress++;
		$('.progress-bar').css("width", Math.floor(100 * gSoundProgress / gSoundTotal) + "%");
		gSounds[inSoundDataURL] = decodedBuffer;
	});
}

$(document).ready(function() {
	var loadPromises = [];

	$('input').each(function (index) {
		loadPromises.push(loadSound($(this).attr('data-url')));
	});

	gSoundTotal = 2 * loadPromises.length;

	Promise.all(loadPromises).then(function () {
		console.log('loaded all sounds');
		$('.progress').hide();
	});

	// respond to a click on the play button either by:
	// starting the sound indicated by the radio buttons, if the play button is not checked
	// stopping the currently playing sound, if the play button is checked

	$('button').click(function (e) {
		e.preventDefault();

		var buttonID = e.target.id;

		$(this).toggleClass('checked');

		if ($(this).hasClass('checked')) {
			var activeRadioButton = $(this).parent().parent().find('.btn-group label.active input');
			var soundURL = activeRadioButton.data('url');
			var rates = (activeRadioButton.data('rates')+"").split(',').map(function(str) { return parseFloat(str); });
			var randomRate = rates[Math.floor(Math.random() * rates.length)];

			console.log('START', activeRadioButton, soundURL, randomRate);

			if (gSounds[soundURL]) {
				// start playing immediately in a loop
				var newSoundSource = gAudioContext.createBufferSource();
				newSoundSource.connect(gAudioContext.destination);
				newSoundSource.buffer = gSounds[soundURL];
				newSoundSource.loop = true;
				newSoundSource.playbackRate.linearRampToValueAtTime(randomRate, gAudioContext.currentTime);
				newSoundSource.start(0);
				gSoundSources[buttonID] = newSoundSource;
			} else {
				console.log('ERROR, did not find in library', soundURL);
			}
		} else {
			console.log('STOP');
			gSoundSources[buttonID].stop(0);
			gSoundSources[buttonID].disconnect();
		}

	});

	$(document).on('change', 'input:radio', function (e) {
		e.preventDefault();
		console.log('clicked selector', e.target);
	});
});<commit_msg>add the classic redirection code better debugging printfs to catch weird problem with ID of button in event handler<commit_after>// app.js

// REDIRECT to HTTPS!
var host = "wfwalker.github.io";
if ((host == window.location.host) && (window.location.protocol != "https:")) {
	window.location.protocol = "https";
}

window.AudioContext = window.AudioContext||window.webkitAudioContext;
var gAudioContext = new AudioContext();
var gSoundSources = {};
var gSounds = {};
var gSoundProgress = 0;
var gSoundTotal = 0;

function getArrayBuffer(url) {
  // Return a new promise.
  return new Promise(function(resolve, reject) {
    // Do the usual XHR stuff
    var req = new XMLHttpRequest();
    req.open('GET', url);

    req.onload = function() {
      // This is called even on 404 etc so check the status
      if (req.status == 200) {
        // Resolve the promise with the response body
        resolve(req.response);
      }
      else {
        // Otherwise reject with the status text which will hopefully be a meaningful error
        reject(Error(req.statusText));
      }
    };

    // Handle network errors
    req.onerror = function() {
      reject(Error("Network Error"));
    };

    // Make the request
	req.responseType = 'arraybuffer';
    req.send();
  });
}

function decodeAudioDataAsync(data){
	// TODO may not work in nightly
    return new Promise(function(resolve, reject){
         gAudioContext.decodeAudioData(data, resolve, reject);
    });
}

function loadSound(inSoundDataURL) {
	return getArrayBuffer(inSoundDataURL).then(function(response) {
		gSoundProgress++;
		$('.progress-bar').css("width", Math.floor(100 * gSoundProgress / gSoundTotal) + "%");
	    return decodeAudioDataAsync(response);
	}).then(function(decodedBuffer) {
		gSoundProgress++;
		$('.progress-bar').css("width", Math.floor(100 * gSoundProgress / gSoundTotal) + "%");
		gSounds[inSoundDataURL] = decodedBuffer;
	});
}

$(document).ready(function() {
	var loadPromises = [];

	$('input').each(function (index) {
		loadPromises.push(loadSound($(this).attr('data-url')));
	});

	gSoundTotal = 2 * loadPromises.length;

	Promise.all(loadPromises).then(function () {
		console.log('loaded all sounds');
		$('.progress').hide();
	});

	// respond to a click on the play button either by:
	// starting the sound indicated by the radio buttons, if the play button is not checked
	// stopping the currently playing sound, if the play button is checked

	$('button').click(function (e) {
		e.preventDefault();

		var buttonID = e.target.id;

		$(this).toggleClass('checked');

		if ($(this).hasClass('checked')) {
			var activeRadioButton = $(this).parent().parent().find('.btn-group label.active input');
			var soundURL = activeRadioButton.data('url');
			var rates = (activeRadioButton.data('rates')+"").split(',').map(function(str) { return parseFloat(str); });
			var randomRate = rates[Math.floor(Math.random() * rates.length)];

			console.log('START', activeRadioButton, soundURL, randomRate);

			if (gSounds[soundURL]) {
				// start playing immediately in a loop
				var newSoundSource = gAudioContext.createBufferSource();
				newSoundSource.connect(gAudioContext.destination);
				newSoundSource.buffer = gSounds[soundURL];
				newSoundSource.loop = true;
				newSoundSource.playbackRate.linearRampToValueAtTime(randomRate, gAudioContext.currentTime);
				newSoundSource.start(0);
				gSoundSources[buttonID] = newSoundSource;
				console.log('STARTED', buttonID, gSoundSources[buttonID]);
			} else {
				console.log('ERROR, did not find in library', soundURL);
			}
		} else {
			console.log('STOP', buttonID, gSoundSources[buttonID]);
			gSoundSources[buttonID].stop(0);
			gSoundSources[buttonID].disconnect();
		}

	});

	$(document).on('change', 'input:radio', function (e) {
		e.preventDefault();
		console.log('clicked selector', e.target);
	});
});<|endoftext|>


<commit_before>'use strict'
// pretty dirty but makes the colors module compatible with the browser
var map = exports.map = require('./map')
var log = console.log
var string = String.prototype
var Style = function (val, style) {
  this.val = val
  this.style = style
}
var style = Style.prototype

for (let key in map.colors) {
  define(key, 'color:' + map.colors[key] + ';')
}

for (let key in map.backgrounds) {
  define(key, 'background-color:' + map.backgrounds[key] + ';')
}

for (let key in map.styles) {
  define(key, convertToCss(map.styles[key]))
}

for (let key in map.extras) {
  define(key, convertToCss(map.extras[key]))
}

console.line = true

console.log = function () {
  var args = [ '' ]

  for (let arg of arguments) {
    if (arg instanceof Style) {
      args[0] += '%c' + arg.val + ' '
      args.push(arg.style)
    } else {
      let type = typeof arg
      if (type === 'string') {
        args[0] += '%c' + arg + ' '
        args.push('color: inherit;')
      } else if (type === 'function') {
        args[0] += '%c' + String(arg) + '\n'
        args.push('color: grey;')
      } else if (type === 'object') {
        args[0] += '%O '
        args.push(arg)
      }
      // dom node
    }
  }
  if (console.line) {
    args[0] += ('\n%c' + extractLineNumberFromStack((new Error()).stack))
    args.push(`
      color: grey;
      font-style:italic;
      font-size:8px;
      line-height: 30px;
      padding-bottom: 0px;
      overflow:hidden;
    `)
  }

  log.apply(console, args)
}

function extractLineNumberFromStack (stack) {
  var lines = stack.split('\n')
  var line = lines[2]
  line = (line.indexOf(' (') >= 0
    ? line.split(' (')[1].substring(0, line.length - 1)
    : line.split('at ')[1]
    )
  return line
}

// util functions
function define (key, css) {
  Object.defineProperty(style, key, {
    get () {
      this.style = this.style + css
      return this
    }
  })
  Object.defineProperty(string, key, {
    get () {
      return new Style(this, css)
    }
  })
}

function convertToCss (obj) {
  let cssMap = obj
  let css = ''
  for (let style in cssMap) {
    css += style + ':' + cssMap[style] + ';'
  }
  return css
}
<commit_msg>added extra types for console replacement<commit_after>'use strict'
// pretty dirty but makes the colors module compatible with the browser
var map = exports.map = require('./map')
var log = console.log
var string = String.prototype
var Style = function (val, style) {
  this.val = val
  this.style = style
}
var style = Style.prototype

for (let key in map.colors) {
  define(key, 'color:' + map.colors[key] + ';')
}

for (let key in map.backgrounds) {
  define(key, 'background-color:' + map.backgrounds[key] + ';')
}

for (let key in map.styles) {
  define(key, convertToCss(map.styles[key]))
}

for (let key in map.extras) {
  define(key, convertToCss(map.extras[key]))
}

console.line = true

console.log = function () {
  var args = [ '' ]

  for (let arg of arguments) {
    if (arg instanceof Style) {
      args[0] += '%c' + arg.val + ' '
      args.push(arg.style)
    } else {
      let type = typeof arg
      if (type === 'string') {
        args[0] += '%c' + arg + ' '
        args.push('color: inherit;')
      } else if (type === 'function') {
        args[0] += '%c' + String(arg) + '\n'
        args.push('color: grey;')
      } else if (arg === null) {
        args[0] += '%cnull'
        args.push('color: grey;')
      } else if (type === 'object') {
        args[0] += '%O '
        args.push(arg)
      } else if (type === 'number' || type === 'boolean') {
        args[0] += '%c' + arg
        args.push('color: blue;')
      } else if (arg === void 0) {
        args[0] += '%cundefined'
        args.push('color: grey;')
      }
      // dom node
    }
  }
  if (console.line) {
    args[0] += ('\n%c' + extractLineNumberFromStack((new Error()).stack))
    args.push(`
      color: grey;
      font-style:italic;
      font-size:8px;
      line-height: 30px;
      padding-bottom: 0px;
      overflow:hidden;
    `)
  }

  log.apply(console, args)
}

function extractLineNumberFromStack (stack) {
  var lines = stack.split('\n')
  var line = lines[2]
  line = (line.indexOf(' (') >= 0
    ? line.split(' (')[1].substring(0, line.length - 1)
    : line.split('at ')[1]
    )
  return line
}

// util functions
function define (key, css) {
  Object.defineProperty(style, key, {
    get () {
      this.style = this.style + css
      return this
    }
  })
  Object.defineProperty(string, key, {
    get () {
      return new Style(this, css)
    }
  })
}

function convertToCss (obj) {
  let cssMap = obj
  let css = ''
  for (let style in cssMap) {
    css += style + ':' + cssMap[style] + ';'
  }
  return css
}
<|endoftext|>


<commit_before>
Elm.Native.WebSocket = {};

Elm.Native.WebSocket.make = function(localRuntime) {
    localRuntime.Native = localRuntime.Native || {};
    localRuntime.Native.WebSocket = localRuntime.Native.WebSocket || {};
    if (localRuntime.Native.WebSocket.values) return localRuntime.Native.WebSocket.values;

    var Task = Elm.Native.Task.make(localRuntime);
    var Utils = Elm.Native.Utils.make(localRuntime);
    var NS = Elm.Native.Signal.make(localRuntime);

    var message = NS.input('WebSocket.message', '');
    var opened = NS.input('WebSocket.opened', false);

    var connection = null;
    var interval = null;
    var pingInterval = null;
    var pingFlag = false;
    var connect = function(url) {
      return Task.asyncFunction(function(callback) {
        _connect(url, callback);
      });
    };
    var _connect = function(url, callback) {
      if(!connection) {
        try {
          connection = new WebSocket(url, ['soap', 'xmpp']);
          var tryStartReconnecting = function() {
            if(!interval) {
              interval = setInterval(function() {
                console.log('trying reconnect...');
                _connect(url);
              }, 3000);
            }
          };
          var stopPing = function() {
            if(pingInterval) {
              clearInterval(pingInterval);
              pingInterval = null;
            }
          };

          connection.onopen = function() {
            localRuntime.notify(opened.id, true);
            callback && callback(Task.succeed(Utils.Tuple0));
            if(interval) {
              clearInterval(interval);
              interval = null;
            }
            pingInterval = setInterval(function() {
              // console.log('ping');
              connection.send('Ping');
              pingFlag = true;
              setTimeout(function() {
                if(pingFlag) {
                  pingFlag = false;
                  stopPing();
                  connection = null;
                  tryStartReconnecting();
                }
              }, 3 * 1000);
            }, 1 * 60 * 1000);
          };
          connection.onclose = function() {
            localRuntime.notify(opened.id, false);
            stopPing();
            connection = null;
            tryStartReconnecting();
          };
          connection.onmessage = function(e) {
            if(e.data === 'Pong') {
              // console.log('pong');
              pingFlag = false;
            } else {
              localRuntime.notify(message.id, e.data);
            }
          };
        } catch(e) {
          console.log(e);
          // localRuntime.notify(opened.id, false);
        }
      }
    };

    var send = function(s) {
      return Task.asyncFunction(function(callback) {
        // console.log('send:' + s);
        if(connection) {
          connection.send(s);
          return callback(Task.succeed(Utils.Tuple0));
        } else {
          return callback(Task.fail("send failed."));
        }
      });
    };

    return localRuntime.Native.WebSocket.values = {
      connect: connect,
      message: message,
      opened: opened,
      send: send
    };
};
<commit_msg>frequent ping<commit_after>
Elm.Native.WebSocket = {};

Elm.Native.WebSocket.make = function(localRuntime) {
    localRuntime.Native = localRuntime.Native || {};
    localRuntime.Native.WebSocket = localRuntime.Native.WebSocket || {};
    if (localRuntime.Native.WebSocket.values) return localRuntime.Native.WebSocket.values;

    var Task = Elm.Native.Task.make(localRuntime);
    var Utils = Elm.Native.Utils.make(localRuntime);
    var NS = Elm.Native.Signal.make(localRuntime);

    var message = NS.input('WebSocket.message', '');
    var opened = NS.input('WebSocket.opened', false);

    var connection = null;
    var interval = null;
    var pingInterval = null;
    var pingFlag = false;
    var connect = function(url) {
      return Task.asyncFunction(function(callback) {
        _connect(url, callback);
      });
    };
    var _connect = function(url, callback) {
      if(!connection) {
        try {
          connection = new WebSocket(url, ['soap', 'xmpp']);
          var tryStartReconnecting = function() {
            if(!interval) {
              interval = setInterval(function() {
                console.log('trying reconnect...');
                _connect(url);
              }, 3000);
            }
          };
          var stopPing = function() {
            if(pingInterval) {
              clearInterval(pingInterval);
              pingInterval = null;
            }
          };

          connection.onopen = function() {
            localRuntime.notify(opened.id, true);
            callback && callback(Task.succeed(Utils.Tuple0));
            if(interval) {
              clearInterval(interval);
              interval = null;
            }
            pingInterval = setInterval(function() {
              // console.log('ping');
              connection.send('Ping');
              pingFlag = true;
              setTimeout(function() {
                if(pingFlag) {
                  pingFlag = false;
                  stopPing();
                  connection = null;
                  tryStartReconnecting();
                }
              }, 3 * 1000);
            }, 1 * 10 * 1000);
          };
          connection.onclose = function() {
            localRuntime.notify(opened.id, false);
            stopPing();
            connection = null;
            tryStartReconnecting();
          };
          connection.onmessage = function(e) {
            if(e.data === 'Pong') {
              // console.log('pong');
              pingFlag = false;
            } else {
              localRuntime.notify(message.id, e.data);
            }
          };
        } catch(e) {
          console.log(e);
          // localRuntime.notify(opened.id, false);
        }
      }
    };

    var send = function(s) {
      return Task.asyncFunction(function(callback) {
        // console.log('send:' + s);
        if(connection) {
          connection.send(s);
          return callback(Task.succeed(Utils.Tuple0));
        } else {
          return callback(Task.fail("send failed."));
        }
      });
    };

    return localRuntime.Native.WebSocket.values = {
      connect: connect,
      message: message,
      opened: opened,
      send: send
    };
};
<|endoftext|>


<commit_before>var path = require('path');
var url = require('url');
var fs = require('fs');
var mapnik = require('mapnik');
var Step = require('step');
var millstone = require('millstone');

models.Datasource.prototype.sync = function(method, model, success, error) {
    if (method !== 'read') return error('Method not supported.');

    var options = model.options;
    var config = Bones.plugin.config;

    if (!options) return error(new Error('options are required.'));
    if (!options.id) return error(new Error('id is required.'));
    if (!options.project) return error(new Error('project is required.'));
    if (options.file) {
        options.file = options.file.trim().replace(/^~/, process.env.HOME);
    }

    millstone.resolve({
        mml: {
            Stylesheet: [{ id: 'layer', data: '' }],
            Layer: [{
                name: options.id,
                srs: options.srs || '',
                Datasource: options
            }]
        },
        base: path.join(config.files, 'project', options.project),
        cache: path.join(config.files, 'cache')
    }, function(err, mml) {
        if (err) return error(err);

        try {
            mml.Layer[0].Datasource = _(mml.Layer[0].Datasource).defaults(options);

            // Some mapnik datasources accept 'row_limit` (like postgis, shape)
            // those that do not will be restricted during the featureset loop below
            var row_limit = 500;
            mml.Layer[0].Datasource = _(mml.Layer[0].Datasource).defaults({
                row_limit: row_limit
                });

            // simplistic validation that subselects have the key_field string present
            // not a proper parser, but this is not the right place to be parsing SQL
            // https://github.com/mapbox/tilemill/issues/1509
            if (mml.Layer[0].Datasource.table !== undefined
                && mml.Layer[0].Datasource.key_field !== undefined
                && mml.Layer[0].Datasource.table.match(/select /i)
                && mml.Layer[0].Datasource.table.indexOf('*') == -1
                && mml.Layer[0].Datasource.table.search(mml.Layer[0].Datasource.key_field) == -1) {
                    return error(new Error("Your SQL subquery needs to explicitly include the custom key_field: '" + mml.Layer[0].Datasource.key_field + "' or use 'select *' to request all fields"));
            }

            var source = new mapnik.Datasource(mml.Layer[0].Datasource);

            var features = [];
            if (!(source.type == "raster") && (options.features || options.info)) {
                var featureset = source.featureset();
                for (var i = 0, feat;
                    i < row_limit && (feat = featureset.next(true));
                    i++) {
                    features.push(feat.attributes());
                }
            }

            // Convert datasource extent to lon/lat when saving
            var layerProj = new mapnik.Projection(mml.Layer[0].srs),
                unProj = new mapnik.Projection('+proj=longlat +ellps=WGS84 +no_defs'),
                trans = new mapnik.ProjTransform(layerProj, unProj),
                unproj_extent = trans.forward(source.extent());
            // clamp to valid unproj_extents
            (unproj_extent[0] < -180) && (unproj_extent[0] = -180);
            (unproj_extent[1] < -85.051) && (unproj_extent[1] = -85.051);
            (unproj_extent[2] > 180) && (unproj_extent[2] = 180);
            (unproj_extent[3] > 85.051) && (unproj_extent[3] = 85.051);

            if (unproj_extent[2] < unproj_extent[0] || unproj_extent[3] < unproj_extent[1]) {
                throw new Error("Extent out of bounds. Check SRS for layer '" + options.id + "'.");
            }

            var desc = source.describe();
            var datasource = {
                id: options.id,
                project: options.project,
                url: options.file,
                fields: desc.fields,
                features: options.features ? features : [],
                type: desc.type,
                geometry_type: desc.type === 'raster' ? 'raster' : desc.geometry_type,
                unproj_extent: unproj_extent,
                extent: source.extent().join(',')
            };

            // Process fields and calculate min/max values.
            for (var f in datasource.fields) {
                var values = _(features).pluck(f);
                var type = datasource.fields[f];
                datasource.fields[f] = { type: type };
                if (options.features || options.info) {
                    datasource.fields[f].max = type === 'String'
                        ? (function() {
                            var val = _(values).max(function(v) { return (v||'').length });
                            return (val && val.length > 55) ?
                              val.slice(0, 55 - 3) + '...' : val;
                        })()
                        : _(values).max();
                    datasource.fields[f].min = type === 'String'
                        ? _(values).min(function(v) { return (v||'').length })
                        : _(values).min();
                }
            }
        } catch(err) {
            return error(err);
        }

        success(datasource);
    });
};
<commit_msg>provide more details if we detect an invalid geographic extent - refs #1670<commit_after>var path = require('path');
var url = require('url');
var fs = require('fs');
var mapnik = require('mapnik');
var Step = require('step');
var millstone = require('millstone');

models.Datasource.prototype.sync = function(method, model, success, error) {
    if (method !== 'read') return error('Method not supported.');

    var options = model.options;
    var config = Bones.plugin.config;

    if (!options) return error(new Error('options are required.'));
    if (!options.id) return error(new Error('id is required.'));
    if (!options.project) return error(new Error('project is required.'));
    if (options.file) {
        options.file = options.file.trim().replace(/^~/, process.env.HOME);
    }

    millstone.resolve({
        mml: {
            Stylesheet: [{ id: 'layer', data: '' }],
            Layer: [{
                name: options.id,
                srs: options.srs || '',
                Datasource: options
            }]
        },
        base: path.join(config.files, 'project', options.project),
        cache: path.join(config.files, 'cache')
    }, function(err, mml) {
        if (err) return error(err);

        try {
            mml.Layer[0].Datasource = _(mml.Layer[0].Datasource).defaults(options);

            // Some mapnik datasources accept 'row_limit` (like postgis, shape)
            // those that do not will be restricted during the featureset loop below
            var row_limit = 500;
            mml.Layer[0].Datasource = _(mml.Layer[0].Datasource).defaults({
                row_limit: row_limit
                });

            // simplistic validation that subselects have the key_field string present
            // not a proper parser, but this is not the right place to be parsing SQL
            // https://github.com/mapbox/tilemill/issues/1509
            if (mml.Layer[0].Datasource.table !== undefined
                && mml.Layer[0].Datasource.key_field !== undefined
                && mml.Layer[0].Datasource.table.match(/select /i)
                && mml.Layer[0].Datasource.table.indexOf('*') == -1
                && mml.Layer[0].Datasource.table.search(mml.Layer[0].Datasource.key_field) == -1) {
                    return error(new Error("Your SQL subquery needs to explicitly include the custom key_field: '" + mml.Layer[0].Datasource.key_field + "' or use 'select *' to request all fields"));
            }

            var source = new mapnik.Datasource(mml.Layer[0].Datasource);

            var features = [];
            if (!(source.type == "raster") && (options.features || options.info)) {
                var featureset = source.featureset();
                for (var i = 0, feat;
                    i < row_limit && (feat = featureset.next(true));
                    i++) {
                    features.push(feat.attributes());
                }
            }

            // Convert datasource extent to lon/lat when saving
            var layerProj = new mapnik.Projection(mml.Layer[0].srs),
                unProj = new mapnik.Projection('+proj=longlat +ellps=WGS84 +no_defs'),
                trans = new mapnik.ProjTransform(layerProj, unProj),
                unproj_extent = trans.forward(source.extent());
            // clamp to valid unproj_extents
            (unproj_extent[0] < -180) && (unproj_extent[0] = -180);
            (unproj_extent[1] < -85.051) && (unproj_extent[1] = -85.051);
            (unproj_extent[2] > 180) && (unproj_extent[2] = 180);
            (unproj_extent[3] > 85.051) && (unproj_extent[3] = 85.051);

            if (unproj_extent[2] < unproj_extent[0] || unproj_extent[3] < unproj_extent[1]) {
                throw new Error("Detected out of bounds geographic extent (" + unproj_extent + ") for layer '" + options.id + "'. Please ensure that the SRS for this layer is correct. Its native extent is '" + source.extent() + "'");
            }

            var desc = source.describe();
            var datasource = {
                id: options.id,
                project: options.project,
                url: options.file,
                fields: desc.fields,
                features: options.features ? features : [],
                type: desc.type,
                geometry_type: desc.type === 'raster' ? 'raster' : desc.geometry_type,
                unproj_extent: unproj_extent,
                extent: source.extent().join(',')
            };

            // Process fields and calculate min/max values.
            for (var f in datasource.fields) {
                var values = _(features).pluck(f);
                var type = datasource.fields[f];
                datasource.fields[f] = { type: type };
                if (options.features || options.info) {
                    datasource.fields[f].max = type === 'String'
                        ? (function() {
                            var val = _(values).max(function(v) { return (v||'').length });
                            return (val && val.length > 55) ?
                              val.slice(0, 55 - 3) + '...' : val;
                        })()
                        : _(values).max();
                    datasource.fields[f].min = type === 'String'
                        ? _(values).min(function(v) { return (v||'').length })
                        : _(values).min();
                }
            }
        } catch(err) {
            return error(err);
        }

        success(datasource);
    });
};
<|endoftext|>


<commit_before>'use strict';

var HTMLImporter = require('substance/model/HTMLImporter');
var noteSchema = require('../note/noteSchema');
var Note = require('../note/Note');

var converters = [
  require('substance/packages/paragraph/ParagraphHTMLConverter'),
  require('substance/packages/blockquote/BlockquoteHTMLConverter'),
  require('substance/packages/codeblock/CodeblockHTMLConverter'),
  require('substance/packages/heading/HeadingHTMLConverter'),
  require('substance/packages/strong/StrongHTMLConverter'),
  require('substance/packages/emphasis/EmphasisHTMLConverter'),
  require('substance/packages/link/LinkHTMLConverter'),
  require('./MarkHTMLConverter'),
  require('./TodoHTMLConverter')
];

function NoteImporter() {
  NoteImporter.super.call(this, {
    schema: noteSchema,
    converters: converters,
    DocumentClass: Note,
    containerId: 'body'
  });
}

NoteImporter.Prototype = function() {
  this.convertDocument = function(elements) {
    this.convertContainer(elements, this.config.containerId);
  };
};

// Expose converters so we can reuse them in NoteHtmlExporter
NoteImporter.converters = converters;

HTMLImporter.extend(NoteImporter);
module.exports = NoteImporter;<commit_msg>Cleanup NoteImporter.<commit_after>'use strict';

var HTMLImporter = require('substance/model/HTMLImporter');
var noteSchema = require('../note/noteSchema');
var Note = require('../note/Note');

var converters = [
  require('substance/packages/paragraph/ParagraphHTMLConverter'),
  require('substance/packages/blockquote/BlockquoteHTMLConverter'),
  require('substance/packages/codeblock/CodeblockHTMLConverter'),
  require('substance/packages/heading/HeadingHTMLConverter'),
  require('substance/packages/strong/StrongHTMLConverter'),
  require('substance/packages/emphasis/EmphasisHTMLConverter'),
  require('substance/packages/link/LinkHTMLConverter'),
  require('./MarkHTMLConverter'),
  require('./TodoHTMLConverter')
];

function NoteImporter() {
  NoteImporter.super.call(this, {
    schema: noteSchema,
    converters: converters,
    DocumentClass: Note
  });
}

NoteImporter.Prototype = function() {
  this.convertDocument = function(els) {
    this.convertContainer(els, 'body');
  };
};

// Expose converters so we can reuse them in NoteHtmlExporter
NoteImporter.converters = converters;

HTMLImporter.extend(NoteImporter);
module.exports = NoteImporter;<|endoftext|>