<commit_before>//helpers methods to disable, enable, and uncheck radio buttons and checkboxes
(function( $ ){
    //disable an element
    $.fn.disable = function() {
        return this.attr('disabled', 'disabled');
    };
})( jQuery );

(function( $ ){
    //enable an element
    $.fn.enable = function() {
        return this.removeAttr('disabled');
    };
})( jQuery );

(function( $ ){
    //uncheck an element
    $.fn.uncheck = function() {
        return this.removeAttr('checked');
    };
})( jQuery );

(function( $ ){
    //check an element
    $.fn.check = function() {
        return this.attr('checked', 'checked');
    };
})( jQuery );

(function( $ ){
    //(pre-)check the only active database checkbox
    $.onedb = function(selector) {
        active_dbs = $(".databases input[type=checkbox]").not(":disabled")
        if (active_dbs.length == 1){
            active_dbs.check()
        }
        return active_dbs;
    };
})( jQuery );

(function ($) {
    $.fn.poll = function () {
        var that, val, tmp;

        that = this;
        val  = that.val();

        (function ping () {
            tmp = that.val();

            if (tmp != val){
                val = tmp;
                that.change();
            }

            setTimeout(ping, 100);
        }());

        return this;
    };
}(jQuery));

/*
    SS - SequenceServer's JavaScript module

    Define a global SS (acronym for SequenceServer) object containing the
    following methods:

        main():
            Initializes SequenceServer's various modules.
*/

//define global SS object
var SS;
if (!SS) {
    SS = {};
}

//SS module
(function () {

    SS.decorate = function (name) {
      return name.match(/(.?)(blast)(.?)/).slice(1).map(function (token, _) {
        if (token) {
            if (token !== 'blast'){
                return '' + token + '';
            }
            else {
              return token;
            }
        }
      }).join('');
    };

    SS.generateGraphicalOverview = function () {

        $("[data-graphit='overview']").each(function () {
            $.graphIt(this, 0, 20);
        });

    };

    SS.init = function () {
        this.$sequence = $('#sequence');
        this.$sequenceFile = $('#sequence-file');
        this.$sequenceControls = $('.sequence-controls');

        this.$sequence.poll();

        SS.blast.init();
    }
}()); //end SS module

$(document).ready(function(){
    SS.init();

    var notification_timeout;

    // drag-and-drop code
    var tgtMarker = $('.dnd-overlay');
    var $sequence = $('#sequence');

    var dndError = function (id) {
        $('.dnd-error').hide();
        $('#' + id + '-notification').show('drop', {direction: 'up'});
        tgtMarker.effect('fade', 2500);
    };

    $(document)
    .on('dragenter', function (evt) {
        // Based on http://stackoverflow.com/a/8494918/1205465.
        // Contrary to what the above link says, the snippet below can't
        // distinguish directories from files. We handle that on drop.
        var dt = evt.originalEvent.dataTransfer;
        var isFile = dt.types && ((dt.types.indexOf &&  // Chrome and Safari
                                  dt.types.indexOf('Files') != -1) ||
                                  (dt.types.contains && // Firefox
                                   dt.types.contains('application/x-moz-file')));

        if (!isFile) { return; }

        tgtMarker.show();
        dt.effectAllowed = 'copy';
        if ($sequence.val() === '') {
            $('.dnd-overlay-overwrite').hide();
            $('.dnd-overlay-drop').show('drop', {direction: 'down'}, 'fast');
        }
        else {
            $('.dnd-overlay-drop').hide();
            $('.dnd-overlay-overwrite').show('drop', {direction: 'down'}, 'fast');
        }
    })
    .on('dragleave', '.dnd-overlay', function (evt) {
        tgtMarker.hide();
        $('.dnd-overlay-drop').hide();
        $('.dnd-overlay-overwrite').hide();
    })
    .on('dragover', '.dnd-overlay', function (evt) {
        evt.originalEvent.dataTransfer.dropEffect = 'copy';
        evt.preventDefault();
    })
    .on('drop', '.dnd-overlay', function (evt) {
        evt.preventDefault();
        evt.stopPropagation();

        var textarea  = $('#sequence');
        var indicator = $('#sequence-file');
        textarea.focus();

        var files = evt.originalEvent.dataTransfer.files;
        if (files.length > 1) {
            dndError('dnd-multi');
            return;
        }

        var file = files[0];
        if (file.size > 10 * 1048576) {
            $('#dnd-large-file-notification .filename').text(file.name);
            dndError('dnd-large-file');
            return;
        }

        var reader = new FileReader();
        reader.onload = function (e) {
            var putativeFastaText = e.target.result;
            // Query file must start with > (possibly after whitespace), or be entirely alphanumeric+whitespace
            if (/^\s*>/.test(putativeFastaText) || /^\s*[0-9A-Za-z\*]+[\s0-9A-Za-z\*]*$/.test(putativeFastaText)) {
                textarea.val(putativeFastaText);
                indicator.text(file.name);
                tgtMarker.hide();
            } else {
                // apparently not FASTA
                $('#dnd-format-notification .filename').text(file.name);
                dndError('dnd-format');
            }
        };
        reader.onerror = function (e) {
            // Couldn't read. Means dropped stuff wasn't FASTA file.
            $('#dnd-read-error-notification .filename').text(file.name);
            dndError('dnd-format');
        }
        reader.readAsText(file);
    });
    // end drag-and-drop

    SS.$sequence.change(function () {
        if (SS.$sequence.val()) {
            // Calculation below is based on -
            // http://chris-spittles.co.uk/jquery-calculate-scrollbar-width/
            var sequenceControlsRight = SS.$sequence[0].offsetWidth -
                SS.$sequence[0].clientWidth;
            SS.$sequenceControls.css('right', sequenceControlsRight + 17);
            SS.$sequenceControls.removeClass('hidden');
        }
        else {
            SS.$sequenceFile.empty();
            SS.$sequenceControls.addClass('hidden');
            SS.$sequence.parent().removeClass('has-error');
        }
    });

    // Handle clearing query sequences(s) when x button is pressed.
    $('#btn-sequence-clear').click(function (e) {
        $('#sequence').val("").focus();
    })

    // pre-select if only on db
    $.onedb();

    // Handles the form submission when Ctrl+Enter is pressed anywhere on page
    $(document).bind("keydown", function (e) {
        if (e.ctrlKey && e.keyCode === 13 && !$('#method').is(':disabled')) {
            $('#method').trigger('submit');
        }
    });

    $('#sequence').on('sequence_type_changed', function (event, type) {
        clearTimeout(notification_timeout);
        $(this).parent().removeClass('has-error');
        $('.notifications .active').hide().removeClass('active');

        if (type) {
            $('#' + type + '-sequence-notification').show('drop', {direction: 'up'}).addClass('active');

            notification_timeout = setTimeout(function () {
                $('.notifications .active').hide('drop', {direction: 'up'}).removeClass('active');
            }, 5000);

            if (type === 'mixed') {
                $(this).parent().addClass('has-error');
            }
        }
    });

    $('body').click(function () {
        $('.notifications .active').hide('drop', {direction: 'up'}).removeClass('active');
    });

    $('.databases').on('database_type_changed', function (event, type) {
        switch (type) {
            case 'protein':
                $('.databases.nucleotide input:checkbox').uncheck().disable();
                $('.databases.nucleotide .checkbox').addClass('disabled');
                break;
            case 'nucleotide':
                $('.databases.protein input:checkbox').uncheck().disable();
                $('.databases.protein .checkbox').addClass('disabled');
                break;
            default:
                $('.databases input:checkbox').enable();
                $('.databases .checkbox').removeClass('disabled');
                break;
        }
    });

    $('form').on('blast_method_changed', function (event, methods){
        // reset
        $('#methods .dropdown-menu').html('');
        $('#method').disable().val('').html('blast');
        $('#methods').removeClass('btn-group').children('.dropdown-toggle').hide();

        if (methods) {
            var method = methods.shift();

            $('#method').enable().val(method).html(SS.decorate(method));

            if (methods.length >=1) {
                $('#methods').addClass('btn-group').
                    children('.dropdown-toggle').show();

                var methods_list = $.map(methods, function (method, _) {
                    return "
" + SS.decorate(method) + "
";
                }).join('');

                $('#methods .dropdown-menu').html(methods_list);
            }

            // jiggle
            $("#methods").effect("bounce", { times:5, direction: 'left', distance: 12 }, 120);
        }
    });

    // The list of possible blast methods is dynamically generated.  So we
    // leverage event bubbling and delegation to trap 'click' event on the list items.
    // Please see : http://api.jquery.com/on/#direct-and-delegated-events

    $(document).on("click", "#methods .dropdown-menu li", function(event) {
        var clicked = $(this);
        var mbutton = $('#method');
        var old_method = mbutton.text();
        var new_method = clicked.text();

        // swap
        clicked.html(SS.decorate(old_method));
        mbutton.val(new_method).html(SS.decorate(new_method));

        // jiggle
        $("#methods").effect("bounce", { times:5, direction: 'left', distance: 12 }, 120);
    });

    $('.result').on('click', "[data-toggle='collapse']", function (event) {
        event.preventDefault();
        $(this).find('.fa').toggleClass('fa-rotate-270');
    });

    $('.result').on('click', '.view-sequence', function (event) {
        event.preventDefault();
        var clicked = $(event.target);

        var url = clicked.attr('href');
        $.get(url)
        .done(function (sequences) {
            $('#fasta').html(sequences).modal();
        })
        .fail(function (jqXHR, status, error) {
            //alert user
            if (jqXHR.responseText) {
                $("#error").html(jqXHR.responseText).modal();
            }
            else {
                $("#error-no-response").modal();
            }
        });
    });

    $('#fasta').on('change', '.hits-box:checkbox', function(event) {
        var checkboxes = $('.hits-box:checkbox').length,
            checked_boxes = $('.hits-box:checkbox:checked').length,
            container = $('.fasta-download'),
            text = container.html();
        if (checked_boxes > 0 &&
            checked_boxes != checkboxes) {
            container.html(text.replace('all', 'selected'));
        }
        else {
            container.html(text.replace('selected', 'all'));
        }
    });

    $('#fasta').on('click', '.fasta-download', function(event) {
        var sequence_ids = $('.hits-box:checkbox:checked').map(function () {
                return this.value;
            }).get(),
            database_ids = $(this).data().databases;

        if (sequence_ids.length < 1) {
            sequence_ids = $('.hits-box:checkbox').map(function() {
                return this.value;
            }).get();
        }
        var url = "get_sequence/?sequence_ids=" +
                  sequence_ids.join(' ') + "&database_ids=" + database_ids +
                  "&download=fasta";
        this.href = url;
    });

    $('#blast').submit(function(){
        //parse AJAX URL
        var action = $(this).attr('action');
        var index  = action.indexOf('#');
        var url    = action.slice(0, index);
        var hash   = action.slice(index, action.length);

        // reset hash so we can always _jump_ back to result
        location.hash = '';

        // show activity spinner
        $('#spinner').modal();

        // BLAST now
        var data = ($(this).serialize() + '&method=' + $('#method').val());
        $.post(url, data).
          done(function (data) {
            // BLASTed successfully

            // display the result
            $('.result').html(data).show();

            // affix the index
            var $index = $('.index');
            $index.affix({
                offset: {
                    top: $index.offset().top
                }
            })
            .width($index.width());

            //jump to the results
            location.hash = hash;

            SS.generateGraphicalOverview();

            $('body').scrollspy({target: '.index-container'});
        }).
          fail(function (jqXHR, status, error) {
            //alert user
            if (jqXHR.responseText) {
                $("#error").html(jqXHR.responseText).modal();
            }
            else {
                $("#error-no-response").modal();
            }
        }).
          always(function () {
            // BLAST complete (succefully or otherwise)
            // remove progress notification
            $('#spinner').modal('hide');
        });

        return false;
    });
});
<commit_msg>Drag n Drop: Remove dead code. Improved FASTA detection regex.<commit_after>//helpers methods to disable, enable, and uncheck radio buttons and checkboxes
(function( $ ){
    //disable an element
    $.fn.disable = function() {
        return this.attr('disabled', 'disabled');
    };
})( jQuery );

(function( $ ){
    //enable an element
    $.fn.enable = function() {
        return this.removeAttr('disabled');
    };
})( jQuery );

(function( $ ){
    //uncheck an element
    $.fn.uncheck = function() {
        return this.removeAttr('checked');
    };
})( jQuery );

(function( $ ){
    //check an element
    $.fn.check = function() {
        return this.attr('checked', 'checked');
    };
})( jQuery );

(function( $ ){
    //(pre-)check the only active database checkbox
    $.onedb = function(selector) {
        active_dbs = $(".databases input[type=checkbox]").not(":disabled")
        if (active_dbs.length == 1){
            active_dbs.check()
        }
        return active_dbs;
    };
})( jQuery );

(function ($) {
    $.fn.poll = function () {
        var that, val, tmp;

        that = this;
        val  = that.val();

        (function ping () {
            tmp = that.val();

            if (tmp != val){
                val = tmp;
                that.change();
            }

            setTimeout(ping, 100);
        }());

        return this;
    };
}(jQuery));

/*
    SS - SequenceServer's JavaScript module

    Define a global SS (acronym for SequenceServer) object containing the
    following methods:

        main():
            Initializes SequenceServer's various modules.
*/

//define global SS object
var SS;
if (!SS) {
    SS = {};
}

//SS module
(function () {

    // Starts with >.
    SS.FASTA_FORMAT = /^>/;

    SS.decorate = function (name) {
      return name.match(/(.?)(blast)(.?)/).slice(1).map(function (token, _) {
        if (token) {
            if (token !== 'blast'){
                return '' + token + '';
            }
            else {
              return token;
            }
        }
      }).join('');
    };

    SS.generateGraphicalOverview = function () {

        $("[data-graphit='overview']").each(function () {
            $.graphIt(this, 0, 20);
        });

    };

    SS.init = function () {
        this.$sequence = $('#sequence');
        this.$sequenceFile = $('#sequence-file');
        this.$sequenceControls = $('.sequence-controls');

        this.$sequence.poll();

        SS.blast.init();
    }
}()); //end SS module

$(document).ready(function(){
    SS.init();

    var notification_timeout;

    // drag-and-drop code
    var tgtMarker = $('.dnd-overlay');
    var $sequence = $('#sequence');

    var dndError = function (id) {
        $('.dnd-error').hide();
        $('#' + id + '-notification').show('drop', {direction: 'up'});
        tgtMarker.effect('fade', 2500);
    };

    $(document)
    .on('dragenter', function (evt) {
        // Based on http://stackoverflow.com/a/8494918/1205465.
        // Contrary to what the above link says, the snippet below can't
        // distinguish directories from files. We handle that on drop.
        var dt = evt.originalEvent.dataTransfer;
        var isFile = dt.types && ((dt.types.indexOf &&  // Chrome and Safari
                                  dt.types.indexOf('Files') != -1) ||
                                  (dt.types.contains && // Firefox
                                   dt.types.contains('application/x-moz-file')));

        if (!isFile) { return; }

        tgtMarker.show();
        dt.effectAllowed = 'copy';
        if ($sequence.val() === '') {
            $('.dnd-overlay-overwrite').hide();
            $('.dnd-overlay-drop').show('drop', {direction: 'down'}, 'fast');
        }
        else {
            $('.dnd-overlay-drop').hide();
            $('.dnd-overlay-overwrite').show('drop', {direction: 'down'}, 'fast');
        }
    })
    .on('dragleave', '.dnd-overlay', function (evt) {
        tgtMarker.hide();
        $('.dnd-overlay-drop').hide();
        $('.dnd-overlay-overwrite').hide();
    })
    .on('dragover', '.dnd-overlay', function (evt) {
        evt.originalEvent.dataTransfer.dropEffect = 'copy';
        evt.preventDefault();
    })
    .on('drop', '.dnd-overlay', function (evt) {
        evt.preventDefault();
        evt.stopPropagation();

        var textarea  = $('#sequence');
        var indicator = $('#sequence-file');
        textarea.focus();

        var files = evt.originalEvent.dataTransfer.files;
        if (files.length > 1) {
            dndError('dnd-multi');
            return;
        }

        var file = files[0];
        if (file.size > 10 * 1048576) {
            dndError('dnd-large-file');
            return;
        }

        var reader = new FileReader();
        reader.onload = function (e) {
            var content = e.target.result;
            if (SS.FASTA_FORMAT.test(content)) {
                textarea.val(content);
                indicator.text(file.name);
                tgtMarker.hide();
            } else {
                // apparently not FASTA
                dndError('dnd-format');
            }
        };
        reader.onerror = function (e) {
            // Couldn't read. Means dropped stuff wasn't FASTA file.
            dndError('dnd-format');
        }
        reader.readAsText(file);
    });
    // end drag-and-drop

    SS.$sequence.change(function () {
        if (SS.$sequence.val()) {
            // Calculation below is based on -
            // http://chris-spittles.co.uk/jquery-calculate-scrollbar-width/
            var sequenceControlsRight = SS.$sequence[0].offsetWidth -
                SS.$sequence[0].clientWidth;
            SS.$sequenceControls.css('right', sequenceControlsRight + 17);
            SS.$sequenceControls.removeClass('hidden');
        }
        else {
            SS.$sequenceFile.empty();
            SS.$sequenceControls.addClass('hidden');
            SS.$sequence.parent().removeClass('has-error');
        }
    });

    // Handle clearing query sequences(s) when x button is pressed.
    $('#btn-sequence-clear').click(function (e) {
        $('#sequence').val("").focus();
    })

    // pre-select if only on db
    $.onedb();

    // Handles the form submission when Ctrl+Enter is pressed anywhere on page
    $(document).bind("keydown", function (e) {
        if (e.ctrlKey && e.keyCode === 13 && !$('#method').is(':disabled')) {
            $('#method').trigger('submit');
        }
    });

    $('#sequence').on('sequence_type_changed', function (event, type) {
        clearTimeout(notification_timeout);
        $(this).parent().removeClass('has-error');
        $('.notifications .active').hide().removeClass('active');

        if (type) {
            $('#' + type + '-sequence-notification').show('drop', {direction: 'up'}).addClass('active');

            notification_timeout = setTimeout(function () {
                $('.notifications .active').hide('drop', {direction: 'up'}).removeClass('active');
            }, 5000);

            if (type === 'mixed') {
                $(this).parent().addClass('has-error');
            }
        }
    });

    $('body').click(function () {
        $('.notifications .active').hide('drop', {direction: 'up'}).removeClass('active');
    });

    $('.databases').on('database_type_changed', function (event, type) {
        switch (type) {
            case 'protein':
                $('.databases.nucleotide input:checkbox').uncheck().disable();
                $('.databases.nucleotide .checkbox').addClass('disabled');
                break;
            case 'nucleotide':
                $('.databases.protein input:checkbox').uncheck().disable();
                $('.databases.protein .checkbox').addClass('disabled');
                break;
            default:
                $('.databases input:checkbox').enable();
                $('.databases .checkbox').removeClass('disabled');
                break;
        }
    });

    $('form').on('blast_method_changed', function (event, methods){
        // reset
        $('#methods .dropdown-menu').html('');
        $('#method').disable().val('').html('blast');
        $('#methods').removeClass('btn-group').children('.dropdown-toggle').hide();

        if (methods) {
            var method = methods.shift();

            $('#method').enable().val(method).html(SS.decorate(method));

            if (methods.length >=1) {
                $('#methods').addClass('btn-group').
                    children('.dropdown-toggle').show();

                var methods_list = $.map(methods, function (method, _) {
                    return "
" + SS.decorate(method) + "
";
                }).join('');

                $('#methods .dropdown-menu').html(methods_list);
            }

            // jiggle
            $("#methods").effect("bounce", { times:5, direction: 'left', distance: 12 }, 120);
        }
    });

    // The list of possible blast methods is dynamically generated.  So we
    // leverage event bubbling and delegation to trap 'click' event on the list items.
    // Please see : http://api.jquery.com/on/#direct-and-delegated-events

    $(document).on("click", "#methods .dropdown-menu li", function(event) {
        var clicked = $(this);
        var mbutton = $('#method');
        var old_method = mbutton.text();
        var new_method = clicked.text();

        // swap
        clicked.html(SS.decorate(old_method));
        mbutton.val(new_method).html(SS.decorate(new_method));

        // jiggle
        $("#methods").effect("bounce", { times:5, direction: 'left', distance: 12 }, 120);
    });

    $('.result').on('click', "[data-toggle='collapse']", function (event) {
        event.preventDefault();
        $(this).find('.fa').toggleClass('fa-rotate-270');
    });

    $('.result').on('click', '.view-sequence', function (event) {
        event.preventDefault();
        var clicked = $(event.target);

        var url = clicked.attr('href');
        $.get(url)
        .done(function (sequences) {
            $('#fasta').html(sequences).modal();
        })
        .fail(function (jqXHR, status, error) {
            //alert user
            if (jqXHR.responseText) {
                $("#error").html(jqXHR.responseText).modal();
            }
            else {
                $("#error-no-response").modal();
            }
        });
    });

    $('#fasta').on('change', '.hits-box:checkbox', function(event) {
        var checkboxes = $('.hits-box:checkbox').length,
            checked_boxes = $('.hits-box:checkbox:checked').length,
            container = $('.fasta-download'),
            text = container.html();
        if (checked_boxes > 0 &&
            checked_boxes != checkboxes) {
            container.html(text.replace('all', 'selected'));
        }
        else {
            container.html(text.replace('selected', 'all'));
        }
    });

    $('#fasta').on('click', '.fasta-download', function(event) {
        var sequence_ids = $('.hits-box:checkbox:checked').map(function () {
                return this.value;
            }).get(),
            database_ids = $(this).data().databases;

        if (sequence_ids.length < 1) {
            sequence_ids = $('.hits-box:checkbox').map(function() {
                return this.value;
            }).get();
        }
        var url = "get_sequence/?sequence_ids=" +
                  sequence_ids.join(' ') + "&database_ids=" + database_ids +
                  "&download=fasta";
        this.href = url;
    });

    $('#blast').submit(function(){
        //parse AJAX URL
        var action = $(this).attr('action');
        var index  = action.indexOf('#');
        var url    = action.slice(0, index);
        var hash   = action.slice(index, action.length);

        // reset hash so we can always _jump_ back to result
        location.hash = '';

        // show activity spinner
        $('#spinner').modal();

        // BLAST now
        var data = ($(this).serialize() + '&method=' + $('#method').val());
        $.post(url, data).
          done(function (data) {
            // BLASTed successfully

            // display the result
            $('.result').html(data).show();

            // affix the index
            var $index = $('.index');
            $index.affix({
                offset: {
                    top: $index.offset().top
                }
            })
            .width($index.width());

            //jump to the results
            location.hash = hash;

            SS.generateGraphicalOverview();

            $('body').scrollspy({target: '.index-container'});
        }).
          fail(function (jqXHR, status, error) {
            //alert user
            if (jqXHR.responseText) {
                $("#error").html(jqXHR.responseText).modal();
            }
            else {
                $("#error-no-response").modal();
            }
        }).
          always(function () {
            // BLAST complete (succefully or otherwise)
            // remove progress notification
            $('#spinner').modal('hide');
        });

        return false;
    });
});
<|endoftext|>


<commit_before>window.ScheduleView = Backbone.View.extend({
    el: $("#content"),
    get_templateHTML: function () {
        return template.schedule();
    },
    get_collection: function () {
        return this.collection;
    },
    make_event: function(occurrence) {
        // make a Media.Occurrence into a fullCalendar event
        return _(occurrence.attributes).clone();
    },
    all_events: function() {
        return this.get_collection().map(this.make_event);
    },
    historical_events: [
        {
            title: 'WALLKINTUN SALE AL AIRE !',
            start: new Date(2012, 11, 7, 22, 0),
            end:   new Date(2012, 11, 8, 6, 0),
            allDay: false,
        },
    ],
    addOne: function (occurrence) {
        this.calendar.fullCalendar('refetchEvents');
    },
    addAll: function() {
        this.calendar.fullCalendar('refetchEvents');
    },
    initialize: function () {
        var self = this;
        self.collection = this.get_collection();

        $(this.el).html(this.get_templateHTML());

        new UniverseListView({
            collection: Universe,
            el: $('#universe', this.el),
            draggable: true,
        });

        this.render();
    },
/*    saveEvent: function (event) {
        // we filter out the event to only store what we really need

        console.log ('saving event:', event);
        var nattr = {
            title:  event.get('title'),
            start:  moment(event.get('start')).unix(),
            end:    moment(event.get('end')).unix(),
            allDay: event.get('allDay'),
            list:   event.get('list'),
        };

        event.clear({silent:true});
        event.save(nattr);

    },*/
    render: function () {
        console.log ('render calendar', $(this.el));
        var self = this;
        this.collection = this.get_collection();
        this.opts = {"timestamp":1361784505,
                     "timezoneOffset":"0",
                     "timeScale":"agendaWeek",
                     "timeInterval":"30",
                     "weekStartDay":"0"};
        this.calendar = $('#schedule_calendar', this.$el);

        var mainHeight = document.documentElement.clientHeight - 200 - 50;

        calendarEventSources = [
            function(start, end, callback) {
                callback(self.all_events());
            }
        ].concat(self.historical_events);

        this.calendar.fullCalendar({
            header: {
                left: 'prev, next, today',
                center: 'title',
                right: 'agendaDay, agendaWeek, month'
            },
            defaultView: this.opts.timeScale,
            slotMinutes: parseInt(this.opts.timeInterval),
            firstDay: this.opts.weekStartDay,
            editable: true,
            droppable: true,
            allDaySlot: false,
            axisFormat: 'H:mm',
            timeFormat: {
                agenda: 'H:mm{ - H:mm}',
                month: 'H:mm{ - H:mm}'
            },
            contentHeight: mainHeight,
            theme: true,
            lazyFetching: false,
            serverTimestamp: parseInt(this.opts.timestamp, 10),
            serverTimezoneOffset: parseInt(this.opts.timezoneOffset, 10),

            eventSources: calendarEventSources,

            //callbacks (in full-calendar-functions.js)
            viewDisplay: function ( view ) {
                view_name = view.name;

                if(view.name === 'agendaDay' || view.name === 'agendaWeek') {

                    var calendarEl = this;

                    var select = $('<select class="schedule_change_slots input_select"/>')
                        .append('<option value="1">1m</option>')
                        .append('<option value="5">5m</option>')
                        .append('<option value="10">10m</option>')
                        .append('<option value="15">15m</option>')
                        .append('<option value="30">30m</option>')
                        .append('<option value="60">60m</option>')
                        .change(function(){
                            var slotMin = $(this).val();
                            var opt = view.calendar.options;
                            var date = $(calendarEl).fullCalendar('getDate');

                            opt.slotMinutes = parseInt(slotMin);
                            opt.defaultView = view.name;
                            // event sources are not kept in the options, I don't know
                            //  if there's a way to retrieve them
                            opt.eventSources = calendarEventSources;

                            //re-initialize calendar with new slotmin options
                            $(calendarEl)
                                .fullCalendar('destroy')
                                .fullCalendar(opt)
                                .fullCalendar( 'gotoDate', date );

                            /** TODO: save view config
                             *
                            //save slotMin value to db
                            var url = '/Schedule/set-time-interval/format/json';
                            $.post(url, {timeInterval: slotMin});
                            */
                        });

                    var topLeft = $(view.element).find("table.fc-agenda-days > thead th:first");

                    select.width(topLeft.width())
                        .height(topLeft.height());

                    topLeft.empty()
                        .append(select);

                    var slotMin = view.calendar.options.slotMinutes;
                    $('.schedule_change_slots option[value="'+slotMin+'"]').attr('selected', 'selected');
                }

                if(($("#add-show-form").length == 1) && ($("#add-show-form").css('display')=='none') && ($('.fc-header-left > span').length == 5)) {

                    //userType is defined in bootstrap.php, and is derived from the currently logged in user.
                    if(userType == "A" || userType == "P"){
                        makeAddShowButton();
                    }
                }

                /* FIXME
                //save view name to db
                var url = '/Schedule/set-time-scale/format/json';
                $.post(url, {timeScale: view.name});
            */
            },
            dayClick: dayClick,
            eventRender: function (event, element, view) {
                var model = self.collection.where({list: event.list})[0];

                element.tooltip({
                    trigger: 'click',
                    placement: 'left',
                    title: function () {
                        console.log ("creating tooltip", model);
                        var target = document.createElement('div');
                        var view = new MediaListView({model: model,
                                                      noSearch:true,
                                                      el: target});
                        return view.render().el;
                    },
                });

                eventRender(event, element, view);
            },
            eventAfterRender: eventAfterRender,
            eventDrop: function(event, dayDelta, minuteDelta, allDay, revertFunc, jsEvent, ui, view) {
                var start = moment(event.start);
                var end = moment(event.end);
                self.collection.find(function(e){ return e.get("_id") == event._id }).save(
                    {start: start.unix(), end: end.unix()});
            },
            eventResize: eventResize,
            drop: function(date, allDay) {
                var list  = Universe.get(this.id);
                console.log ('drop->', self.collection.pluck('end'));

                var start = moment(date);
                var end   = moment(start);

                end.add('ms', list.get('duration'));
                var event = {
                    title:  list.get('name'),
                    list:   list.get('_id'),
                    start:  start.unix(), end: end.unix(),
                    allDay: allDay,
                };

                console.log ('to save', event);
                var item = self.collection.create (event);
                console.log (this, list, event, item);
            }
        });

                                             /*
            eventRender: function(event, element) {
                var model = self.collection.where({list: event.list})[0];

                element.tooltip({
                    trigger: 'click',
                    placement: 'left',
                    title: function () {
                        var target = document.createElement('div');
                        var view = new MediaListView({model: model,
                                                      noSearch:true,
                                                      el: target});
                        return view.render().el;
                    },
                });
            }
            });
                                             */

        _.bindAll(this,
                  'addOne',
                  'addAll'
                 );

        self.collection.bind('add',   this.addOne, this);
        self.collection.bind('reset', this.addAll, this);
//        self.collection.bind('remove',this.checkEmpty, this);
        self.collection.bind('all',   function (e, a) {console.log('got: ' + e, a);}, this);
        self.collection.bind('update',this.update, this);

        return this;
    },
});
<commit_msg>make variable local<commit_after>window.ScheduleView = Backbone.View.extend({
    el: $("#content"),
    get_templateHTML: function () {
        return template.schedule();
    },
    get_collection: function () {
        return this.collection;
    },
    make_event: function(occurrence) {
        // make a Media.Occurrence into a fullCalendar event
        return _(occurrence.attributes).clone();
    },
    all_events: function() {
        return this.get_collection().map(this.make_event);
    },
    historical_events: [
        {
            title: 'WALLKINTUN SALE AL AIRE !',
            start: new Date(2012, 11, 7, 22, 0),
            end:   new Date(2012, 11, 8, 6, 0),
            allDay: false,
        },
    ],
    addOne: function (occurrence) {
        this.calendar.fullCalendar('refetchEvents');
    },
    addAll: function() {
        this.calendar.fullCalendar('refetchEvents');
    },
    initialize: function () {
        var self = this;
        self.collection = this.get_collection();

        $(this.el).html(this.get_templateHTML());

        new UniverseListView({
            collection: Universe,
            el: $('#universe', this.el),
            draggable: true,
        });

        this.render();
    },
/*    saveEvent: function (event) {
        // we filter out the event to only store what we really need

        console.log ('saving event:', event);
        var nattr = {
            title:  event.get('title'),
            start:  moment(event.get('start')).unix(),
            end:    moment(event.get('end')).unix(),
            allDay: event.get('allDay'),
            list:   event.get('list'),
        };

        event.clear({silent:true});
        event.save(nattr);

    },*/
    render: function () {
        console.log ('render calendar', $(this.el));
        var self = this;
        this.collection = this.get_collection();
        this.opts = {"timestamp":1361784505,
                     "timezoneOffset":"0",
                     "timeScale":"agendaWeek",
                     "timeInterval":"30",
                     "weekStartDay":"0"};
        this.calendar = $('#schedule_calendar', this.$el);

        var mainHeight = document.documentElement.clientHeight - 200 - 50;

        var calendarEventSources = [
            function(start, end, callback) {
                callback(self.all_events());
            }
        ].concat(self.historical_events);

        this.calendar.fullCalendar({
            header: {
                left: 'prev, next, today',
                center: 'title',
                right: 'agendaDay, agendaWeek, month'
            },
            defaultView: this.opts.timeScale,
            slotMinutes: parseInt(this.opts.timeInterval),
            firstDay: this.opts.weekStartDay,
            editable: true,
            droppable: true,
            allDaySlot: false,
            axisFormat: 'H:mm',
            timeFormat: {
                agenda: 'H:mm{ - H:mm}',
                month: 'H:mm{ - H:mm}'
            },
            contentHeight: mainHeight,
            theme: true,
            lazyFetching: false,
            serverTimestamp: parseInt(this.opts.timestamp, 10),
            serverTimezoneOffset: parseInt(this.opts.timezoneOffset, 10),

            eventSources: calendarEventSources,

            //callbacks (in full-calendar-functions.js)
            viewDisplay: function ( view ) {
                view_name = view.name;

                if(view.name === 'agendaDay' || view.name === 'agendaWeek') {

                    var calendarEl = this;

                    var select = $('<select class="schedule_change_slots input_select"/>')
                        .append('<option value="1">1m</option>')
                        .append('<option value="5">5m</option>')
                        .append('<option value="10">10m</option>')
                        .append('<option value="15">15m</option>')
                        .append('<option value="30">30m</option>')
                        .append('<option value="60">60m</option>')
                        .change(function(){
                            var slotMin = $(this).val();
                            var opt = view.calendar.options;
                            var date = $(calendarEl).fullCalendar('getDate');

                            opt.slotMinutes = parseInt(slotMin);
                            opt.defaultView = view.name;
                            // event sources are not kept in the options, I don't know
                            //  if there's a way to retrieve them
                            opt.eventSources = calendarEventSources;

                            //re-initialize calendar with new slotmin options
                            $(calendarEl)
                                .fullCalendar('destroy')
                                .fullCalendar(opt)
                                .fullCalendar( 'gotoDate', date );

                            /** TODO: save view config
                             *
                            //save slotMin value to db
                            var url = '/Schedule/set-time-interval/format/json';
                            $.post(url, {timeInterval: slotMin});
                            */
                        });

                    var topLeft = $(view.element).find("table.fc-agenda-days > thead th:first");

                    select.width(topLeft.width())
                        .height(topLeft.height());

                    topLeft.empty()
                        .append(select);

                    var slotMin = view.calendar.options.slotMinutes;
                    $('.schedule_change_slots option[value="'+slotMin+'"]').attr('selected', 'selected');
                }

                if(($("#add-show-form").length == 1) && ($("#add-show-form").css('display')=='none') && ($('.fc-header-left > span').length == 5)) {

                    //userType is defined in bootstrap.php, and is derived from the currently logged in user.
                    if(userType == "A" || userType == "P"){
                        makeAddShowButton();
                    }
                }

                /* FIXME
                //save view name to db
                var url = '/Schedule/set-time-scale/format/json';
                $.post(url, {timeScale: view.name});
            */
            },
            dayClick: dayClick,
            eventRender: function (event, element, view) {
                var model = self.collection.where({list: event.list})[0];

                element.tooltip({
                    trigger: 'click',
                    placement: 'left',
                    title: function () {
                        console.log ("creating tooltip", model);
                        var target = document.createElement('div');
                        var view = new MediaListView({model: model,
                                                      noSearch:true,
                                                      el: target});
                        return view.render().el;
                    },
                });

                eventRender(event, element, view);
            },
            eventAfterRender: eventAfterRender,
            eventDrop: function(event, dayDelta, minuteDelta, allDay, revertFunc, jsEvent, ui, view) {
                var start = moment(event.start);
                var end = moment(event.end);
                self.collection.find(function(e){ return e.get("_id") == event._id }).save(
                    {start: start.unix(), end: end.unix()});
            },
            eventResize: eventResize,
            drop: function(date, allDay) {
                var list  = Universe.get(this.id);
                console.log ('drop->', self.collection.pluck('end'));

                var start = moment(date);
                var end   = moment(start);

                end.add('ms', list.get('duration'));
                var event = {
                    title:  list.get('name'),
                    list:   list.get('_id'),
                    start:  start.unix(), end: end.unix(),
                    allDay: allDay,
                };

                console.log ('to save', event);
                var item = self.collection.create (event);
                console.log (this, list, event, item);
            }
        });

                                             /*
            eventRender: function(event, element) {
                var model = self.collection.where({list: event.list})[0];

                element.tooltip({
                    trigger: 'click',
                    placement: 'left',
                    title: function () {
                        var target = document.createElement('div');
                        var view = new MediaListView({model: model,
                                                      noSearch:true,
                                                      el: target});
                        return view.render().el;
                    },
                });
            }
            });
                                             */

        _.bindAll(this,
                  'addOne',
                  'addAll'
                 );

        self.collection.bind('add',   this.addOne, this);
        self.collection.bind('reset', this.addAll, this);
//        self.collection.bind('remove',this.checkEmpty, this);
        self.collection.bind('all',   function (e, a) {console.log('got: ' + e, a);}, this);
        self.collection.bind('update',this.update, this);

        return this;
    },
});
<|endoftext|>


<commit_before>function getPoll(poll, done) {
    $.ajax({
        url: '/' + poll,
        type: "GET",
        cache: false,
        success: function(data) {
            done(null, data);
        },
        error: function(xhr, status, err) {
            done(new Error(err));
        }
    });
}

function getAnswers(poll, done) {
    $.ajax({
        url: '/' + poll + '/resultados',
        type: "GET",
        cache: false,
        success: function(data) {
            done(null, data);
        },
        error: function(xhr, status, err) {
            done(new Error(err));
        }
    });
}

function postAnswers(poll, answers, done) {
    $.ajax({
        url: '/' + poll,
        type: "PUT",
        data: JSON.stringify({
            "answers": answers
        }),
        contentType: 'application/json',
        cache: false,
        success: function(data) {
            done(null, data);
        },
        error: function(xhr, status, err) {
            done(new Error(err));
        }
    });
}

$(function() {
    $(".poll").each( function() {
	var name =  $(this).attr( 'data-poll-name');
	console.log(name);
	getPoll(name, function(err, res) {
	console.log( "getPoll " + name );
        if (err) {
            console.log(err);
        } else {
            $.each(res, function(id_que, val_que) {
                console.log(val_que);
                $(".poll").append("
" + val_que.question + "
");
                var def = false;

                $.each(val_que.options, function(id_ans, val_ans) {
                    if (!def) {
                        $(".poll").append("<input type='radio' name='" + val_que.id + "' value='" + id_ans + "' checked='checked'>" + val_ans + "
");
                        def = true;
                    } else {
                        $(".poll").append("<input type='radio' name='" + val_que.id + "' value='" + id_ans + "'>" + val_ans + "
");
                    }
                });

                $(".poll").append("
<button type='button' class='submit' id='" + val_que.id + "'>Enviar respuestas</button>");

                $(".submit").click(function() {
                    function Answer(id, answer) {
                        this.id = id;
                        this.answer = answer;
                    }

                    var answers = [];

                    $.each($('input[type=radio]'), function() {
                        if ($(this).is(":checked")) {
                            //console.log("Question: " + $(this).attr("name") + " - Answer: " + $(this).val());
                            var answer = new Answer($(this).attr("name"), $(this).val());
                            answers.push(answer);
                        }
                    });

                    //console.log(JSON.stringify(answers));

                    postAnswers(name, answers, function(err, res) {
                        if (err) {
                            console.log(err);
                        } else {
                            console.log(res);
                        }
                    });
                });

            });
        }
	});
    });

});
<commit_msg>Integrado con reveal.js<commit_after>function getPoll(poll, done) {
    $.ajax({
        url: '/' + poll,
        type: "GET",
        cache: false,
        success: function(data) {
            done(null, data);
        },
        error: function(xhr, status, err) {
            done(new Error(err));
        }
    });
}

function getAnswers(poll, done) {
    $.ajax({
        url: '/' + poll + '/resultados',
        type: "GET",
        cache: false,
        success: function(data) {
            done(null, data);
        },
        error: function(xhr, status, err) {
            done(new Error(err));
        }
    });
}

function postAnswers(poll, answers, done) {
    $.ajax({
        url: '/' + poll,
        type: "PUT",
        data: JSON.stringify({
            "answers": answers
        }),
        contentType: 'application/json',
        cache: false,
        success: function(data) {
            done(null, data);
        },
        error: function(xhr, status, err) {
            done(new Error(err));
        }
    });
}

$(function() {
    $(".poll").each( function() {
	var this_div = $(this);
	var name =  this_div.attr( 'data-poll-name');
	console.log(name);

	getPoll(name, function(err, res) {
	    console.log( "getPoll " + name );

            if (err) {
		console.log(err);
            } else {
		$.each(res, function(id_que, val_que) {
                    console.log(val_que);
                    this_div.append("
" + val_que.question + "
");
                    var def = false;

                    $.each(val_que.options, function(id_ans, val_ans) {
			if (!def) {
                            this_div.append("<input type='radio' name='" + val_que.id + "' value='" + id_ans + "' checked='checked'>" + val_ans + "
");
                            def = true;
			} else {
                            this_div.append("<input type='radio' name='" + val_que.id + "' value='" + id_ans + "'>" + val_ans + "
");
			}
                    });

                    this_div.append("
<button type='button' class='submit' id='" + val_que.id + "'>Enviar respuestas</button>");

                    $(".submit").click(function() {
			function Answer(id, answer) {
                            this.id = id;
                            this.answer = answer;
			}

			var answers = [];

			$.each($('input[type=radio]'), function() {
                            if (this_div.is(":checked")) {
				//console.log("Question: " + this_div.attr("name") + " - Answer: " + this_div.val());
				var answer = new Answer(this_div.attr("name"), this_div.val());
				answers.push(answer);
                            }
			});

			//console.log(JSON.stringify(answers));

			postAnswers(name, answers, function(err, res) {
                            if (err) {
				console.log(err);
                            } else {
				console.log(res);
                            }
			});
                    });

		});
            }
	});
    });

});
<|endoftext|>


<commit_before>function getPoll(poll,done){
    $.ajax({
			url: '/'+poll,
			type: "GET",
			dataType: 'json',
			cache: false,
			success: function(data) {
				done(null, data);
			},
			error: function(xhr, status, err) {
				done(new Error(err));
			}
		});
}

$(function(){
    var name=$(".poll").attr('data-poll-name');
    console.log(name);
    getPoll(name,function(err,res){
        if(err) console.log(err);
        else {
            $(".poll").text(JSON.stringify(res));
        }
    });
});
<commit_msg>polleitor script<commit_after>function getPoll(poll,done){
    $.ajax({
			url: '/'+poll,
			type: "GET",
			cache: false,
			success: function(data) {
				done(null, data);
			},
			error: function(xhr, status, err) {
				done(new Error(err));
			}
		});
}

function getAnswers(poll,done){
    $.ajax({
            url: '/'+poll+'/resultados',
            type: "GET",
            cache: false,
            success: function(data) {
                done(null, data);
            },
            error: function(xhr, status, err) {
                done(new Error(err));
            }
        });
}
function postAnswers(poll,answers,done){
    $.ajax({
            url: '/'+poll,
            type: "POST",
            data: answers,
            dataType: 'json',
            cache: false,
            success: function(data) {
                done(null, data);
            },
            error: function(xhr, status, err) {
                done(new Error(err));
            }
        });
}

$(function(){
    var name=$(".poll").attr('data-poll-name');
    console.log(name);
    getPoll(name,function(err,res){
        if(err) console.log(err);
        else {
            $(".poll").text(JSON.stringify(res));
        }
    });
});
<|endoftext|>


<commit_before>// This class abstracts away most of the interaction with Spotify's API. All methods return promises
// which will be resolved once the requested resource has been returned from Spotify
function SpotifyRequestor(spotifyApi, timeRange, reportProgress) {
  this.s = spotifyApi;
  this.timeRange = timeRange;
  this.reportProgress = reportProgress || function() {};
  this.defaultPageSize = 50;
  this.maxResults = 1000;
  this.retryCount = 3;
}

// Helper function which will run fn more than once if the promise is rejected during execution.
// fn must be a function which returns a promise
SpotifyRequestor.prototype._runWithRetry = function(fn, actionDescription, retryCount) {
    retryCount = retryCount || this.retryCount;
    console.log("Running with retryCount of " + retryCount);

    function tryRunPromise() {
        return fn().then(function(data) {
            console.log("Promise '" + actionDescription + "' succeeded execution!");
            return Promise.resolve(data);
        }, function(err) {
            console.log("Error encountered. Current retryCount is = " + retryCount);
            if (retryCount > 0) {
                console.log(actionDescription + " failed. Trying again.");
                retryCount--;
                return tryRunPromise();
            } else {
                console.error("Out of retries, failing the call: " + actionDescription);
                Promise.reject(err);
            }
        });
    };

    return tryRunPromise();
}

// Helper function to make a request which returns a promise and process the data which the call returns.
// fn must be a function which returns a promise. rowProcessor will be called once for every row of data
// returned by the resolved fn promise. rowAccessor is an optional parameter to be called when fn resolves
// to access the array of objects for rowProcessor to handle
SpotifyRequestor.prototype._makeRequestAndProcessRows = function(description, fn, rowProcessor, rowAccessor) {
    console.log("Making request for " + description);
    rowAccessor = rowAccessor || function(data) { return data.items; };
    return new Promise(function(resolve, reject) {

         // Run this request using the retry logic we have
         return this._runWithRetry(fn, description).then(function(data) {
             console.log("Received Results for " + description + ". Number of rows: " + rowAccessor(data).length);
             var toRet = rowAccessor(data).map(rowProcessor);

            // Send back some paging information to the caller
            var paging = {
                offset : data.offset || 0,
                total : data.total || 0
            };

            resolve({rows: toRet, paging: paging});
         });
    }.bind(this));
}

// Helper function for paging through multiple requests. Takes the same parameters as _makeRequestAndProcessRows, but
// uses the returned paging information to make another request. the paging information will be applied to fn when
// it is called for each new page
SpotifyRequestor.prototype._makeRequestAndProcessRowsWithPaging = function(description, fn, rowProcessor, rowAccessor) {
    console.log("Making request with paging for " + description);
    var allRows = [];

    // Define a getPage helper function for getting a single page of data
    var getPage = function(limit, offset) {
        console.log("Getting a page of data with limit=" + limit + " and offset=" + offset);
        return this._makeRequestAndProcessRows(
            description,
            fn.bind(this, {limit: limit, offset: offset}), // bind the limit and offset in here
            rowProcessor,
            rowAccessor).then(function(result) {
                var nextOffset = result.paging.offset + this.defaultPageSize;
                allRows = allRows.concat(result.rows);
                var totalRows = result.paging.total < this.maxResults ? result.paging.total : this.maxResults;

                console.log("Received a page of data for " + description + ". nextOffset is "  + nextOffset + ". totalRows is " + result.paging.total + ". maxResults is " + this.maxResults);

                // Report our progress to the progress reporting function which was passed in
                this.reportProgress("Received data for " + description + ". Retrieved " + result.paging.offset + " of " + totalRows);
                if (nextOffset < result.paging.total && nextOffset < this.maxResults) {
                    return getPage(this.defaultPageSize, nextOffset);
                } else {
                    console.log("Done paging through results. Number of results was " + allRows.length)
                    return Promise.resolve(allRows);
                }
        }.bind(this));
    }.bind(this);

    return getPage(this.defaultPageSize, 0);
}

// Helper function for calling a fn which takes in an array of ids. If the call has a limited blockSize, the requests
// will be broken up. The results will be recombined and returned in the same order as ids
SpotifyRequestor.prototype._getCollectionFromIds = function(ids, blockSize, description, fn, rowProcessor, rowAccessor) {
    console.log("Retrieving a collection for " + description + ". " + ids.length + " ids are requested with blockSize " + blockSize);

    // Request blockSize ids at a time
    var idBlocks = [];
    var currBlock = undefined;
    for(var i = 0; i < ids.length; i++) {
        if (!currBlock || currBlock.length == blockSize) {
            currBlock = new Array();
            idBlocks.push(currBlock);
        }

        currBlock.push(ids[i]);
    }

    console.log("Created " + idBlocks.length + " blocks");

    // Allocate a results array which will will insert all of our results into. This must return
    // The results in the order which ids were passed in
    var resultBlocks = new Array(idBlocks.length);

    var promises = [];
    for (var i = 0; i < idBlocks.length; i++) {
        // This function will get called when each promise finishes
        var insertValues = function(index, result) {
            // Place these values in their appropriate spot
            resultBlocks[index] = result.rows;
        }.bind(this, i);

        // Create a promise for each block
        promises.push(this._makeRequestAndProcessRows(
            description,
            fn.bind(this, idBlocks[i]),
            rowProcessor,
            rowAccessor)
            .then(insertValues)
        );
    }

    // Once all the promises have finished, combine the resultBlocks into a single array
    return Promise.all(promises).then(function() {
        console.log("All requests have finished. Combining arrays together for " + description);
        var merged = [].concat.apply([], resultBlocks);
        return merged;
    });
}

// Gets the user's top artists for the given time range
SpotifyRequestor.prototype.getMyTopArtists = function() {
    if (this._myTopArtists) {
        console.log("Returning cached list of top artists");
        return Promise.resolve(this._myTopArtists);
    }

    return this._makeRequestAndProcessRows(
        "getMyTopArtists",
        this.s.getMyTopArtists.bind(this, {time_range: this.timeRange}),
        function(artist) {
            console.log("Processing item " + artist.name);
            return {
                "followers": artist.followers ? artist.followers.total : 0,
                "genre1": artist.genres[0] || null,
                "genre2": artist.genres[1] || null,
                "href": artist.href,
                "id": artist.id,
                "image_link":artist.images[0] ? artist.images[0].url : null,
                "name": artist.name,
                "popularity":artist.popularity,
                "uri": artist.uri
            };
        }).then(function(result) {
            // Cache this off in case we need it later
            console.log("Finished retrieving top artists");
            this._myTopArtists = result.rows;
            return Promise.resolve(result.rows);
        }.bind(this));
}

// Gets the user's top tracks for the given time range
SpotifyRequestor.prototype.getMyTopTracks = function() {
    if (this._myTopTracks) {
        console.log("Returning cached list of top tracks");
        return Promise.resolve(this._myTopTracks);
    }

    return this._makeRequestAndProcessRows(
        "getMyTopTracks",
        this.s.getMyTopTracks.bind(this, {time_range: this.timeRange}),
        function(track) {
            console.log("Processing track " + track.name);
            return {
                "album_id": track.album.id,
                "artist_id": track.artists[0].id,
                "artist_name": track.artists[0].name,
                "duration_ms": track.duration_ms,
                "explicit": track.explicit,
                "href": track.href,
                "id": track.id,
                "name": track.name,
                "preview_url": track.preview_url,
                "track_number": track.track_number,
                "uri": track.uri
            };
        }).then(function(result) {
            // Cache this off in case we need it later
            console.log("Finished retrieving top tracks");
            this._myTopTracks = result.rows;
            return Promise.resolve(result.rows);
        }.bind(this));
}

// Gets the saved albums for this user
SpotifyRequestor.prototype.getMySavedAlbums = function() {
    if (this._mySavedAlbums) {
        console.log("Returning cached list of saved albums");
        return Promise.resolve(this._mySavedAlbums);
    }

    return this._makeRequestAndProcessRowsWithPaging(
        "getMySavedAlbums",
        this.s.getMySavedAlbums.bind(this),
        function(albumObject) {
            console.log("Processing album " + albumObject.album.name);
            return {
                "added_at": albumObject.added_at,
                "artist_id": albumObject.album.artists[0].id,
                "genre1": albumObject.album.genres[0] || null,
                "genre2": albumObject.album.genres[1] || null,
                "href": albumObject.album.href,
                "id": albumObject.album.id,
                "image_link": albumObject.album.images[0] ? albumObject.album.images[0].url : null,
                "name": albumObject.album.name,
                "popularity": albumObject.album.popularity,
                "release_date": albumObject.album.release_date,
                "type": albumObject.album.type,
                "uri": albumObject.album.uri
            };
        }).then(function(data) {
            this._mySavedAlbums = data;
            return data;
        }.bind(this));
}

// Gets the saved tracks for this user as well as some metrics for each track
SpotifyRequestor.prototype.getMySavedTracks = function() {
    if (this._mySavedTracks) {
        console.log("Returning cached list of saved tracks");
        return Promise.resolve(this._mySavedTracks);
    }

    return this._makeRequestAndProcessRowsWithPaging(
    "getMySavedTracks",
    this.s.getMySavedTracks.bind(this),
    function(trackObject) {
        console.log("Processing track " + trackObject.track.name);
        return {
            "added_at": trackObject.added_at,
            "album_id": trackObject.track.album.id,
            "artist_id": trackObject.track.artists[0].id,
            "artist_name": trackObject.track.artists[0].name,
            "duration_ms": trackObject.track.duration_ms,
            "explicit": trackObject.track.explicit,
            "href": trackObject.track.href,
            "id": trackObject.track.id,
            "name": trackObject.track.name,
            "preview_url": trackObject.track.preview_url,
            "track_number": trackObject.track.track_number,
            "uri": trackObject.track.uri
        };
    }).then(function(rows) {
        // We have retrieved all the tracks. Now let's decorate them with some metrics
        var ids = rows.map(function(row) { return row.id; });
        return this.getTrackFeatures(ids).then(function(trackFeatures) {
            var finalResults = rows;
            for(var i = 0; i < trackFeatures.length; i++) {
                for (var attrname in trackFeatures[i]) {
                    finalResults[i][attrname] = trackFeatures[i][attrname];
                }
            }

            this._mySavedTracks = finalResults;
            return finalResults;
        }.bind(this));
    }.bind(this));
}

// Gets the saved artists for the user
SpotifyRequestor.prototype.getMySavedArtists = function() {
    if (this._mySavedArtists) {
        console.log("Returning cached list of saved artists");
        return Promise.resolve(this._mySavedArtists);
    }

    // To get artists, we first must get all the user's albums and tracks since
    // there isn't an endpoint for getting artists
    var allArtists = [];
    var appendArtists = function(rows) {
        var artists = rows.map(function(row) { return row.artist_id; } );
        for(var i in artists) {
            if (allArtists.indexOf(artists[i]) == -1) {
                allArtists.push(artists[i]);
            }
        }
    };

    return Promise.all([
        spotifyRequestor.getMySavedAlbums().then(appendArtists),
        spotifyRequestor.getMySavedTracks().then(appendArtists)]).then(function() {
            console.log("Finished finding artists in albums and tracks. Number of artists=" + allArtists.length);
            return this.getArtists(allArtists).then(function(finalResults) {
                this._mySavedArtists = finalResults;
                return finalResults;
            }.bind(this));
        }.bind(this));
}

// Gets artists by their ids
SpotifyRequestor.prototype.getArtists = function(ids) {
    // TODO - cache the artists we have already retrieved by their id

    // Spotify only lets us request 50 artists at a time
    return this._getCollectionFromIds(ids, 50, "getArtists",
        this.s.getArtists.bind(this),
        function(artist) {
            return {
                "followers": artist.followers ? artist.followers.total : 0,
                "genre1": artist.genres[0] || null,
                "genre2": artist.genres[1] || null,
                "href": artist.href,
                "id": artist.id,
                "image_link": artist.images[0] ? artist.images[0].url : null,
                "name": artist.name,
                "popularity":artist.popularity,
                "uri": artist.uri
            };
        },
        function(data) { return data.artists; });
}

// Gets track features by their ids
SpotifyRequestor.prototype.getTrackFeatures = function(ids) {
    // TODO - cache the tracks we have already retrieved by their id

    return this._getCollectionFromIds(ids, 100, "getTrackFeatures",
        this.s.getAudioFeaturesForTracks.bind(this),
        function(audioFeature) {
            var keyLookup = {
                0 : "C",
                1 : "C♯",
                2 : "D",
                3 : "E♭",
                4 : "E",
                5 : "F",
                6 : "F♯",
                7 : "G",
                8 : "A♭",
                9 : "A",
                10 : "A♯",
                11 : "B" };
            return {
                "danceability": audioFeature.danceability,
                "energy": audioFeature.energy,
                "key": keyLookup[audioFeature.key],
                "loudness": audioFeature.loudness,
                "mode": audioFeature.mode == 1 ? "Major" : "Minor",
                "speechiness": audioFeature.speechiness,
                "acousticness": audioFeature.acousticness,
                "instrumentalness": audioFeature.instrumentalness,
                "liveness": audioFeature.liveness,
                "valence": audioFeature.valence,
                "tempo": audioFeature.tempo,
                "time_signature": audioFeature.time_signature
            }
        },
        function(data) { return data.audio_features; });
}
<commit_msg>Get 50 top artists and tracks<commit_after>// This class abstracts away most of the interaction with Spotify's API. All methods return promises
// which will be resolved once the requested resource has been returned from Spotify
function SpotifyRequestor(spotifyApi, timeRange, reportProgress) {
  this.s = spotifyApi;
  this.timeRange = timeRange;
  this.reportProgress = reportProgress || function() {};
  this.defaultPageSize = 50;
  this.maxResults = 1000;
  this.retryCount = 3;
}

// Helper function which will run fn more than once if the promise is rejected during execution.
// fn must be a function which returns a promise
SpotifyRequestor.prototype._runWithRetry = function(fn, actionDescription, retryCount) {
    retryCount = retryCount || this.retryCount;
    console.log("Running with retryCount of " + retryCount);

    function tryRunPromise() {
        return fn().then(function(data) {
            console.log("Promise '" + actionDescription + "' succeeded execution!");
            return Promise.resolve(data);
        }, function(err) {
            console.log("Error encountered. Current retryCount is = " + retryCount);
            if (retryCount > 0) {
                console.log(actionDescription + " failed. Trying again.");
                retryCount--;
                return tryRunPromise();
            } else {
                console.error("Out of retries, failing the call: " + actionDescription);
                Promise.reject(err);
            }
        });
    };

    return tryRunPromise();
}

// Helper function to make a request which returns a promise and process the data which the call returns.
// fn must be a function which returns a promise. rowProcessor will be called once for every row of data
// returned by the resolved fn promise. rowAccessor is an optional parameter to be called when fn resolves
// to access the array of objects for rowProcessor to handle
SpotifyRequestor.prototype._makeRequestAndProcessRows = function(description, fn, rowProcessor, rowAccessor) {
    console.log("Making request for " + description);
    rowAccessor = rowAccessor || function(data) { return data.items; };
    return new Promise(function(resolve, reject) {

         // Run this request using the retry logic we have
         return this._runWithRetry(fn, description).then(function(data) {
             console.log("Received Results for " + description + ". Number of rows: " + rowAccessor(data).length);
             var toRet = rowAccessor(data).map(rowProcessor);

            // Send back some paging information to the caller
            var paging = {
                offset : data.offset || 0,
                total : data.total || 0
            };

            resolve({rows: toRet, paging: paging});
         });
    }.bind(this));
}

// Helper function for paging through multiple requests. Takes the same parameters as _makeRequestAndProcessRows, but
// uses the returned paging information to make another request. the paging information will be applied to fn when
// it is called for each new page
SpotifyRequestor.prototype._makeRequestAndProcessRowsWithPaging = function(description, fn, rowProcessor, rowAccessor) {
    console.log("Making request with paging for " + description);
    var allRows = [];

    // Define a getPage helper function for getting a single page of data
    var getPage = function(limit, offset) {
        console.log("Getting a page of data with limit=" + limit + " and offset=" + offset);
        return this._makeRequestAndProcessRows(
            description,
            fn.bind(this, {limit: limit, offset: offset}), // bind the limit and offset in here
            rowProcessor,
            rowAccessor).then(function(result) {
                var nextOffset = result.paging.offset + this.defaultPageSize;
                allRows = allRows.concat(result.rows);
                var totalRows = result.paging.total < this.maxResults ? result.paging.total : this.maxResults;

                console.log("Received a page of data for " + description + ". nextOffset is "  + nextOffset + ". totalRows is " + result.paging.total + ". maxResults is " + this.maxResults);

                // Report our progress to the progress reporting function which was passed in
                this.reportProgress("Received data for " + description + ". Retrieved " + result.paging.offset + " of " + totalRows);
                if (nextOffset < result.paging.total && nextOffset < this.maxResults) {
                    return getPage(this.defaultPageSize, nextOffset);
                } else {
                    console.log("Done paging through results. Number of results was " + allRows.length)
                    return Promise.resolve(allRows);
                }
        }.bind(this));
    }.bind(this);

    return getPage(this.defaultPageSize, 0);
}

// Helper function for calling a fn which takes in an array of ids. If the call has a limited blockSize, the requests
// will be broken up. The results will be recombined and returned in the same order as ids
SpotifyRequestor.prototype._getCollectionFromIds = function(ids, blockSize, description, fn, rowProcessor, rowAccessor) {
    console.log("Retrieving a collection for " + description + ". " + ids.length + " ids are requested with blockSize " + blockSize);

    // Request blockSize ids at a time
    var idBlocks = [];
    var currBlock = undefined;
    for(var i = 0; i < ids.length; i++) {
        if (!currBlock || currBlock.length == blockSize) {
            currBlock = new Array();
            idBlocks.push(currBlock);
        }

        currBlock.push(ids[i]);
    }

    console.log("Created " + idBlocks.length + " blocks");

    // Allocate a results array which will will insert all of our results into. This must return
    // The results in the order which ids were passed in
    var resultBlocks = new Array(idBlocks.length);

    var promises = [];
    for (var i = 0; i < idBlocks.length; i++) {
        // This function will get called when each promise finishes
        var insertValues = function(index, result) {
            // Place these values in their appropriate spot
            resultBlocks[index] = result.rows;
        }.bind(this, i);

        // Create a promise for each block
        promises.push(this._makeRequestAndProcessRows(
            description,
            fn.bind(this, idBlocks[i]),
            rowProcessor,
            rowAccessor)
            .then(insertValues)
        );
    }

    // Once all the promises have finished, combine the resultBlocks into a single array
    return Promise.all(promises).then(function() {
        console.log("All requests have finished. Combining arrays together for " + description);
        var merged = [].concat.apply([], resultBlocks);
        return merged;
    });
}

// Gets the user's top artists for the given time range
SpotifyRequestor.prototype.getMyTopArtists = function() {
    if (this._myTopArtists) {
        console.log("Returning cached list of top artists");
        return Promise.resolve(this._myTopArtists);
    }

    return this._makeRequestAndProcessRows(
        "getMyTopArtists",
        this.s.getMyTopArtists.bind(this, {time_range: this.timeRange, limit: 50}),
        function(artist) {
            console.log("Processing item " + artist.name);
            return {
                "followers": artist.followers ? artist.followers.total : 0,
                "genre1": artist.genres[0] || null,
                "genre2": artist.genres[1] || null,
                "href": artist.href,
                "id": artist.id,
                "image_link":artist.images[0] ? artist.images[0].url : null,
                "name": artist.name,
                "popularity":artist.popularity,
                "uri": artist.uri
            };
        }).then(function(result) {
            // Cache this off in case we need it later
            console.log("Finished retrieving top artists");
            this._myTopArtists = result.rows;
            return Promise.resolve(result.rows);
        }.bind(this));
}

// Gets the user's top tracks for the given time range
SpotifyRequestor.prototype.getMyTopTracks = function() {
    if (this._myTopTracks) {
        console.log("Returning cached list of top tracks");
        return Promise.resolve(this._myTopTracks);
    }

    return this._makeRequestAndProcessRows(
        "getMyTopTracks",
        this.s.getMyTopTracks.bind(this, {time_range: this.timeRange, limit: 50}),
        function(track) {
            console.log("Processing track " + track.name);
            return {
                "album_id": track.album.id,
                "artist_id": track.artists[0].id,
                "artist_name": track.artists[0].name,
                "duration_ms": track.duration_ms,
                "explicit": track.explicit,
                "href": track.href,
                "id": track.id,
                "name": track.name,
                "preview_url": track.preview_url,
                "track_number": track.track_number,
                "uri": track.uri
            };
        }).then(function(result) {
            // Cache this off in case we need it later
            console.log("Finished retrieving top tracks");
            this._myTopTracks = result.rows;
            return Promise.resolve(result.rows);
        }.bind(this));
}

// Gets the saved albums for this user
SpotifyRequestor.prototype.getMySavedAlbums = function() {
    if (this._mySavedAlbums) {
        console.log("Returning cached list of saved albums");
        return Promise.resolve(this._mySavedAlbums);
    }

    return this._makeRequestAndProcessRowsWithPaging(
        "getMySavedAlbums",
        this.s.getMySavedAlbums.bind(this),
        function(albumObject) {
            console.log("Processing album " + albumObject.album.name);
            return {
                "added_at": albumObject.added_at,
                "artist_id": albumObject.album.artists[0].id,
                "genre1": albumObject.album.genres[0] || null,
                "genre2": albumObject.album.genres[1] || null,
                "href": albumObject.album.href,
                "id": albumObject.album.id,
                "image_link": albumObject.album.images[0] ? albumObject.album.images[0].url : null,
                "name": albumObject.album.name,
                "popularity": albumObject.album.popularity,
                "release_date": albumObject.album.release_date,
                "type": albumObject.album.type,
                "uri": albumObject.album.uri
            };
        }).then(function(data) {
            this._mySavedAlbums = data;
            return data;
        }.bind(this));
}

// Gets the saved tracks for this user as well as some metrics for each track
SpotifyRequestor.prototype.getMySavedTracks = function() {
    if (this._mySavedTracks) {
        console.log("Returning cached list of saved tracks");
        return Promise.resolve(this._mySavedTracks);
    }

    return this._makeRequestAndProcessRowsWithPaging(
    "getMySavedTracks",
    this.s.getMySavedTracks.bind(this),
    function(trackObject) {
        console.log("Processing track " + trackObject.track.name);
        return {
            "added_at": trackObject.added_at,
            "album_id": trackObject.track.album.id,
            "artist_id": trackObject.track.artists[0].id,
            "artist_name": trackObject.track.artists[0].name,
            "duration_ms": trackObject.track.duration_ms,
            "explicit": trackObject.track.explicit,
            "href": trackObject.track.href,
            "id": trackObject.track.id,
            "name": trackObject.track.name,
            "preview_url": trackObject.track.preview_url,
            "track_number": trackObject.track.track_number,
            "uri": trackObject.track.uri
        };
    }).then(function(rows) {
        // We have retrieved all the tracks. Now let's decorate them with some metrics
        var ids = rows.map(function(row) { return row.id; });
        return this.getTrackFeatures(ids).then(function(trackFeatures) {
            var finalResults = rows;
            for(var i = 0; i < trackFeatures.length; i++) {
                for (var attrname in trackFeatures[i]) {
                    finalResults[i][attrname] = trackFeatures[i][attrname];
                }
            }

            this._mySavedTracks = finalResults;
            return finalResults;
        }.bind(this));
    }.bind(this));
}

// Gets the saved artists for the user
SpotifyRequestor.prototype.getMySavedArtists = function() {
    if (this._mySavedArtists) {
        console.log("Returning cached list of saved artists");
        return Promise.resolve(this._mySavedArtists);
    }

    // To get artists, we first must get all the user's albums and tracks since
    // there isn't an endpoint for getting artists
    var allArtists = [];
    var appendArtists = function(rows) {
        var artists = rows.map(function(row) { return row.artist_id; } );
        for(var i in artists) {
            if (allArtists.indexOf(artists[i]) == -1) {
                allArtists.push(artists[i]);
            }
        }
    };

    return Promise.all([
        spotifyRequestor.getMySavedAlbums().then(appendArtists),
        spotifyRequestor.getMySavedTracks().then(appendArtists)]).then(function() {
            console.log("Finished finding artists in albums and tracks. Number of artists=" + allArtists.length);
            return this.getArtists(allArtists).then(function(finalResults) {
                this._mySavedArtists = finalResults;
                return finalResults;
            }.bind(this));
        }.bind(this));
}

// Gets artists by their ids
SpotifyRequestor.prototype.getArtists = function(ids) {
    // TODO - cache the artists we have already retrieved by their id

    // Spotify only lets us request 50 artists at a time
    return this._getCollectionFromIds(ids, 50, "getArtists",
        this.s.getArtists.bind(this),
        function(artist) {
            return {
                "followers": artist.followers ? artist.followers.total : 0,
                "genre1": artist.genres[0] || null,
                "genre2": artist.genres[1] || null,
                "href": artist.href,
                "id": artist.id,
                "image_link": artist.images[0] ? artist.images[0].url : null,
                "name": artist.name,
                "popularity":artist.popularity,
                "uri": artist.uri
            };
        },
        function(data) { return data.artists; });
}

// Gets track features by their ids
SpotifyRequestor.prototype.getTrackFeatures = function(ids) {
    // TODO - cache the tracks we have already retrieved by their id

    return this._getCollectionFromIds(ids, 100, "getTrackFeatures",
        this.s.getAudioFeaturesForTracks.bind(this),
        function(audioFeature) {
            var keyLookup = {
                0 : "C",
                1 : "C♯",
                2 : "D",
                3 : "E♭",
                4 : "E",
                5 : "F",
                6 : "F♯",
                7 : "G",
                8 : "A♭",
                9 : "A",
                10 : "A♯",
                11 : "B" };
            return {
                "danceability": audioFeature.danceability,
                "energy": audioFeature.energy,
                "key": keyLookup[audioFeature.key],
                "loudness": audioFeature.loudness,
                "mode": audioFeature.mode == 1 ? "Major" : "Minor",
                "speechiness": audioFeature.speechiness,
                "acousticness": audioFeature.acousticness,
                "instrumentalness": audioFeature.instrumentalness,
                "liveness": audioFeature.liveness,
                "valence": audioFeature.valence,
                "tempo": audioFeature.tempo,
                "time_signature": audioFeature.time_signature
            }
        },
        function(data) { return data.audio_features; });
}
<|endoftext|>


<commit_before>'use strict';

let FileFunnel = require('filefunnel.js');
let angular = require('angular');

let ff = angular.module('filefunnel', ['ngMaterial']);

// @ngInject
ff.controller('FFUploadController', function($scope, $mdDialog, options) {
  let ff = new FileFunnel(null, options);
  $scope.ff = ff;

  ff._elements.fileInput.on('change', () => {
    $scope.$apply();
  });

  ff.on('success', file => {
    if (ff.status === FileFunnel.status.COMPLETED) {
      $mdDialog.hide(ff.files);
    }
  }).on('error', file => {
    ff.progressType = 'determinate';
  }).on('progress', file => {
    ff.progressType = 'determinate';
  }).on('start', file => {
    ff.progressType = 'indeterminate';
  });

  $scope.FileFunnelStatus = FileFunnel.status;
  ff.progressType = 'determinate';
  console.log(ff);
});

ff.service('fileFunnelService', function($mdDialog) {
  const DEFAULTS = {
    server: "http://apptest.data.npolar.no/_file",
    accept: "*/*",
    chunked: true
  };

  let opts = {};
  let defineOptions = function (key, options) {
    opts[key] = Object.assign({}, DEFAULTS, options, {multiple:false});
  };

  let getOptions = function (key) {
    return opts[key] || DEFAULTS;
  };

  let showUpload = function(ev, path, options) {
    return $mdDialog.show({
      clickOutsideToClose: true,
      controller: 'FFUploadController',
      locals: {
        options: Object.assign({}, getOptions(path), options)
      },
      targetEvent: ev,
      template: require('./filefunnel.html')
    });
  };

  return {
    defineOptions,
    getOptions,
    showUpload,
    status: FileFunnel.status
  };
});

ff.directive('filefunnel', function(fileFunnelService) {
  return {
    restrict: 'A',
    //@ngInject
    controller($scope, $mdDialog) {
      $scope.showUpload = function(ev, target) {
        fileFunnelService.showUpload(ev, $scope.field.path)
          .then(files => {
            let options = fileFunnelService.getOptions($scope.field.path);
            if (target[0] instanceof HTMLInputElement) {
              target[0].value = options.server + files[0].location;
            } else if ($scope.field) {
              $scope.field.fields.forEach(field => {
                switch (field.id) {
                  case 'uri':
                    field.value = options.server + files[0].location;
                    break;
                  case 'filename':
                    field.value = files[0].reference.name;
                    break;
                  case 'filesize':
                    field.value = files[0].reference.size;
                    break;
                  case 'mimetype':
                    field.value = files[0].reference.type;
                    break;
                  default:
                    // noop
                }
                field.readonly = true;
              });

              if ($scope.field.itemAdd) {
                let oldItemAdd = $scope.field.itemAdd;
                $scope.field.itemAdd.itemAdd = function (ev) {
                  fileFunnelService.showUpload(ev, $scope.field.path, {}).then(files => {
                    files.forEach(file => {
                      if (file.status !== fileFunnelService.status.COMPLETED) {
                        return;
                      }
                      let newItem = oldItemAdd.call($scope.field);
                      newItem.fields.forEach(field => {
                        switch (field.id) {
                          case 'uri':
                            field.value = fileFunnelService.getOptions(field.path).server + file.location;
                            break;
                          case 'filename':
                            field.value = file.reference.name;
                            break;
                          case 'filesize':
                            field.value = file.reference.size;
                            break;
                          case 'mimetype':
                            field.value = file.reference.type;
                            break;
                          default:
                            // noop
                        }
                        field.readonly = true;
                      });
                    });
                  });
                };
              }
            }
          });
      };
    },
    link(scope, element, attrs) {
      element.bind('click', function(ev) {
        scope.showUpload(ev, element);
      });
    }
  };
});
<commit_msg>Added $scope.apply to display errors<commit_after>'use strict';

let FileFunnel = require('filefunnel.js');
let angular = require('angular');

let ff = angular.module('filefunnel', ['ngMaterial']);

// @ngInject
ff.controller('FFUploadController', function($scope, $mdDialog, options) {
  let ff = new FileFunnel(null, options);
  $scope.ff = ff;

  ff._elements.fileInput.on('change', () => {
    $scope.$apply();
  });

  ff.on('success', file => {
    if (ff.status === FileFunnel.status.COMPLETED) {
      $mdDialog.hide(ff.files);
    }
  }).on('error', file => {
    ff.progressType = 'determinate';
	$scope.$apply();
  }).on('progress', file => {
    ff.progressType = 'determinate';
  }).on('start', file => {
    ff.progressType = 'indeterminate';
  });

  $scope.FileFunnelStatus = FileFunnel.status;
  ff.progressType = 'determinate';
  console.log(ff);
});

ff.service('fileFunnelService', function($mdDialog) {
  const DEFAULTS = {
    server: "http://apptest.data.npolar.no/_file",
    accept: "*/*",
    chunked: true
  };

  let opts = {};
  let defineOptions = function (key, options) {
    opts[key] = Object.assign({}, DEFAULTS, options, {multiple:false});
  };

  let getOptions = function (key) {
    return opts[key] || DEFAULTS;
  };

  let showUpload = function(ev, path, options) {
    return $mdDialog.show({
      clickOutsideToClose: true,
      controller: 'FFUploadController',
      locals: {
        options: Object.assign({}, getOptions(path), options)
      },
      targetEvent: ev,
      template: require('./filefunnel.html')
    });
  };

  return {
    defineOptions,
    getOptions,
    showUpload,
    status: FileFunnel.status
  };
});

ff.directive('filefunnel', function(fileFunnelService) {
  return {
    restrict: 'A',
    //@ngInject
    controller($scope, $mdDialog) {
      $scope.showUpload = function(ev, target) {
        fileFunnelService.showUpload(ev, $scope.field.path)
          .then(files => {
            let options = fileFunnelService.getOptions($scope.field.path);
            if (target[0] instanceof HTMLInputElement) {
              target[0].value = options.server + files[0].location;
            } else if ($scope.field) {
              $scope.field.fields.forEach(field => {
                switch (field.id) {
                  case 'uri':
                    field.value = options.server + files[0].location;
                    break;
                  case 'filename':
                    field.value = files[0].reference.name;
                    break;
                  case 'filesize':
                    field.value = files[0].reference.size;
                    break;
                  case 'mimetype':
                    field.value = files[0].reference.type;
                    break;
                  default:
                    // noop
                }
                field.readonly = true;
              });

              if ($scope.field.itemAdd) {
                let oldItemAdd = $scope.field.itemAdd;
                $scope.field.itemAdd.itemAdd = function (ev) {
                  fileFunnelService.showUpload(ev, $scope.field.path, {}).then(files => {
                    files.forEach(file => {
                      if (file.status !== fileFunnelService.status.COMPLETED) {
                        return;
                      }
                      let newItem = oldItemAdd.call($scope.field);
                      newItem.fields.forEach(field => {
                        switch (field.id) {
                          case 'uri':
                            field.value = fileFunnelService.getOptions(field.path).server + file.location;
                            break;
                          case 'filename':
                            field.value = file.reference.name;
                            break;
                          case 'filesize':
                            field.value = file.reference.size;
                            break;
                          case 'mimetype':
                            field.value = file.reference.type;
                            break;
                          default:
                            // noop
                        }
                        field.readonly = true;
                      });
                    });
                  });
                };
              }
            }
          });
      };
    },
    link(scope, element, attrs) {
      element.bind('click', function(ev) {
        scope.showUpload(ev, element);
      });
    }
  };
});
<|endoftext|>