<commit_before>/*global FormplayerFrontend, Util */

FormplayerFrontend.module("SessionNavigate.MenuList", function (MenuList, FormplayerFrontend, Backbone, Marionette) {
    MenuList.MenuView = Marionette.ItemView.extend({
        tagName: "tr",
        className: "formplayer-request",
        events: {
            "click": "rowClick",
            "click .js-module-audio-play": "audioPlay",
            "click .js-module-audio-pause": "audioPause",
        },

        getTemplate: function () {
            if (this.model.get('audioUri')) {
                return "#menu-view-item-audio-template";
            } else {
                return "#menu-view-item-template";
            }
        },

        rowClick: function (e) {
            e.preventDefault();
            if (!($(e.originalEvent.srcElement).hasClass('js-module-audio-icon')
                || $(e.originalEvent.srcElement).hasClass('js-module-audio-play')
                || $(e.originalEvent.srcElement).hasClass('js-module-audio-pause'))
            ) {
                var model = this.model;
                FormplayerFrontend.trigger("menu:select", model.get('index'));
            }
        },
        audioPlay: function (e) {
            e.preventDefault();
            var $playBtn = $(e.originalEvent.srcElement).closest('.js-module-audio-play');
            var $pauseBtn = $playBtn.parent().find('.js-module-audio-pause');
            $pauseBtn.removeClass('hide');
            $playBtn.addClass('hide');
            var $audioElem = $playBtn.parent().find('.js-module-audio');
            if ($audioElem.data('isFirstPlay') !== 'yes') {
                $audioElem.data('isFirstPlay', 'yes');
                $audioElem.one('ended', function () {
                    $playBtn.removeClass('hide');
                    $pauseBtn.addClass('hide');
                    $audioElem.data('isFirstPlay', 'no');
                });
            }
            $audioElem.get(0).play();
        },
        audioPause: function (e) {
            e.preventDefault();
            var $pauseBtn = $(e.originalEvent.srcElement).closest('.js-module-audio-pause');
            $pauseBtn.parent().find('.js-module-audio-play').removeClass('hide');
            $pauseBtn.addClass('hide');
            $pauseBtn.parent().find('.js-module-audio').get(0).pause();
        },
        templateHelpers: function () {
            var imageUri = this.options.model.get('imageUri');
            var audioUri = this.options.model.get('audioUri');
            var navState = this.options.model.get('navigationState');
            var appId = Util.currentUrlToObject().appId;
            return {
                navState: navState,
                imageUrl: imageUri ? FormplayerFrontend.request('resourceMap', imageUri, appId) : "",
                audioUrl: audioUri ? FormplayerFrontend.request('resourceMap', audioUri, appId) : "",
            };
        },
    });

    MenuList.MenuListView = Marionette.CompositeView.extend({
        tagName: "div",
        template: "#menu-view-list-template",
        childView: MenuList.MenuView,
        childViewContainer: "tbody",
        templateHelpers: function () {
            return {
                title: this.options.title,
            };
        },
        childViewOptions: function () {
            return {
                sessionId: this.options.sessionId,
            };
        },
    });

    MenuList.CaseView = Marionette.ItemView.extend({
        tagName: "tr",
        template: "#case-view-item-template",

        className: "formplayer-request",

        events: {
            "click": "rowClick",
        },

        rowClick: function (e) {
            e.preventDefault();
            FormplayerFrontend.trigger("menu:show:detail", this, 0);
        },

        templateHelpers: function () {
            var appId = Util.currentUrlToObject().appId;
            return {
                data: this.options.model.get('data'),
                styles: this.options.styles,
                resolveUri: function (uri) {
                    return FormplayerFrontend.request('resourceMap', uri, appId);
                },
            };
        },
    });

    // return the string grid-area attribute
    // takes the form of  [x-coord] / [y-Coord] / [width] / [height]
    var getGridAttributes = function (tile) {
        if (!tile) {
            return null;
        }
        var rowStart = tile.gridY + 1;
        var colStart = tile.gridX + 1;
        var rowEnd = rowStart + tile.gridHeight;
        var colEnd = colStart + tile.gridWidth;

        return rowStart + " / " + colStart + " / " +
            rowEnd + " / " + colEnd;
    };
    // generate the case tile's style block and insert
    var generateCaseTileStyles = function (tiles) {
        var templateString,
            caseTileStyle,
            caseTileStyleTemplate,
            tileModels;

        tileModels = _.chain(tiles || [])
            .map(function (tile, idx) {
                if (tile === null || tile === undefined) {
                    return null;
                }
                return {
                    id: 'grid-style-' + idx,
                    gridStyle: getGridAttributes(tile),
                    fontStyle: tile.fontSize,
                };
            })
            .filter(function (tile) {
                return tile !== null;
            }).value();

        templateString = $("#case-tile-style-template").html();
        caseTileStyleTemplate = _.template(templateString);
        caseTileStyle = caseTileStyleTemplate({
            models: tileModels,
        });

        // need to remove this attribute so the grid style is re-evaluated
        $("#case-tiles-style").html(caseTileStyle).removeAttr("data-css-polyfilled");
        $("#inner-tiles-container-style").removeAttr("data-css-polyfilled");
    };

    var makeOuterGridStyle = function (numRows, numColumns, numCasesPerRow) {
        var outerGridTemplateString,
            outerGridStyle,
            outerGridStyleTemplate,
            outerGridModel;

        var widthPercentage = 100 / numCasesPerRow;
        var widthHeightRatio = numRows / numColumns;
        var heightPercentage = widthPercentage * widthHeightRatio;

        outerGridModel = {
            widthPercentage: widthPercentage,
            heightPercentage: heightPercentage,
        };
        outerGridTemplateString = $("#case-grid-style-template").html();
        outerGridStyleTemplate = _.template(outerGridTemplateString);
        outerGridStyle = outerGridStyleTemplate({
            model: outerGridModel,
        });
        // need to remove this attribute so the grid style is re-evaluated
        $("#outer-tiles-container-style").html(outerGridStyle).removeAttr("data-css-polyfilled");
    };

    var makeInnerGridStyle = function (numRows, numColumns, numCasesPerRow) {
        var templateString,
            view,
            template,
            model;

        var widthPercentage = 100 / numColumns;
        var widthHeightRatio = numRows / numColumns;
        var heightPercentage = widthPercentage * widthHeightRatio;

        model = {
            numRows: numRows,
            numColumns: numColumns,
            widthPercentage: widthPercentage,
            heightPercentage: heightPercentage,
        };
        templateString = $("#grid-inner-style-template").html();
        template = _.template(templateString);
        view = template({
            model: model,
        });
        // need to remove this attribute so the grid style is re-evaluated
        $("#inner-tiles-container-style").html(view).removeAttr("data-css-polyfilled");

        if (numCasesPerRow > 1) {
            makeOuterGridStyle(numRows, numColumns, numCasesPerRow);
        }
    };

    MenuList.CaseTileView = MenuList.CaseView.extend({
        tagName: "tr",
        template: "#case-tile-view-item-template",
        className: "formplayer-request",

        templateHelpers: function () {
            var appId = Util.currentUrlToObject().appId;
            return {
                data: this.options.model.get('data'),
                styles: this.options.styles,
                tiles: this.options.tiles,
                resolveUri: function (uri) {
                    return FormplayerFrontend.request('resourceMap', uri, appId);
                },
            };
        },
    });

    MenuList.CaseListView = Marionette.CompositeView.extend({
        tagName: "div",
        template: "#case-view-list-template",
        childViewContainer: ".case-container",
        childView: MenuList.CaseView,

        initialize: function (options) {
            this.styles = options.styles;
        },

        childViewOptions: function () {
            return {
                styles: this.options.styles,
            };
        },

        ui: {
            actionButton: '#double-management',
            searchButton: '#case-list-search-button',
            paginators: '.page-link',
        },

        events: {
            'click @ui.actionButton': 'caseListAction',
            'click @ui.searchButton': 'caseListSearch',
            'click @ui.paginators': 'paginateAction',
        },

        caseListAction: function () {
            FormplayerFrontend.trigger("menu:select", "action 0");
        },

        caseListSearch: function (e) {
            e.preventDefault();
            var searchText = $('#searchText').val();
            FormplayerFrontend.trigger("menu:search", searchText);
        },

        paginateAction: function (e) {
            var pageSelection = $(e.currentTarget).data("id");
            FormplayerFrontend.trigger("menu:paginate", pageSelection);
        },

        templateHelpers: function () {
            return {
                title: this.options.title,
                headers: this.options.headers,
                widthHints: this.options.widthHints,
                action: this.options.action,
                currentPage: this.options.currentPage,
                pageCount: this.options.pageCount,
                styles: this.options.styles,
                breadcrumbs: this.options.breadcrumbs,
            };
        },
    });

    MenuList.CaseTileListView = MenuList.CaseListView.extend({
        template: "#case-view-tile-list-template",
        childView: MenuList.CaseTileView,
        initialize: function (options) {
            this.styles = options.styles;
            this.tiles = options.tiles;
            this.numCases = options.collection.length;
            this.numEntitiesPerRow = options.numEntitiesPerRow;
            this.maxWidth = options.maxWidth;
            this.maxHeight = options.maxHeight;
            generateCaseTileStyles(options.tiles);
            makeInnerGridStyle(options.maxHeight, options.maxWidth, options.numEntitiesPerRow);
        },

        childViewOptions: function () {
            return {
                styles: this.options.styles,
                tiles: this.options.tiles,
            };
        },

        templateHelpers: function () {
            return {
                title: this.options.title,
                action: this.options.action,
                currentPage: this.options.currentPage,
                pageCount: this.options.pageCount,
                styles: this.options.styles,
                tiles: this.options.tiles,
                breadcrumbs: this.options.breadcrumbs,
                numEntitiesPerRow: this.options.numEntitiesPerRow,
                numCases: this.options.numCases,
            };
        },
    });

    MenuList.GridCaseTileViewItem = MenuList.CaseTileView.extend({
        tagName: "div",
        className: "formplayer-request case-tile-grid-item",
        template: "#case-tile-grid-view-item-template",
    });

    MenuList.GridCaseTileListView = MenuList.CaseTileListView.extend({
        childView: MenuList.GridCaseTileViewItem,
    });

    MenuList.BreadcrumbView = Marionette.ItemView.extend({
        tagName: "li",
        template: "#breadcrumb-item-template",
        className: "breadcrumb-text",
        events: {
            "click": "crumbClick",
        },

        crumbClick: function (e) {
            e.preventDefault();
            var crumbId = this.options.model.get('id');
            FormplayerFrontend.trigger("breadcrumbSelect", crumbId);
        },
    });

    MenuList.BreadcrumbListView = Marionette.CompositeView.extend({
        tagName: "div",
        template: "#breadcrumb-list-template",
        childView: MenuList.BreadcrumbView,
        childViewContainer: "ol",
    });

    MenuList.DetailView = Marionette.ItemView.extend({
        tagName: "tr",
        className: "row",
        template: "#detail-view-item-template",
    });

    MenuList.DetailListView = Marionette.CompositeView.extend({
        tagName: "table",
        className: "table table-hover table-striped table-bordered",
        template: "#detail-view-list-template",
        childView: MenuList.DetailView,
        childViewContainer: "tbody",
    });

    MenuList.DetailTabView = Marionette.ItemView.extend({
        tagName: "li",
        template: "#detail-view-tab-item-template",
        events: {
            "click": "tabClick",
        },
        initialize: function (options) {
            this.index = options.model.get('id');
            this.showDetail = options.showDetail;
        },
        tabClick: function (e) {
            e.preventDefault();
            this.options.showDetail(this.index);
        },
    });

    MenuList.DetailTabListView = Marionette.CompositeView.extend({
        tagName: "div",
        template: "#detail-view-tab-list-template",
        childView: MenuList.DetailTabView,
        childViewContainer: "ul",
        childViewOptions: function () {
            return {
                showDetail: this.options.showDetail,
            };
        },
    });
})
;
<commit_msg>remove unneeded tiles param<commit_after>/*global FormplayerFrontend, Util */

FormplayerFrontend.module("SessionNavigate.MenuList", function (MenuList, FormplayerFrontend, Backbone, Marionette) {
    MenuList.MenuView = Marionette.ItemView.extend({
        tagName: "tr",
        className: "formplayer-request",
        events: {
            "click": "rowClick",
            "click .js-module-audio-play": "audioPlay",
            "click .js-module-audio-pause": "audioPause",
        },

        getTemplate: function () {
            if (this.model.get('audioUri')) {
                return "#menu-view-item-audio-template";
            } else {
                return "#menu-view-item-template";
            }
        },

        rowClick: function (e) {
            e.preventDefault();
            if (!($(e.originalEvent.srcElement).hasClass('js-module-audio-icon')
                || $(e.originalEvent.srcElement).hasClass('js-module-audio-play')
                || $(e.originalEvent.srcElement).hasClass('js-module-audio-pause'))
            ) {
                var model = this.model;
                FormplayerFrontend.trigger("menu:select", model.get('index'));
            }
        },
        audioPlay: function (e) {
            e.preventDefault();
            var $playBtn = $(e.originalEvent.srcElement).closest('.js-module-audio-play');
            var $pauseBtn = $playBtn.parent().find('.js-module-audio-pause');
            $pauseBtn.removeClass('hide');
            $playBtn.addClass('hide');
            var $audioElem = $playBtn.parent().find('.js-module-audio');
            if ($audioElem.data('isFirstPlay') !== 'yes') {
                $audioElem.data('isFirstPlay', 'yes');
                $audioElem.one('ended', function () {
                    $playBtn.removeClass('hide');
                    $pauseBtn.addClass('hide');
                    $audioElem.data('isFirstPlay', 'no');
                });
            }
            $audioElem.get(0).play();
        },
        audioPause: function (e) {
            e.preventDefault();
            var $pauseBtn = $(e.originalEvent.srcElement).closest('.js-module-audio-pause');
            $pauseBtn.parent().find('.js-module-audio-play').removeClass('hide');
            $pauseBtn.addClass('hide');
            $pauseBtn.parent().find('.js-module-audio').get(0).pause();
        },
        templateHelpers: function () {
            var imageUri = this.options.model.get('imageUri');
            var audioUri = this.options.model.get('audioUri');
            var navState = this.options.model.get('navigationState');
            var appId = Util.currentUrlToObject().appId;
            return {
                navState: navState,
                imageUrl: imageUri ? FormplayerFrontend.request('resourceMap', imageUri, appId) : "",
                audioUrl: audioUri ? FormplayerFrontend.request('resourceMap', audioUri, appId) : "",
            };
        },
    });

    MenuList.MenuListView = Marionette.CompositeView.extend({
        tagName: "div",
        template: "#menu-view-list-template",
        childView: MenuList.MenuView,
        childViewContainer: "tbody",
        templateHelpers: function () {
            return {
                title: this.options.title,
            };
        },
        childViewOptions: function () {
            return {
                sessionId: this.options.sessionId,
            };
        },
    });

    MenuList.CaseView = Marionette.ItemView.extend({
        tagName: "tr",
        template: "#case-view-item-template",

        className: "formplayer-request",

        events: {
            "click": "rowClick",
        },

        rowClick: function (e) {
            e.preventDefault();
            FormplayerFrontend.trigger("menu:show:detail", this, 0);
        },

        templateHelpers: function () {
            var appId = Util.currentUrlToObject().appId;
            return {
                data: this.options.model.get('data'),
                styles: this.options.styles,
                resolveUri: function (uri) {
                    return FormplayerFrontend.request('resourceMap', uri, appId);
                },
            };
        },
    });

    // return the string grid-area attribute
    // takes the form of  [x-coord] / [y-Coord] / [width] / [height]
    var getGridAttributes = function (tile) {
        if (!tile) {
            return null;
        }
        var rowStart = tile.gridY + 1;
        var colStart = tile.gridX + 1;
        var rowEnd = rowStart + tile.gridHeight;
        var colEnd = colStart + tile.gridWidth;

        return rowStart + " / " + colStart + " / " +
            rowEnd + " / " + colEnd;
    };
    // generate the case tile's style block and insert
    var generateCaseTileStyles = function (tiles) {
        var templateString,
            caseTileStyle,
            caseTileStyleTemplate,
            tileModels;

        tileModels = _.chain(tiles || [])
            .map(function (tile, idx) {
                if (tile === null || tile === undefined) {
                    return null;
                }
                return {
                    id: 'grid-style-' + idx,
                    gridStyle: getGridAttributes(tile),
                    fontStyle: tile.fontSize,
                };
            })
            .filter(function (tile) {
                return tile !== null;
            }).value();

        templateString = $("#case-tile-style-template").html();
        caseTileStyleTemplate = _.template(templateString);
        caseTileStyle = caseTileStyleTemplate({
            models: tileModels,
        });

        // need to remove this attribute so the grid style is re-evaluated
        $("#case-tiles-style").html(caseTileStyle).removeAttr("data-css-polyfilled");
        $("#inner-tiles-container-style").removeAttr("data-css-polyfilled");
    };

    // Dynamically generate the CSS style to display multiple tiles per line
    var makeOuterGridStyle = function (numRows, numColumns, numCasesPerRow) {
        var outerGridTemplateString,
            outerGridStyle,
            outerGridStyleTemplate,
            outerGridModel;

        var widthPercentage = 100 / numCasesPerRow;
        var widthHeightRatio = numRows / numColumns;
        var heightPercentage = widthPercentage * widthHeightRatio;

        outerGridModel = {
            widthPercentage: widthPercentage,
            heightPercentage: heightPercentage,
        };
        outerGridTemplateString = $("#case-grid-style-template").html();
        outerGridStyleTemplate = _.template(outerGridTemplateString);
        outerGridStyle = outerGridStyleTemplate({
            model: outerGridModel,
        });
        // need to remove this attribute so the grid style is re-evaluated
        $("#outer-tiles-container-style").html(outerGridStyle).removeAttr("data-css-polyfilled");
    };

    // Dynamically generate the CSS style for the grid polyfill to use for the case tile
    var makeInnerGridStyle = function (numRows, numColumns, numCasesPerRow) {
        var templateString,
            view,
            template,
            model;

        var widthPercentage = 100 / numColumns;
        var widthHeightRatio = numRows / numColumns;
        var heightPercentage = widthPercentage * widthHeightRatio;

        model = {
            numRows: numRows,
            numColumns: numColumns,
            widthPercentage: widthPercentage,
            heightPercentage: heightPercentage,
        };
        templateString = $("#grid-inner-style-template").html();
        template = _.template(templateString);
        view = template({
            model: model,
        });
        // need to remove this attribute so the grid style is re-evaluated
        $("#inner-tiles-container-style").html(view).removeAttr("data-css-polyfilled");

        // If we have multiple cases per line, need to generate the outer grid style as well
        if (numCasesPerRow > 1) {
            makeOuterGridStyle(numRows, numColumns, numCasesPerRow);
        }
    };

    MenuList.CaseTileView = MenuList.CaseView.extend({
        tagName: "tr",
        template: "#case-tile-view-item-template",
        className: "formplayer-request",

        templateHelpers: function () {
            var appId = Util.currentUrlToObject().appId;
            return {
                data: this.options.model.get('data'),
                styles: this.options.styles,
                resolveUri: function (uri) {
                    return FormplayerFrontend.request('resourceMap', uri, appId);
                },
            };
        },
    });

    MenuList.CaseListView = Marionette.CompositeView.extend({
        tagName: "div",
        template: "#case-view-list-template",
        childViewContainer: ".case-container",
        childView: MenuList.CaseView,

        initialize: function (options) {
            this.styles = options.styles;
        },

        childViewOptions: function () {
            return {
                styles: this.options.styles,
            };
        },

        ui: {
            actionButton: '#double-management',
            searchButton: '#case-list-search-button',
            paginators: '.page-link',
        },

        events: {
            'click @ui.actionButton': 'caseListAction',
            'click @ui.searchButton': 'caseListSearch',
            'click @ui.paginators': 'paginateAction',
        },

        caseListAction: function () {
            FormplayerFrontend.trigger("menu:select", "action 0");
        },

        caseListSearch: function (e) {
            e.preventDefault();
            var searchText = $('#searchText').val();
            FormplayerFrontend.trigger("menu:search", searchText);
        },

        paginateAction: function (e) {
            var pageSelection = $(e.currentTarget).data("id");
            FormplayerFrontend.trigger("menu:paginate", pageSelection);
        },

        templateHelpers: function () {
            return {
                title: this.options.title,
                headers: this.options.headers,
                widthHints: this.options.widthHints,
                action: this.options.action,
                currentPage: this.options.currentPage,
                pageCount: this.options.pageCount,
                styles: this.options.styles,
                breadcrumbs: this.options.breadcrumbs,
            };
        },
    });

    MenuList.CaseTileListView = MenuList.CaseListView.extend({
        template: "#case-view-tile-list-template",
        childView: MenuList.CaseTileView,
        initialize: function (options) {
            this.styles = options.styles;
            this.numCases = options.collection.length;
            this.numEntitiesPerRow = options.numEntitiesPerRow;
            this.maxWidth = options.maxWidth;
            this.maxHeight = options.maxHeight;
            generateCaseTileStyles(options.tiles);
            makeInnerGridStyle(options.maxHeight, options.maxWidth, options.numEntitiesPerRow);
        },

        childViewOptions: function () {
            return {
                styles: this.options.styles,
            };
        },

        templateHelpers: function () {
            return {
                title: this.options.title,
                action: this.options.action,
                currentPage: this.options.currentPage,
                pageCount: this.options.pageCount,
                styles: this.options.styles,
                breadcrumbs: this.options.breadcrumbs,
                numEntitiesPerRow: this.options.numEntitiesPerRow,
                numCases: this.options.numCases,
            };
        },
    });

    MenuList.GridCaseTileViewItem = MenuList.CaseTileView.extend({
        tagName: "div",
        className: "formplayer-request case-tile-grid-item",
        template: "#case-tile-grid-view-item-template",
    });

    MenuList.GridCaseTileListView = MenuList.CaseTileListView.extend({
        childView: MenuList.GridCaseTileViewItem,
    });

    MenuList.BreadcrumbView = Marionette.ItemView.extend({
        tagName: "li",
        template: "#breadcrumb-item-template",
        className: "breadcrumb-text",
        events: {
            "click": "crumbClick",
        },

        crumbClick: function (e) {
            e.preventDefault();
            var crumbId = this.options.model.get('id');
            FormplayerFrontend.trigger("breadcrumbSelect", crumbId);
        },
    });

    MenuList.BreadcrumbListView = Marionette.CompositeView.extend({
        tagName: "div",
        template: "#breadcrumb-list-template",
        childView: MenuList.BreadcrumbView,
        childViewContainer: "ol",
    });

    MenuList.DetailView = Marionette.ItemView.extend({
        tagName: "tr",
        className: "row",
        template: "#detail-view-item-template",
    });

    MenuList.DetailListView = Marionette.CompositeView.extend({
        tagName: "table",
        className: "table table-hover table-striped table-bordered",
        template: "#detail-view-list-template",
        childView: MenuList.DetailView,
        childViewContainer: "tbody",
    });

    MenuList.DetailTabView = Marionette.ItemView.extend({
        tagName: "li",
        template: "#detail-view-tab-item-template",
        events: {
            "click": "tabClick",
        },
        initialize: function (options) {
            this.index = options.model.get('id');
            this.showDetail = options.showDetail;
        },
        tabClick: function (e) {
            e.preventDefault();
            this.options.showDetail(this.index);
        },
    });

    MenuList.DetailTabListView = Marionette.CompositeView.extend({
        tagName: "div",
        template: "#detail-view-tab-list-template",
        childView: MenuList.DetailTabView,
        childViewContainer: "ul",
        childViewOptions: function () {
            return {
                showDetail: this.options.showDetail,
            };
        },
    });
})
;
<|endoftext|>


<commit_before>module('lively.ide.codeeditor.DocumentChange').requires().toRun(function() {

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// how to deal with document changes
// this class connects the lively.morphic.CodeEditor with the change handlers
// out there
Object.subclass('lively.ide.CodeEditor.DocumentChangeHandler',
'initialize', {
    initialize: function(plugins) {
        this.plugins = plugins;
        this.onDocumentChangeDebounced = Functions.debounce(200, this.onDocumentChange.bind(this));
        this.onModeChangeDebounced = Functions.debounce(200, this.onModeChange.bind(this));
        this.onDocumentChangeResetDebounced = Functions.debounce(200, this.onDocumentChangeReset.bind(this), true);
        this.onSelectionChangeDebounced = Functions.debounce(300, this.onSelectionChange.bind(this), true);
    }
},
'editor state changes', {
    onDocumentChangeReset: function(evt, codeEditor) {
        // called when a document change occurs and is supposed to reset state
        // that will be recomputed in the real on doc change handler
    },

    onModeChange: function(evt, codeEditor) {
        var session = codeEditor.getSession();
        evt.codeEditor = codeEditor;
        evt.session = session;
        var self = this;
        lively.ide.codeeditor.Modes.ensureModeExtensionIsLoaded(session.getMode(),
            function(err, modeExtModule) { self.invokePlugins('onModeChange', evt); });
    },

    onDocumentChange: function(evt, codeEditor) {
        // 1. signal connection
        var conns = codeEditor.attributeConnections;
        if (conns) {
            for (var i = 0; i < conns.length; i++) {
                var con = conns[i];
                if (con.sourceAttrName === 'textChange') con.update(evt);
                if (con.sourceAttrName === 'textString') con.update(codeEditor.textString);
            }
        }

        // reacts to a document change by dispatching to plugins
        var session = codeEditor.getSession();
        evt.codeEditor = codeEditor;
        evt.session = session;
        this.invokePlugins('onDocumentChange', evt);
    },

    onSelectionChange: function(evt, codeEditor) {
        evt.codeEditor = codeEditor;
        evt.session = codeEditor.getSession();
        this.invokePlugins('onSelectionChange', evt);
    }
},
'plugins', {
    invokePlugins: function(methodName, evt) {
        return this.getPlugins()
            .select(function(plugin) { return plugin[methodName] && plugin.isActiveFor(evt); })
            .invoke(methodName, evt)
            .length;
    },
    getPlugins: function() { return this.plugins; }
});

Object.extend(lively.ide.CodeEditor.DocumentChangeHandler, {
    plugins: [],
    create: function() {
        return new lively.ide.CodeEditor.DocumentChangeHandler(this.plugins);
    },
    registerModeHandler: function(handlerClass) {
        if (!this.plugins.pluck('constructor').include(handlerClass))
            this.plugins.push(new handlerClass());
    }
});

// Abstract super class for specific text modes
Object.subclass('lively.ide.codeeditor.ModeChangeHandler',
"settings", {
    targetMode: null
},
"testing", {
    isActiveFor: function(evt) {
        if (!this.targetMode) return false;
        var mode = evt.session.getMode();
        if (mode.$id === this.targetMode) return true;
        var codeMarker = evt.session.$livelyCodeMarker;
        return codeMarker && codeMarker.modeId === this.targetMode;
    }
},
"markers", {
    // simple background markers for highlighting
    ensureLivelyCodeMarker: function(session) {
        var marker = session.$livelyCodeMarker;
        if (!marker) {
            marker = session.$livelyCodeMarker = new lively.ide.CodeEditor.CodeMarker();
            marker.attach(session);
        }
        return marker;
    },

    drawMarkerHighlight: function(spec, codeEditor, marker) {
        // spec = {pos: {start: NUMBER, end: NUMBER}, message: STRING, cssClassName: STRING
        var pos = spec.pos;
        var start = {row: pos.row, column: Math.max(0, pos.column-1)};
        var end = {row: pos.row, column: pos.column+1};
        if (start.column === end.column) end.column++;
        var absStart = codeEditor.positionToIndex(start);
        var absEnd = codeEditor.positionToIndex(end);
        var cssClass = spec.cssClassName;
        if (!cssClass) {
            if (spec.type === 'error') cssClass = "ace-syntax-error";
            if (spec.type === 'warning') cssClass = "ace-marker-warning";
        }
        var markerPart = {cssClassName: cssClass, end: absEnd, start: absStart}
        marker.markerRanges.push(markerPart);
        marker.redraw(codeEditor.getSession());
    }
},
"overlays", {
    // text overlay that is rendered on top of the real text
    addOverlay: function(spec, codeEditor, marker, overlayBounds) {
        // spec = {pos: {start: NUMBER, end: NUMBER}, message: STRING, cssClassName: STRING
        var messageWidth = spec.message.split('\n').pluck('length').max(),
            r = codeEditor.aceEditor.renderer,
            edWidth = Math.floor((r.$size.width-r.gutterWidth-r.scrollBarV.width)/r.characterWidth),
            col = edWidth-messageWidth,
            row = spec.pos.row,
            overlay = {start: {column: col, row: row}, text: spec.message};
        overlay = this.transformOverlaySpecToNotOverlap(overlay, overlayBounds);
        this.recordOverlayedArea(overlay, overlayBounds);
        codeEditor.addTextOverlay(overlay);
    },

    overlaySpecBounds: function(overlaySpec) {
        var x = overlaySpec.start.column, y = overlaySpec.start.row,
            lines = overlaySpec.text.split('\n'),
            width = lines.max(function(line) { return line.length; }).length,
            height = lines.length;
        return lively.rect(x,y,width,height);
    },

    recordOverlayedArea: function(overlaySpec, overlayBounds) {
        overlayBounds.push(this.overlaySpecBounds(overlaySpec));
    },

    transformOverlaySpecToNotOverlap: function(overlaySpec, otherOverlayBounds) {
        var bounds = this.overlaySpecBounds(overlaySpec), last = otherOverlayBounds.last();
        if (!last || !last.intersects(bounds)) return overlaySpec;
        return Object.extend(Object.create(overlaySpec), {start: {column: bounds.left(), row: last.bottom()}});
    }
},
'update', {
    onModeChange: function(evt) {
        var sess = evt.session;
        if (sess.getMode().$id === this.targetMode) { this.onDocumentChange(evt); return; }
        var marker = this.ensureLivelyCodeMarker(sess);
        sess.$ast = null;
        marker.markerRanges.length = 0;
        marker.modeId = null;
        marker.redraw(sess);
    },
    onDocumentChange: function(evt) {}
});

Object.subclass('lively.ide.CodeEditor.CodeMarker',
// used as a "dynamic marker" inside an ace editor. The update method draws
// into the ace rendering area
"initializing", {
    initialize: function() {
        this.markerRanges = [];
    },
    attach: function(session) {
        if (!this.id || !(this.id in session.$backMarkers))
            session.addDynamicMarker(this);
    },
    detach: function(session) {
        this.markerRanges.length = 0;
        session._emit('changeBackMarker');
        session.removeMarker(this);
        session.$astFeedbackMarker = null;
    }
},
"rendering", {
    update: function(html, markerLayer, session, config) {
        var Range = lively.ide.ace.require("ace/range").Range;
        var screenStartRow = config.firstRow, screenEndRow = config.lastRow;
        this.markerRanges.forEach(function(range) {
            var start, end;
            if (range.pos) {
                start = session.doc.indexToPosition(range.pos-1),
                end = session.doc.indexToPosition(range.pos+1);
            } else if (range.start && range.end) {
                start = session.doc.indexToPosition(range.start);
                end = session.doc.indexToPosition(range.end);
            } else if (range.startPos &&range.endPos) {
                start = range.startPos; end = range.endPos;
            } else {
                console.warn('lively.morphic.CodeMarker cannot render %s', range);
                return;
            }
            if (start.row < screenStartRow || end.row > screenEndRow) return;
            var realRange = Range.fromPoints(start, end);
            var method = start.row === end.row ? 'drawSingleLineMarker' : 'drawTextMarker';
            markerLayer[method](html, realRange.toScreenRange(session), range.cssClassName || "lively-ace-codemarker", config);
        });
    },

    redraw: function(session) {
        session._emit('changeBackMarker');
    }
});

}) // end of module
<commit_msg>Fixing mistaken CodeEditor check<commit_after>module('lively.ide.codeeditor.DocumentChange').requires().toRun(function() {

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// how to deal with document changes
// this class connects the lively.morphic.CodeEditor with the change handlers
// out there
Object.subclass('lively.ide.CodeEditor.DocumentChangeHandler',
'initialize', {
    initialize: function(plugins) {
        this.plugins = plugins;
        this.onDocumentChangeDebounced = Functions.debounce(200, this.onDocumentChange.bind(this));
        this.onModeChangeDebounced = Functions.debounce(200, this.onModeChange.bind(this));
        this.onDocumentChangeResetDebounced = Functions.debounce(200, this.onDocumentChangeReset.bind(this), true);
        this.onSelectionChangeDebounced = Functions.debounce(300, this.onSelectionChange.bind(this), true);
    }
},
'editor state changes', {
    onDocumentChangeReset: function(evt, codeEditor) {
        // called when a document change occurs and is supposed to reset state
        // that will be recomputed in the real on doc change handler
    },

    onModeChange: function(evt, codeEditor) {
        var session = codeEditor.getSession();
        evt.codeEditor = codeEditor;
        evt.session = session;
        var self = this;
        lively.ide.codeeditor.Modes.ensureModeExtensionIsLoaded(session.getMode(),
            function(err, modeExtModule) { self.invokePlugins('onModeChange', evt); });
    },

    onDocumentChange: function(evt, codeEditor) {
        // 1. signal connection
        var conns = codeEditor.attributeConnections;
        if (conns) {
            for (var i = 0; i < conns.length; i++) {
                var con = conns[i];
                if (con.sourceAttrName === 'textChange') con.update(evt);
                if (con.sourceAttrName === 'textString') con.update(codeEditor.textString);
            }
        }

        // reacts to a document change by dispatching to plugins
        var session = codeEditor.getSession();
        evt.codeEditor = codeEditor;
        evt.session = session;
        this.invokePlugins('onDocumentChange', evt);
    },

    onSelectionChange: function(evt, codeEditor) {
        evt.codeEditor = codeEditor;
        evt.session = codeEditor.getSession();
        this.invokePlugins('onSelectionChange', evt);
    }
},
'plugins', {
    invokePlugins: function(methodName, evt) {
        return this.getPlugins()
            .select(function(plugin) { return plugin[methodName] && plugin.isActiveFor(evt); })
            .invoke(methodName, evt)
            .length;
    },
    getPlugins: function() { return this.plugins; }
});

Object.extend(lively.ide.CodeEditor.DocumentChangeHandler, {
    plugins: [],
    create: function() {
        return new lively.ide.CodeEditor.DocumentChangeHandler(this.plugins);
    },
    registerModeHandler: function(handlerClass) {
        if (!this.plugins.pluck('constructor').include(handlerClass))
            this.plugins.push(new handlerClass());
    }
});

// Abstract super class for specific text modes
Object.subclass('lively.ide.codeeditor.ModeChangeHandler',
"settings", {
    targetMode: null
},
"testing", {
    isActiveFor: function(evt) {
        if (!this.targetMode) return false;
        var mode = evt.session.getMode();
        if (mode.$id === this.targetMode) return true;
        var codeMarker = evt.session.$livelyCodeMarker;
        return codeMarker && codeMarker.modeId === this.targetMode;
    }
},
"markers", {
    // simple background markers for highlighting
    ensureLivelyCodeMarker: function(session) {
        var marker = session.$livelyCodeMarker;
        if (!marker) {
            marker = session.$livelyCodeMarker = new lively.ide.CodeEditor.CodeMarker();
            marker.attach(session);
        }
        return marker;
    },

    drawMarkerHighlight: function(spec, codeEditor, marker) {
        // spec = {pos: {start: NUMBER, end: NUMBER}, message: STRING, cssClassName: STRING
        var pos = spec.pos;
        var start = {row: pos.row, column: Math.max(0, pos.column-1)};
        var end = {row: pos.row, column: pos.column+1};
        if (start.column === end.column) end.column++;
        var absStart = codeEditor.positionToIndex(start);
        var absEnd = codeEditor.positionToIndex(end);
        var cssClass = spec.cssClassName;
        if (!cssClass) {
            if (spec.type === 'error') cssClass = "ace-syntax-error";
            if (spec.type === 'warning') cssClass = "ace-marker-warning";
        }
        var markerPart = {cssClassName: cssClass, end: absEnd, start: absStart}
        marker.markerRanges.push(markerPart);
        marker.redraw(codeEditor.getSession());
    }
},
"overlays", {
    // text overlay that is rendered on top of the real text
    addOverlay: function(spec, codeEditor, marker, overlayBounds) {
        // spec = {pos: {start: NUMBER, end: NUMBER}, message: STRING, cssClassName: STRING
        var messageWidth = spec.message.split('\n').pluck('length').max(),
            r = codeEditor.aceEditor.renderer,
            edWidth = Math.floor((r.$size.width-r.gutterWidth-r.scrollBarV.width)/r.characterWidth),
            col = edWidth-messageWidth,
            row = spec.pos.row,
            overlay = {start: {column: col, row: row}, text: spec.message};
        overlay = this.transformOverlaySpecToNotOverlap(overlay, overlayBounds);
        this.recordOverlayedArea(overlay, overlayBounds);
        codeEditor.addTextOverlay(overlay);
    },

    overlaySpecBounds: function(overlaySpec) {
        var x = overlaySpec.start.column, y = overlaySpec.start.row,
            lines = overlaySpec.text.split('\n'),
            width = lines.max(function(line) { return line.length; }).length,
            height = lines.length;
        return lively.rect(x,y,width,height);
    },

    recordOverlayedArea: function(overlaySpec, overlayBounds) {
        overlayBounds.push(this.overlaySpecBounds(overlaySpec));
    },

    transformOverlaySpecToNotOverlap: function(overlaySpec, otherOverlayBounds) {
        var bounds = this.overlaySpecBounds(overlaySpec), last = otherOverlayBounds.last();
        if (!last || !last.intersects(bounds)) return overlaySpec;
        return Object.extend(Object.create(overlaySpec), {start: {column: bounds.left(), row: last.bottom()}});
    }
},
'update', {
    onModeChange: function(evt) {
        var sess = evt.session;
        if (sess.getMode().$id === this.targetMode) { this.onDocumentChange(evt); return; }
        var marker = this.ensureLivelyCodeMarker(sess);
        sess.$ast = null;
        marker.markerRanges.length = 0;
        marker.modeId = null;
        marker.redraw(sess);
    },
    onDocumentChange: function(evt) {}
});

Object.subclass('lively.ide.CodeEditor.CodeMarker',
// used as a "dynamic marker" inside an ace editor. The update method draws
// into the ace rendering area
"initializing", {
    initialize: function() {
        this.markerRanges = [];
    },
    attach: function(session) {
        if (!this.id || !(this.id in session.$backMarkers))
            session.addDynamicMarker(this);
    },
    detach: function(session) {
        this.markerRanges.length = 0;
        session._emit('changeBackMarker');
        session.removeMarker(this);
        session.$astFeedbackMarker = null;
    }
},
"rendering", {
    update: function(html, markerLayer, session, config) {
        var Range = lively.ide.ace.require("ace/range").Range;
        var screenStartRow = config.firstRow, screenEndRow = config.lastRow;
        this.markerRanges.forEach(function(range) {
            var start, end;
            if (range.pos) {
                start = session.doc.indexToPosition(range.pos-1),
                end = session.doc.indexToPosition(range.pos+1);
            } else if (range.start != null && range.end != null) {
                start = session.doc.indexToPosition(range.start);
                end = session.doc.indexToPosition(range.end);
            } else if (range.startPos && range.endPos) {
                start = range.startPos; end = range.endPos;
            } else {
                console.warn('lively.morphic.CodeMarker cannot render %s', range);
                return;
            }
            if (start.row < screenStartRow || end.row > screenEndRow) return;
            var realRange = Range.fromPoints(start, end);
            var method = start.row === end.row ? 'drawSingleLineMarker' : 'drawTextMarker';
            markerLayer[method](html, realRange.toScreenRange(session), range.cssClassName || "lively-ace-codemarker", config);
        });
    },

    redraw: function(session) {
        session._emit('changeBackMarker');
    }
});

}) // end of module
<|endoftext|>


<commit_before>//==========
//===EH LEE LA DOCUMENTACIÓN ACA=====
//==http://expressjs.com/guide/routing.html===
//==Para más detalles de la API MIRA ACA===
//==http://expressjs.com/4x/api.html
var express = require('express');
var app = express();
var io = require('socket.io-client');

app.get('/', function (req, res) {
  res.send('Hello World!');
});

var server = app.listen(5000, function () {
  var host = server.address().address;
  var port = server.address().port;

  console.log('Example app listening at http://%s:%s', host, port);
});

var socket = io.connect('http://localhost:3000');

while(true) {
  produce();
}

function produce() {
  socket.emit('mande fruta');
  console.log('mande una consulta');
  sleep(3000);
}

function sleep(milliseconds) {
  var start = new Date().getTime();
  for (var i = 0; i < 1e7; i++) {
      if ((new Date().getTime() - start) > milliseconds){
            break;
          }
    }
}

<commit_msg>alumno http get<commit_after>//==========
//===EH LEE LA DOCUMENTACIÓN ACA=====
//==http://expressjs.com/guide/routing.html===
//==Para más detalles de la API MIRA ACA===
//==http://expressjs.com/4x/api.html
var express = require('express');
var http = require('http');
var app = express();

var options = {
  hostname: 'localhost',
  port: 3000,
  path: '/alumnoSeConecta',
  method: 'GET'
}

app.get('/', function (req, res) {
  res.send('Hello World!');
});

var server = app.listen(5000, function () {
  var host = server.address().address;
  var port = server.address().port;

  console.log('Example app listening at http://%s:%s', host, port);
});

while(true) {
  produce();
}
function produce() {
  console.log('voy');
  http.get('http://localhost:3000/alumnoSeConecta', function(res){
    console.log('hola');
  });
  sleep(3000);
}

function sleep(milliseconds) {
  var start = new Date().getTime();
  for (var i = 0; i < 1e7; i++) {
      if ((new Date().getTime() - start) > milliseconds){
            break;
          }
    }
}

<|endoftext|>


<commit_before>(function(global) {
  global.S3MP = (function() {

    // Wrap this into underscore library extension
    _.mixin({
      findIndex : function (collection, filter) {
        for (var i = 0; i < collection.length; i++) {
          if (filter(collection[i], i, collection)) {
            return i;
          }
        }
        return -1;
      }
    });
// S3MP Constructor
function S3MP(options) {
  var files
    , progress_timer = []
    , S3MP = this;

  _.extend(this, options);
  this.headers = _.object(_.map(options.headers, function(v,k) { return ["x-amz-" + k.toLowerCase(), v] }));

  this.uploadList = [];

  // Handles all of the success/failure events, and
  // progress notifiers
  this.handler = {

    // Activate an appropriate number of parts (number = pipes)
    // when all of the parts have been successfully initialized
    beginUpload: function() {
      var i = [];
      function beginUpload(pipes, uploadObj) {
        var key = uploadObj.key
          , num_parts = uploadObj.parts.length;

        if (typeof i[key] === "undefined") {
          i[key] = 0;
        }

        i[key]++;

        if (i[key] === num_parts) {
          for (var j=0; j<pipes; j++) {
            uploadObj.parts[j].activate();
          }
          S3MP.handler.startProgressTimer(key);
          S3MP.onStart(uploadObj); // This probably needs to go somewhere else.
        }
      }
      return beginUpload;
    }(),

    // called when an upload is paused or the network connection cuts out
    onError: function(uploadObj, part) {
      S3MP.onError(uploadObj, part);
    },

    // called when a single part has successfully uploaded
    onPartSuccess: function(uploadObj, finished_part) {
      var parts, i, ETag;

      parts = uploadObj.parts;
      finished_part.status = "complete";

      // Append the ETag (in the response header) to the ETags array
      ETag = finished_part.xhr.getResponseHeader("ETag");
      uploadObj.Etags.push({ ETag: ETag.replace(/\"/g, ''), partNum: finished_part.num });

      // Increase the uploaded count and delete the finished part
      uploadObj.uploaded += finished_part.size;
      uploadObj.inprogress[finished_part.num] = 0;
      i = _.indexOf(parts, finished_part);
      parts.splice(i,1);

      // activate one of the remaining parts
      if (parts.length) {
        i = _.findIndex(parts, function(el, index, collection) {
          if (el.status !== "active") {
            return true;
          }
        });
        if (i !== -1){
          parts[i].activate();
        }
      }

      // If no parts remain then the upload has finished
      if (!parts.length) {
        this.onComplete(uploadObj);
      }
    },

    // called when all parts have successfully uploaded
    onComplete: function(uploadObj) {
      var key = _.indexOf(S3MP.uploadList, uploadObj);

      // Stop the onprogress timer
      this.clearProgressTimer(key);

      // Tell the server to put together the pieces
      S3MP.completeMultipart(uploadObj, function(obj) {
        // Notify the client that the upload has succeeded when we
        // get confirmation from the server
        if (obj.location) {
          obj.extra_data ? S3MP.onComplete(uploadObj, obj.extra_data) : S3MP.onComplete(uploadObj);
        }
      });

    },

    // Called by progress_timer
    onProgress: function(key, size, done, percent, speed) {
      S3MP.onProgress(key, size, done, percent, speed);
    },

    startProgressTimer: function() {
      var last_upload_chunk = [];
      var fn = function(key) {
        progress_timer[key] = global.setInterval(function() {
          var upload, size, done, percent, speed;

          if (typeof last_upload_chunk[key] === "undefined") {
            last_upload_chunk[key] = 0;
          }

          upload = S3MP.uploadList[key];
          size = upload.size;
          done = upload.uploaded;

          _.each(upload.inprogress,function(val, index) {
            part_available = _.find(upload.parts, function(part){
              return part.num == index
            })
            if(part_available && val !== undefined) done += val;
          });

          percent = done/size * 100;
          speed = done - last_upload_chunk[key];
          last_upload_chunk[key] = done;

          upload.handler.onProgress(key, size, done, percent, speed);
        }, 1000);
      };
      return fn;
    }(),

    clearProgressTimer: function(key) {
      global.clearInterval(progress_timer[key]);
    }

  };

  // List of files may come from a FileList object or an array of files
  if (this.fileSelector) {
    files = $(this.fileSelector).get(0).files; // FileList object
  } else {
    files = this.fileList; // array specified in configuration
  }

  _.each(files, function(file, key) {
    var upload = new Upload(file, S3MP, key);
    S3MP.uploadList.push(upload);
    upload.init();
  });

};

S3MP.prototype.initiateMultipart = function(upload, cb) {
  var url, body, xhr;

  url = '/s3_multipart/uploads';
  body = JSON.stringify({ object_name  : upload.name,
                          content_type : upload.type,
                          content_size : upload.size,
                          headers      : this.headers,
                          context      : $(this.fileInputElement).data("context"),
                          uploader     : $(this.fileInputElement).data("uploader")
                        });

  xhr = this.createXhrRequest('POST', url);
  this.deliverRequest(xhr, body, cb);

};

S3MP.prototype.signPartRequests = function(id, object_name, upload_id, parts, cb) {
  var content_lengths, url, body, xhr;

  content_lengths = _.reduce(_.rest(parts), function(memo, part) {
    return memo + "-" + part.size;
  }, parts[0].size);

  url = "/s3_multipart/uploads/"+id;
  body = JSON.stringify({ object_name     : object_name,
                          upload_id       : upload_id,
                          content_lengths : content_lengths
                        });

  xhr = this.createXhrRequest('PUT', url);
  this.deliverRequest(xhr, body, cb);
};

S3MP.prototype.completeMultipart = function(uploadObj, cb) {
  var url, body, xhr;

  url = '/s3_multipart/uploads/'+uploadObj.id;
  body = JSON.stringify({ object_name    : uploadObj.object_name,
                          upload_id      : uploadObj.upload_id,
                          content_length : uploadObj.size,
                          parts          : uploadObj.Etags
                        });

  xhr = this.createXhrRequest('PUT', url);
  this.deliverRequest(xhr, body, cb);
};

// Specify callbacks, request body, and settings for requests that contact
// the site server, and send the request.
S3MP.prototype.deliverRequest = function(xhr, body, cb) {
  var self = this;

  xhr.onload = function() {
    response = JSON.parse(this.responseText);
    if (response.error) {
      return self.onError({
        name: "ServerResponse",
        message: response.error
      });
    }
    cb(response);
  };

  xhr.onerror = function() {
    // To-do: Handle communication errors
  };

  xhr.setRequestHeader('Content-Type', 'application/json');
  xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));

  xhr.send(body);
}

S3MP.prototype.createXhrRequest = function() {
  var xhrRequest;

  // Sniff for xhr object
  if (typeof XMLHttpRequest.constructor === "function") {
    xhrRequest = XMLHttpRequest;
  } else if (typeof XDomainRequest !== "undefined") {
    xhrRequest = XDomainRequest;
  } else {
    xhrRequest = null; // Error out to the client (To-do)
  }

  return function(method, url, cb, open) { // open defaults to true
    var args, xhr, open = true;

    args = Array.prototype.slice.call(arguments);
    if (typeof args[0] === "undefined") {
      cb = null;
      open = false;
    }

    xhr = new xhrRequest();
    if (open) { // open the request unless specified otherwise
      xhr.open(method, url, true);
    }
    xhr.onreadystatechange = cb;

    return xhr;
  };

}();

S3MP.prototype.sliceBlob = function() {
  try {
    var test_blob = new Blob();
  } catch(e) {
    return "Unsupported";
  }

  if (test_blob.slice) {
    return function(blob, start, end) {
      return blob.slice(start, end);
    }
  } else if (test_blob.mozSlice) {
    return function(blob, start, end) {
      return blob.mozSlice(start, end);
    }
  } else if (test_blob.webkitSlice) {
    return function(blob, start, end) {
      return blob.webkitSlice(start, end);
    }
  } else {
    return "Unsupported";
  }
}();

// utility function to return an upload object given a file
S3MP.prototype._returnUploadObj = function(key) {
  var uploadObj = _.find(this.uploadList, function(uploadObj) {
    return uploadObj.key === key;
  });
  return uploadObj;
};

// cancel a given file upload
S3MP.prototype.cancel = function(key) {
  var uploadObj, i;

  uploadObj = this._returnUploadObj(key);
  i = _.indexOf(this.uploadList, uploadObj);

  this.uploadList.splice(i,i+1);
  this.onCancel(key);
  this.handler.clearProgressTimer(key);
};

// pause a given file upload
S3MP.prototype.pause = function(key) {
  var uploadObj = this._returnUploadObj(key);

  _.each(uploadObj.parts, function(part, key, list) {
    if (part.status == "active") {
      part.pause();
    }
  });

  this.onPause(key);
  this.handler.clearProgressTimer(key);
};

// resume a given file upload
S3MP.prototype.resume = function(key) {
  var uploadObj = this._returnUploadObj(key);

  _.each(uploadObj.parts, function(part, key, list) {
    if (part.status == "paused") {
      part.activate();
    }
  });
  this.handler.startProgressTimer(key);
  this.onResume(key);
};

// Upload constructor
function Upload(file, o, key) {
  function Upload() {
    var upload, id, parts, part, segs, chunk_segs, chunk_lens, pipes, blob;

    upload = this;

    this.key = key;
    this.file = file;
    this.name = file.name;
    this.size = file.size;
    this.type = file.type;
    this.Etags = [];
    this.inprogress = [];
    this.uploaded = 0;
    this.status = "";

    // Break the file into an appropriate amount of chunks
    // This needs to be optimized for various browsers types/versions
    if (this.size > 1000000000) { // size greater than 1gb
      num_segs = 200;
      pipes = 2;
    } else if (this.size > 500000000) { // greater than 500mb
      num_segs = 140;
      pipes = 2;
    } else if (this.size > 100000000) { // greater than 100 mb
      num_segs = 20;
      pipes = 5;
    } else if (this.size > 50000000) { // greater than 50 mb
      num_segs = 5;
      pipes = 2;
    } else if (this.size > 10000000) { // greater than 10 mb
      num_segs = 2;
      pipes = 2;
    } else { // greater than 5 mb (S3 does not allow multipart uploads < 5 mb)
      num_segs = 1;
      pipes = 1;
    }

    chunk_segs = _.range(num_segs + 1);
    chunk_lens = _.map(chunk_segs, function(seg) {
      return Math.round(seg * (file.size/num_segs));
    });

    if (upload.sliceBlob == "Unsupported") {
      this.parts = [new UploadPart(file, 0, upload)];
    } else {
      this.parts = _.map(chunk_lens, function(len, i) {
        blob = upload.sliceBlob(file, len, chunk_lens[i+1]);
        return new UploadPart(blob, i+1, upload);
      });
      this.parts.pop(); // Remove the empty blob at the end of the array
    }

    // init function will initiate the multipart upload, sign all the parts, and
    // start uploading some parts in parallel
    this.init = function() {
      upload.initiateMultipart(upload, function(obj) {
        var id = upload.id = obj.id
          , upload_id = upload.upload_id = obj.upload_id
          , object_name = upload.object_name = obj.key // uuid generated by the server, different from name
          , parts = upload.parts;

        upload.signPartRequests(id, object_name, upload_id, parts, function(response) {
          _.each(parts, function(part, key) {
            part.date = response.uploads[key].date;
            part.auth = response.uploads[key].authorization;

            // Notify handler that an xhr request has been opened
            upload.handler.beginUpload(pipes, upload);
          });
        });
      });
    }
  };
  // Inherit the properties and prototype methods of the passed in S3MP instance object
  Upload.prototype = o;
  return new Upload();
}

// Upload part constructor
function UploadPart(blob, key, upload) {
  var part, xhr;

  part = this;

  this.size = blob.size;
  this.blob = blob;
  this.num = key;
  this.upload = upload;

  this.xhr = xhr = upload.createXhrRequest();
  xhr.onload = function() {
    if (part.xhr.getResponseHeader("ETag") === null) {
      upload.handler.onError(upload, part);
    } else {
      upload.handler.onPartSuccess(upload, part);
    }
  };
  xhr.onerror = function() {
    upload.handler.onError(upload, part);
  };
  xhr.upload.onprogress = _.throttle(function(e) {
    if (e.lengthComputable) {
      upload.inprogress[key] = e.loaded;
    }
  }, 1000);

};

UploadPart.prototype.activate = function() {
  var protocol = window.location.protocol;
  this.xhr.open('PUT', protocol+'//'+this.upload.bucket+'.s3.amazonaws.com/'+this.upload.object_name+'?partNumber='+this.num+'&uploadId='+this.upload.upload_id, true);
  this.xhr.setRequestHeader('x-amz-date', this.date);
  this.xhr.setRequestHeader('Authorization', this.auth);

  this.xhr.send(this.blob);
  this.status = "active";
};

UploadPart.prototype.pause = function() {
  this.xhr.abort();
  this.status = "paused";
};

return S3MP;

  }());

}(this));<commit_msg>resolving RequestTimeTooSkewed issue<commit_after>(function(global) {
  global.S3MP = (function() {

    // Wrap this into underscore library extension
    _.mixin({
      findIndex : function (collection, filter) {
        for (var i = 0; i < collection.length; i++) {
          if (filter(collection[i], i, collection)) {
            return i;
          }
        }
        return -1;
      }
    });
// S3MP Constructor
function S3MP(options) {
  var files
    , progress_timer = []
    , S3MP = this;

  _.extend(this, options);
  this.headers = _.object(_.map(options.headers, function(v,k) { return ["x-amz-" + k.toLowerCase(), v] }));

  this.uploadList = [];

  // Handles all of the success/failure events, and
  // progress notifiers
  this.handler = {

    // Activate an appropriate number of parts (number = pipes)
    // when all of the parts have been successfully initialized
    beginUpload: function() {
      var i = [];
      function beginUpload(pipes, uploadObj) {
        var key = uploadObj.key
          , num_parts = uploadObj.parts.length;

        if (typeof i[key] === "undefined") {
          i[key] = 0;
        }

        i[key]++;

        for (var j=0; j<pipes; j++) {
          uploadObj.parts[j].activate();
        }
        S3MP.handler.startProgressTimer(key);
        S3MP.onStart(uploadObj); // This probably needs to go somewhere else.
      }
      return beginUpload;
    }(),

    // called when an upload is paused or the network connection cuts out
    onError: function(uploadObj, part) {
      S3MP.onError(uploadObj, part);
    },

    // called when a single part has successfully uploaded
    onPartSuccess: function(uploadObj, finished_part) {
      var parts, i, ETag;

      parts = uploadObj.parts;
      finished_part.status = "complete";

      // Append the ETag (in the response header) to the ETags array
      ETag = finished_part.xhr.getResponseHeader("ETag");
      uploadObj.Etags.push({ ETag: ETag.replace(/\"/g, ''), partNum: finished_part.num });

      // Increase the uploaded count and delete the finished part
      uploadObj.uploaded += finished_part.size;
      uploadObj.inprogress[finished_part.num] = 0;
      i = _.indexOf(parts, finished_part);
      parts.splice(i,1);

      // activate one of the remaining parts
      if (parts.length) {
        i = _.findIndex(parts, function(el, index, collection) {
          if (el.status !== "active") {
            return true;
          }
        });
        if (i !== -1){
          parts[i].activate();
        }
      }

      // If no parts remain then the upload has finished
      if (!parts.length) {
        this.onComplete(uploadObj);
      }
    },

    // called when all parts have successfully uploaded
    onComplete: function(uploadObj) {
      var key = _.indexOf(S3MP.uploadList, uploadObj);

      // Stop the onprogress timer
      this.clearProgressTimer(key);

      // Tell the server to put together the pieces
      S3MP.completeMultipart(uploadObj, function(obj) {
        // Notify the client that the upload has succeeded when we
        // get confirmation from the server
        if (obj.location) {
          obj.extra_data ? S3MP.onComplete(uploadObj, obj.extra_data) : S3MP.onComplete(uploadObj);
        }
      });

    },

    // Called by progress_timer
    onProgress: function(key, size, done, percent, speed) {
      S3MP.onProgress(key, size, done, percent, speed);
    },

    startProgressTimer: function() {
      var last_upload_chunk = [];
      var fn = function(key) {
        progress_timer[key] = global.setInterval(function() {
          var upload, size, done, percent, speed;

          if (typeof last_upload_chunk[key] === "undefined") {
            last_upload_chunk[key] = 0;
          }

          upload = S3MP.uploadList[key];
          size = upload.size;
          done = upload.uploaded;

          _.each(upload.inprogress,function(val, index) {
            part_available = _.find(upload.parts, function(part){
              return part.num == index
            })
            if(part_available && val !== undefined) done += val;
          });

          percent = done/size * 100;
          speed = done - last_upload_chunk[key];
          last_upload_chunk[key] = done;

          upload.handler.onProgress(key, size, done, percent, speed);
        }, 1000);
      };
      return fn;
    }(),

    clearProgressTimer: function(key) {
      global.clearInterval(progress_timer[key]);
    }

  };

  // List of files may come from a FileList object or an array of files
  if (this.fileSelector) {
    files = $(this.fileSelector).get(0).files; // FileList object
  } else {
    files = this.fileList; // array specified in configuration
  }

  _.each(files, function(file, key) {
    var upload = new Upload(file, S3MP, key);
    S3MP.uploadList.push(upload);
    upload.init();
  });

};

S3MP.prototype.initiateMultipart = function(upload, cb) {
  var url, body, xhr;

  url = '/s3_multipart/uploads';
  body = JSON.stringify({ object_name  : upload.name,
                          content_type : upload.type,
                          content_size : upload.size,
                          headers      : this.headers,
                          context      : $(this.fileInputElement).data("context"),
                          uploader     : $(this.fileInputElement).data("uploader")
                        });

  xhr = this.createXhrRequest('POST', url);
  this.deliverRequest(xhr, body, cb);

};

S3MP.prototype.signPartRequest = function(id, object_name, upload_id, part, cb) {
  var content_lengths, url, body, xhr;

  content_length = part.size;
  url = "/s3_multipart/uploads/"+id;
  body = JSON.stringify({ object_name     : object_name,
    upload_id       : upload_id,
    content_length : content_length,
    part_number : part.num
  });
  xhr = this.createXhrRequest('PUT', url);
  this.deliverRequest(xhr, body, cb);
};

S3MP.prototype.completeMultipart = function(uploadObj, cb) {
  var url, body, xhr;

  url = '/s3_multipart/uploads/'+uploadObj.id;
  body = JSON.stringify({ object_name    : uploadObj.object_name,
                          upload_id      : uploadObj.upload_id,
                          content_length : uploadObj.size,
                          parts          : uploadObj.Etags
                        });

  xhr = this.createXhrRequest('PUT', url);
  this.deliverRequest(xhr, body, cb);
};

// Specify callbacks, request body, and settings for requests that contact
// the site server, and send the request.
S3MP.prototype.deliverRequest = function(xhr, body, cb) {
  var self = this;

  xhr.onload = function() {
    response = JSON.parse(this.responseText);
    if (response.error) {
      return self.onError({
        name: "ServerResponse",
        message: response.error
      });
    }
    cb(response);
  };

  xhr.onerror = function() {
    // To-do: Handle communication errors
  };

  xhr.setRequestHeader('Content-Type', 'application/json');
  xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));

  xhr.send(body);
}

S3MP.prototype.createXhrRequest = function() {
  var xhrRequest;

  // Sniff for xhr object
  if (typeof XMLHttpRequest.constructor === "function") {
    xhrRequest = XMLHttpRequest;
  } else if (typeof XDomainRequest !== "undefined") {
    xhrRequest = XDomainRequest;
  } else {
    xhrRequest = null; // Error out to the client (To-do)
  }

  return function(method, url, cb, open) { // open defaults to true
    var args, xhr, open = true;

    args = Array.prototype.slice.call(arguments);
    if (typeof args[0] === "undefined") {
      cb = null;
      open = false;
    }

    xhr = new xhrRequest();
    if (open) { // open the request unless specified otherwise
      xhr.open(method, url, true);
    }
    xhr.onreadystatechange = cb;

    return xhr;
  };

}();

S3MP.prototype.sliceBlob = function() {
  try {
    var test_blob = new Blob();
  } catch(e) {
    return "Unsupported";
  }

  if (test_blob.slice) {
    return function(blob, start, end) {
      return blob.slice(start, end);
    }
  } else if (test_blob.mozSlice) {
    return function(blob, start, end) {
      return blob.mozSlice(start, end);
    }
  } else if (test_blob.webkitSlice) {
    return function(blob, start, end) {
      return blob.webkitSlice(start, end);
    }
  } else {
    return "Unsupported";
  }
}();

// utility function to return an upload object given a file
S3MP.prototype._returnUploadObj = function(key) {
  var uploadObj = _.find(this.uploadList, function(uploadObj) {
    return uploadObj.key === key;
  });
  return uploadObj;
};

// cancel a given file upload
S3MP.prototype.cancel = function(key) {
  var uploadObj, i;

  uploadObj = this._returnUploadObj(key);
  i = _.indexOf(this.uploadList, uploadObj);

  this.uploadList.splice(i,i+1);
  this.onCancel(key);
  this.handler.clearProgressTimer(key);
};

// pause a given file upload
S3MP.prototype.pause = function(key) {
  var uploadObj = this._returnUploadObj(key);

  _.each(uploadObj.parts, function(part, key, list) {
    if (part.status == "active") {
      part.pause();
    }
  });

  this.onPause(key);
  this.handler.clearProgressTimer(key);
};

// resume a given file upload
S3MP.prototype.resume = function(key) {
  var uploadObj = this._returnUploadObj(key);

  _.each(uploadObj.parts, function(part, key, list) {
    if (part.status == "paused") {
      part.activate();
    }
  });
  this.handler.startProgressTimer(key);
  this.onResume(key);
};

// Upload constructor
function Upload(file, o, key) {
  function Upload() {
    var upload, id, parts, part, segs, chunk_segs, chunk_lens, pipes, blob;

    upload = this;

    this.key = key;
    this.file = file;
    this.name = file.name;
    this.size = file.size;
    this.type = file.type;
    this.Etags = [];
    this.inprogress = [];
    this.uploaded = 0;
    this.status = "";

    // Break the file into an appropriate amount of chunks
    // This needs to be optimized for various browsers types/versions
    if (this.size > 1000000000) { // size greater than 1gb
      num_segs = 200;
      pipes = 2;
    } else if (this.size > 500000000) { // greater than 500mb
      num_segs = 140;
      pipes = 2;
    } else if (this.size > 100000000) { // greater than 100 mb
      num_segs = 20;
      pipes = 5;
    } else if (this.size > 50000000) { // greater than 50 mb
      num_segs = 5;
      pipes = 2;
    } else if (this.size > 10000000) { // greater than 10 mb
      num_segs = 2;
      pipes = 2;
    } else { // greater than 5 mb (S3 does not allow multipart uploads < 5 mb)
      num_segs = 1;
      pipes = 1;
    }

    chunk_segs = _.range(num_segs + 1);
    chunk_lens = _.map(chunk_segs, function(seg) {
      return Math.round(seg * (file.size/num_segs));
    });

    if (upload.sliceBlob == "Unsupported") {
      this.parts = [new UploadPart(file, 0, upload)];
    } else {
      this.parts = _.map(chunk_lens, function(len, i) {
        blob = upload.sliceBlob(file, len, chunk_lens[i+1]);
        return new UploadPart(blob, i+1, upload);
      });
      this.parts.pop(); // Remove the empty blob at the end of the array
    }

    // init function will initiate the multipart upload, sign all the parts, and
    // start uploading some parts in parallel
    this.init = function() {
      upload.initiateMultipart(upload, function(obj) {
        var id = upload.id = obj.id
          , upload_id = upload.upload_id = obj.upload_id
          , object_name = upload.object_name = obj.key // uuid generated by the server, different from name
          , parts = upload.parts;

        //upload.signPartRequests(id, object_name, upload_id, parts, function(response) {
        //  _.each(parts, function(part, key) {
        //    part.date = response.uploads[key].date;
        //    part.auth = response.uploads[key].authorization;
        //
        //    // Notify handler that an xhr request has been opened
        //    upload.handler.beginUpload(pipes, upload);
        //  });
        //});
        upload.handler.beginUpload(pipes, upload);
      });
    }
  };
  // Inherit the properties and prototype methods of the passed in S3MP instance object
  Upload.prototype = o;
  return new Upload();
}

// Upload part constructor
function UploadPart(blob, key, upload) {
  var part, xhr;

  part = this;

  this.size = blob.size;
  this.blob = blob;
  this.num = key;
  this.upload = upload;

  this.xhr = xhr = upload.createXhrRequest();
  xhr.onload = function() {
    if (part.xhr.getResponseHeader("ETag") === null) {
      upload.handler.onError(upload, part);
    } else {
      upload.handler.onPartSuccess(upload, part);
    }
  };
  xhr.onerror = function() {
    upload.handler.onError(upload, part);
  };
  xhr.upload.onprogress = _.throttle(function(e) {
    if (e.lengthComputable) {
      upload.inprogress[key] = e.loaded;
    }
  }, 1000);

};

UploadPart.prototype.activate = function() {
  var upload_part = this;
  this.upload.signPartRequest(this.upload.id, this.upload.object_name, this.upload.upload_id, this, function(response) {
    upload_part.xhr.open('PUT', '//'+upload_part.upload.bucket+'.s3.amazonaws.com/'+upload_part.upload.object_name+'?partNumber='+upload_part.num+'&uploadId='+upload_part.upload.upload_id, true);

    upload_part.xhr.setRequestHeader('x-amz-date', response.date);
    upload_part.xhr.setRequestHeader('Authorization', response.authorization);

    upload_part.xhr.send(upload_part.blob);
    upload_part.status = "active";
  });
};

UploadPart.prototype.pause = function() {
  this.xhr.abort();
  this.status = "paused";
};

return S3MP;

  }());

}(this));<|endoftext|>