<commit_before>import Ember from "ember";
import { assert } from "qunit";

var assertElement = function(selector, type, count) {
    var element = find(selector);
    assert.equal(element.length, count || 1, "element with selector '" + selector + "' not found");
    assert.equal(element.attr("type"), type);
    return element;
};

var isVisible = function(selector) {
    var element = find(selector);
    assert.equal(element.hasClass("hidden"), false);
};

var isHidden = function(selector) {
    var element = find(selector);
    assert.equal(element.hasClass("hidden"), true);
};

var isTextInput = function(selector) {
    assertElement(selector, "text");
};

var isEmailInput = function(selector) {
    assertElement(selector, "email");
};

var isPasswordInput = function(selector) {
    assertElement(selector, "password");
};

var isTelInput = function(selector) {
    assertElement(selector, "tel");
};

var isCheckbox = function(selector) {
    assertElement(selector, "checkbox");
};

var isHiddenInput = function(name, value) {
    var element = assertElement("input[name=%@]".fmt(name), "hidden");
    assert.equal(element.val(), value, "value of '" + value + "' is not equal to the element's value of '" + element.val() + "'");
};

var isRadioButton = function(selector, count) {
    assertElement(selector, "radio", count);
};

var isTextButton = function(selector, text, type) {
    if (!text) {
        Ember.assert("Text must be passed to the isTextButton assertion function");
    }

    var _type = type || "button";
    var element = assertElement(selector, _type);
    assert.equal(element.text(), text);
};

var isRadioButtonWithText = function(selector, text) {
    var control = find(selector);
    var controlId = control.attr("id");
    var label = find("label").filter("[for=" + controlId + "]");

    assert.equal(control.length, 1, "Unable to find radio button for selector " + selector);
    assert.equal(control.attr("type"), "radio", "Element for selector " + selector + " is not a radio button; type is " + control.attr("type"));
    assert.ok(control.attr("id"), "Radio button for selector " + selector + " has no id attribute, so it cannot be referred to by a label 'for' attribute");

    assert.equal(label.length, 1, "Unable to find label with 'for' attribute of the selector given for the radio button, " + controlId);
    assert.equal(label.text(), text);
};

var isLink = function(selector, text, href) {
    if (!text) {
        Ember.assert("Text must be passed to the isLink assertion function");
    }

    var element = find(selector);
    assert.equal(element.length, 1);
    assert.equal(element.text(), text);

    if (href) {
        assert.equal(element.attr("href"), href);
    }
};

var isFocused = function(selector) {
    var focused = $(document.activeElement);
    var expected = $(selector);

    if (expected.length > 1) {
        Ember.assert("Too many elements for selector " + selector + " found that were expected to have focus (" +
            expected.length + "); use a more specific selector");
        return;
    }

    if (focused.length === 0) {
        Ember.assert("Expected " + selector + " to have focus, but no element currently has focus");
    } else {
        if (focused.is(expected)) {
            assert.ok(true);
        } else {
            var tagName = focused.prop("tagName").toLowerCase();
            var classes = focused.attr("class");

            if (classes) {
                tagName = tagName + "." + classes.replace(/\s+/g, ".");
            }

            assert.equal(focused.filter(selector).length, 1, "Expected " + selector + " to have focus, but " + tagName + " has focus");
        }
    }
};

var selectRadioButton = function(selector, value) {
    andThen(function() {
        if (value === undefined) {
            find(selector).each(function(index, element) {
                if (element.checked) {
                    element.checked = false;
                    $(element).trigger("change");
                }
            });
        } else {
            find(selector).filter("[value=%@]".fmt(value)).prop("checked", true).trigger("change");
        }
    });
};

var setCheckboxChecked = function(selector, checked) {
    andThen(function() {
        var element = find(selector)[0];

        if (element.checked !== checked) {
            click(selector);
        }
    });
};

export {isTextInput, isEmailInput, isPasswordInput, isTelInput, isHiddenInput, isCheckbox, isRadioButton, isRadioButtonWithText, isTextButton, isLink,
    isFocused, selectRadioButton, setCheckboxChecked, isHidden, isVisible};
<commit_msg>[BUG]: fixing "assert" import in "input.js"<commit_after>import Ember from "ember";
import QUnit from "qunit";

var assertElement = function(selector, type, count) {
    var element = find(selector);
    QUnit.assert.equal(element.length, count || 1, "element with selector '" + selector + "' not found");
    QUnit.assert.equal(element.attr("type"), type);
    return element;
};

var isVisible = function(selector) {
    var element = find(selector);
    QUnit.assert.equal(element.hasClass("hidden"), false);
};

var isHidden = function(selector) {
    var element = find(selector);
    QUnit.assert.equal(element.hasClass("hidden"), true);
};

var isTextInput = function(selector) {
    assertElement(selector, "text");
};

var isEmailInput = function(selector) {
    assertElement(selector, "email");
};

var isPasswordInput = function(selector) {
    assertElement(selector, "password");
};

var isTelInput = function(selector) {
    assertElement(selector, "tel");
};

var isCheckbox = function(selector) {
    assertElement(selector, "checkbox");
};

var isHiddenInput = function(name, value) {
    var element = assertElement("input[name=%@]".fmt(name), "hidden");
    QUnit.assert.equal(element.val(), value, "value of '" + value + "' is not equal to the element's value of '" + element.val() + "'");
};

var isRadioButton = function(selector, count) {
    assertElement(selector, "radio", count);
};

var isTextButton = function(selector, text, type) {
    if (!text) {
        Ember.assert("Text must be passed to the isTextButton assertion function");
    }

    var _type = type || "button";
    var element = assertElement(selector, _type);
    QUnit.assert.equal(element.text(), text);
};

var isRadioButtonWithText = function(selector, text) {
    var control = find(selector);
    var controlId = control.attr("id");
    var label = find("label").filter("[for=" + controlId + "]");

    QUnit.assert.equal(control.length, 1, "Unable to find radio button for selector " + selector);
    QUnit.assert.equal(control.attr("type"), "radio", "Element for selector " + selector + " is not a radio button; type is " + control.attr("type"));
    QUnit.assert.ok(control.attr("id"), "Radio button for selector " + selector + " has no id attribute, so it cannot be referred to by a label 'for' attribute");

    QUnit.assert.equal(label.length, 1, "Unable to find label with 'for' attribute of the selector given for the radio button, " + controlId);
    QUnit.assert.equal(label.text(), text);
};

var isLink = function(selector, text, href) {
    if (!text) {
        Ember.assert("Text must be passed to the isLink assertion function");
    }

    var element = find(selector);
    QUnit.assert.equal(element.length, 1);
    QUnit.assert.equal(element.text(), text);

    if (href) {
        QUnit.assert.equal(element.attr("href"), href);
    }
};

var isFocused = function(selector) {
    var focused = $(document.activeElement);
    var expected = $(selector);

    if (expected.length > 1) {
        Ember.assert("Too many elements for selector " + selector + " found that were expected to have focus (" +
            expected.length + "); use a more specific selector");
        return;
    }

    if (focused.length === 0) {
        Ember.assert("Expected " + selector + " to have focus, but no element currently has focus");
    } else {
        if (focused.is(expected)) {
            QUnit.assert.ok(true);
        } else {
            var tagName = focused.prop("tagName").toLowerCase();
            var classes = focused.attr("class");

            if (classes) {
                tagName = tagName + "." + classes.replace(/\s+/g, ".");
            }

            QUnit.assert.equal(focused.filter(selector).length, 1, "Expected " + selector + " to have focus, but " + tagName + " has focus");
        }
    }
};

var selectRadioButton = function(selector, value) {
    andThen(function() {
        if (value === undefined) {
            find(selector).each(function(index, element) {
                if (element.checked) {
                    element.checked = false;
                    $(element).trigger("change");
                }
            });
        } else {
            find(selector).filter("[value=%@]".fmt(value)).prop("checked", true).trigger("change");
        }
    });
};

var setCheckboxChecked = function(selector, checked) {
    andThen(function() {
        var element = find(selector)[0];

        if (element.checked !== checked) {
            click(selector);
        }
    });
};

export {isTextInput, isEmailInput, isPasswordInput, isTelInput, isHiddenInput, isCheckbox, isRadioButton, isRadioButtonWithText, isTextButton, isLink,
    isFocused, selectRadioButton, setCheckboxChecked, isHidden, isVisible};
<|endoftext|>


<commit_before>(function(){
    'use strict';

    window.App = window.App || {};
    App.utils = {};

    App.utils.makeLink = function makeLink(url, text) {
        var link = $('<a />', {
            href: url,
            text: text
        });
        return $('<div />').append(link).html();
    };

    App.utils.makeXYGraph = function makeXYGraph(container, options) {
        $.getJSON(App.BASE + options.endpoint)
            .done(function (json) {
                var data = json.data;
                $(container).highcharts({
                    chart: {
                        type: options.type
                    },
                    title: {
                        text: options.title
                    },
                    subtitle: {
                        text: options.subtitle
                    },
                    xAxis: {
                        categories: data.map(function (e) {
                            if (typeof options.keyName === 'function') {
                                return options.keyName(e);
                            }
                            return e[options.keyName];
                        })
                    },
                    yAxis: {
                        min: 0,
                    title: {
                        text: options.yTitle
                    }
                    },
                    legend: {
                        enabled: false
                    },
                    series: [{
                        name: options.label,
                        data: data.map(function (e) {
                            if (typeof options.valueName === 'function') {
                                return options.valueName(e);
                            }
                            return e[options.valueName];
                        })
                    }]
                });
            }).fail(displayFailMessage);
    };

    function makeStackedSeries(data, valueKey) {
        // find all the series
        var seriesNames = [];
        var i, j;
        for (i = 0; i < data.length; ++i) {
            var values = data[i][valueKey];
            var keys = Object.keys(values);
            for (j = 0; j < keys.length; ++j) {
                var category = keys[j];
                if (seriesNames.indexOf(category) === -1) {
                    seriesNames.push(category);
                }
            }
        }

        // get the actual values
        var series = [];
        for (i = 0; i < seriesNames.length; ++i) {
            var s = {
                name: seriesNames[i],
                data: []
            };
            for (j = 0; j < data.length; ++j) {
                var d = data[j][valueKey];
                if (d[s.name]) {
                    s.data.push(d[s.name]);
                } else {
                    s.data.push(0);
                }
            }
            series.push(s);
        }
        return series;
    }

    function makeStackedGraph(container, options) {
        var graph = {
            chart: {
                type: options.type
            },
            title: {
                text: options.title
            },
            subtitle: {
                text: options.subtitle
            },
            xAxis: {
                categories: options.categories,
                title: {
                    enabled: true
                }
            },
            yAxis: {
                title: {
                    text: options.yTitle
                },
                min: 0
            },
            legend: {
                labelFormatter: options.legendFormatter
            },
            tooltip: {
                shared: true,
                valueSuffix: ' ' + options.suffix,
                formatter: options.tooltipFormatter
            },
            plotOptions: {},
            series: options.series
        };
        graph.plotOptions[options.type] = {
            stacking: 'normal',
            fillOpacity: 1,
            lineColor: '#666666',
            lineWidth: 1,
            marker: {
                enabled: false
            }
        };
        $(container).highcharts(graph);
    }

    window.drawActivityGraph = function drawActivityGraph() {
        var mode = $('#total-events select')[0].value;
        $.getJSON(App.BASE + '/total_events', {mode: mode})
            .done(function(data) {
                var series = makeStackedSeries(data.data, 'value');
                var categories = data.data.map(function (e) {
                    if (mode == 'weekly') {
                        return e.weekStart + ' - ' + e.weekEnd;
                    }
                    return e.month;
                });

                var options = {
                    type: 'column',
                    title: "Activity",
                    subtitle: "Total " + mode + " events",
                    yTitle: 'Events',
                    suffix: 'events',
                    series: series,
                    categories: categories,
                    legendFormatter: function () {
                        var label = this.name;
                        var idx = label.indexOf("Event");
                        if (idx == -1) {
                            idx = label.indexOf("event");
                        }
                        return label.substr(0, idx);
                    }
                };
                makeStackedGraph('#total-events .graph-container', options);
            })
        .fail(displayFailMessage);
    }

    function drawActivePeopleGraph() {
        $.getJSON(App.BASE + '/most_active_people')
            .done(function(data) {
                var series = makeStackedSeries(data.data, 'events');
                var categories = data.data.map(function (e) {
                    return e.login;
                });

                var options = {
                    type: 'bar',
                    title: "Most active people",
                    subtitle: "By number of events",
                    yTitle: 'Events',
                    suffix: 'events',
                    series: series,
                    categories: categories,
                    legendFormatter: function () {
                        var label = this.name;
                        var idx = label.indexOf("Event");
                        if (idx == -1) {
                            idx = label.indexOf("event");
                        }
                        return label.substr(0, idx);
                    }
                };
                makeStackedGraph('#most-active-people', options);
            }).fail(displayFailMessage);
    }

    function drawPopularityEvolutionGraph() {
        $.getJSON(App.BASE + '/popularity_evolution')
            .done(function(data) {
                var series = makeStackedSeries(data.data, 'value');
                var categories = data.data.map(function (e) {
                    return e.month;
                });

                var options = {
                    type: 'column',
            title: "Monthly Popularity Increase",
            subtitle: "Number of new stars and forks",
            yTitle: 'Events',
            suffix: '',
            series: series,
            categories: categories,
            legendFormatter: function () {
                return this.name;
            }
                };
                makeStackedGraph('#popularity-evolution', options);
            })
        .fail(displayFailMessage);
    }

    window.drawGraphs = function drawGraphs() {
        $('li[data-tab=tab-graphs]').addClass('selected');
        drawActivePeopleGraph();
        drawActivityGraph();
        drawPopularityEvolutionGraph();

        App.utils.makeXYGraph('#most-active-issues', {
            endpoint: '/most_active_issues',
            type: 'bar',
            title: "Most active open issues",
            keyName: function (e) {
                return App.utils.makeLink('http://github.com/' + App.REPO + '/issues/' + e.term,
                    "#" + e.term);
            },
            valueName: 'count',
            yTitle: 'Events',
            label: 'events'
        });
    };

})();
<commit_msg>Scope the functions into the window.<commit_after>(function(){
    'use strict';

    window.App = window.App || {};
    App.utils = {};

    App.utils.makeLink = function makeLink(url, text) {
        var link = $('<a />', {
            href: url,
            text: text
        });
        return $('<div />').append(link).html();
    };

    App.utils.makeXYGraph = function makeXYGraph(container, options) {
        $.getJSON(App.BASE + options.endpoint)
            .done(function (json) {
                var data = json.data;
                $(container).highcharts({
                    chart: {
                        type: options.type
                    },
                    title: {
                        text: options.title
                    },
                    subtitle: {
                        text: options.subtitle
                    },
                    xAxis: {
                        categories: data.map(function (e) {
                            if (typeof options.keyName === 'function') {
                                return options.keyName(e);
                            }
                            return e[options.keyName];
                        })
                    },
                    yAxis: {
                        min: 0,
                    title: {
                        text: options.yTitle
                    }
                    },
                    legend: {
                        enabled: false
                    },
                    series: [{
                        name: options.label,
                        data: data.map(function (e) {
                            if (typeof options.valueName === 'function') {
                                return options.valueName(e);
                            }
                            return e[options.valueName];
                        })
                    }]
                });
            }).fail(displayFailMessage);
    };

    function makeStackedSeries(data, valueKey) {
        // find all the series
        var seriesNames = [];
        var i, j;
        for (i = 0; i < data.length; ++i) {
            var values = data[i][valueKey];
            var keys = Object.keys(values);
            for (j = 0; j < keys.length; ++j) {
                var category = keys[j];
                if (seriesNames.indexOf(category) === -1) {
                    seriesNames.push(category);
                }
            }
        }

        // get the actual values
        var series = [];
        for (i = 0; i < seriesNames.length; ++i) {
            var s = {
                name: seriesNames[i],
                data: []
            };
            for (j = 0; j < data.length; ++j) {
                var d = data[j][valueKey];
                if (d[s.name]) {
                    s.data.push(d[s.name]);
                } else {
                    s.data.push(0);
                }
            }
            series.push(s);
        }
        return series;
    }

    function makeStackedGraph(container, options) {
        var graph = {
            chart: {
                type: options.type
            },
            title: {
                text: options.title
            },
            subtitle: {
                text: options.subtitle
            },
            xAxis: {
                categories: options.categories,
                title: {
                    enabled: true
                }
            },
            yAxis: {
                title: {
                    text: options.yTitle
                },
                min: 0
            },
            legend: {
                labelFormatter: options.legendFormatter
            },
            tooltip: {
                shared: true,
                valueSuffix: ' ' + options.suffix,
                formatter: options.tooltipFormatter
            },
            plotOptions: {},
            series: options.series
        };
        graph.plotOptions[options.type] = {
            stacking: 'normal',
            fillOpacity: 1,
            lineColor: '#666666',
            lineWidth: 1,
            marker: {
                enabled: false
            }
        };
        $(container).highcharts(graph);
    }

    window.drawActivityGraph = function drawActivityGraph() {
        var mode = $('#total-events select')[0].value;
        $.getJSON(App.BASE + '/total_events', {mode: mode})
            .done(function(data) {
                var series = makeStackedSeries(data.data, 'value');
                var categories = data.data.map(function (e) {
                    if (mode == 'weekly') {
                        return e.weekStart + ' - ' + e.weekEnd;
                    }
                    return e.month;
                });

                var options = {
                    type: 'column',
                    title: "Activity",
                    subtitle: "Total " + mode + " events",
                    yTitle: 'Events',
                    suffix: 'events',
                    series: series,
                    categories: categories,
                    legendFormatter: function () {
                        var label = this.name;
                        var idx = label.indexOf("Event");
                        if (idx == -1) {
                            idx = label.indexOf("event");
                        }
                        return label.substr(0, idx);
                    }
                };
                makeStackedGraph('#total-events .graph-container', options);
            })
        .fail(displayFailMessage);
    }

    window.drawActivePeopleGraph = function drawActivePeopleGraph() {
        $.getJSON(App.BASE + '/most_active_people')
            .done(function(data) {
                var series = makeStackedSeries(data.data, 'events');
                var categories = data.data.map(function (e) {
                    return e.login;
                });

                var options = {
                    type: 'bar',
                    title: "Most active people",
                    subtitle: "By number of events",
                    yTitle: 'Events',
                    suffix: 'events',
                    series: series,
                    categories: categories,
                    legendFormatter: function () {
                        var label = this.name;
                        var idx = label.indexOf("Event");
                        if (idx == -1) {
                            idx = label.indexOf("event");
                        }
                        return label.substr(0, idx);
                    }
                };
                makeStackedGraph('#most-active-people', options);
            }).fail(displayFailMessage);
    }

    window.drawPopularityEvolutionGraph = function drawPopularityEvolutionGraph() {
        $.getJSON(App.BASE + '/popularity_evolution')
            .done(function(data) {
                var series = makeStackedSeries(data.data, 'value');
                var categories = data.data.map(function (e) {
                    return e.month;
                });

                var options = {
                    type: 'column',
            title: "Monthly Popularity Increase",
            subtitle: "Number of new stars and forks",
            yTitle: 'Events',
            suffix: '',
            series: series,
            categories: categories,
            legendFormatter: function () {
                return this.name;
            }
                };
                makeStackedGraph('#popularity-evolution', options);
            })
        .fail(displayFailMessage);
    }

    window.drawGraphs = function drawGraphs() {
        $('li[data-tab=tab-graphs]').addClass('selected');
        drawActivePeopleGraph();
        drawActivityGraph();
        drawPopularityEvolutionGraph();

        App.utils.makeXYGraph('#most-active-issues', {
            endpoint: '/most_active_issues',
            type: 'bar',
            title: "Most active open issues",
            keyName: function (e) {
                return App.utils.makeLink('http://github.com/' + App.REPO + '/issues/' + e.term,
                    "#" + e.term);
            },
            valueName: 'count',
            yTitle: 'Events',
            label: 'events'
        });
    };

})();
<|endoftext|>


<commit_before>var app = angular.module('shoes', ['ngRoute'])

    .config(function ($routeProvider, $locationProvider, $logProvider) {
        //configure the routing rules here
        $routeProvider.when('/shoes', {
            controller: 'shoesController',
            templateUrl: 'view/shoes/shoes.html'
        });

        $logProvider.debugEnabled(true);
    })

    .directive('onFinishRender', function ($timeout) {
        return {
            restrict: 'A',
            link: function (scope, element, attr) {
                if (scope.$last === true) {
                    $timeout(function () {
                        scope.$emit('ngRepeatFinished');
                    });
                }
            }
        }
    })

    .controller('shoesController', ['$scope','$routeParams', '$location', '$http', function($scope, $routeParams, $location, $http) {
        var shoesId = $location.search().shoesId;
        var brand = $location.search().brand;

        if(brand && shoesId) {
            $http.get('/data/' + brand + '.json').then(function(response) {
                shoes = response.data.items;
                $scope.type = response.data.type;

                for(i = 0; i < shoes.length; i++) {
                    if(shoes[i] && shoes[i].id === shoesId && shoes[i].carouselImages)
                    {
                        $scope.carouselImages = shoes[i].carouselImages;
                        break;
                    }
                }
            });
        }

        $scope.$on('ngRepeatFinished', function(ngRepeatFinishedEvent) {
            if($('.ps-list').length == 0) {
                $('.pgwSlideshow').pgwSlideshow({maxHeight:550});
            }
        });
    }]);

<commit_msg>increase interval<commit_after>var app = angular.module('shoes', ['ngRoute'])

    .config(function ($routeProvider, $locationProvider, $logProvider) {
        //configure the routing rules here
        $routeProvider.when('/shoes', {
            controller: 'shoesController',
            templateUrl: 'view/shoes/shoes.html'
        });

        $logProvider.debugEnabled(true);
    })

    .directive('onFinishRender', function ($timeout) {
        return {
            restrict: 'A',
            link: function (scope, element, attr) {
                if (scope.$last === true) {
                    $timeout(function () {
                        scope.$emit('ngRepeatFinished');
                    });
                }
            }
        }
    })

    .controller('shoesController', ['$scope','$routeParams', '$location', '$http', function($scope, $routeParams, $location, $http) {
        var shoesId = $location.search().shoesId;
        var brand = $location.search().brand;

        if(brand && shoesId) {
            $http.get('/data/' + brand + '.json').then(function(response) {
                shoes = response.data.items;
                $scope.type = response.data.type;

                for(i = 0; i < shoes.length; i++) {
                    if(shoes[i] && shoes[i].id === shoesId && shoes[i].carouselImages)
                    {
                        $scope.carouselImages = shoes[i].carouselImages;
                        break;
                    }
                }
            });
        }

        $scope.$on('ngRepeatFinished', function(ngRepeatFinishedEvent) {
            if($('.ps-list').length == 0) {
                $('.pgwSlideshow').pgwSlideshow({maxHeight:550, intervalDuration: 7000});
            }
        });
    }]);

<|endoftext|>


<commit_before>// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

import {seekStringToSeconds} from 'base/utils'

import Playback from 'base/playback'
import template from 'base/template'
import Styler from 'base/styler'
import Browser from 'components/browser'
import Events from 'base/events'
import tagStyle from './public/style.scss'
import sourceHTML from './public/index.html'
import find from 'lodash.find'

const MIMETYPES = {
  'mp4': ["avc1.42E01E", "avc1.58A01E", "avc1.4D401E", "avc1.64001E", "mp4v.20.8", "mp4v.20.240", "mp4a.40.2"].map(
    (codec) => { return 'video/mp4; codecs="' + codec + ', mp4a.40.2"'}),
  'ogg': ['video/ogg; codecs="theora, vorbis"', 'video/ogg; codecs="dirac"', 'video/ogg; codecs="theora, speex"'],
  '3gpp': ['video/3gpp; codecs="mp4v.20.8, samr"'],
  'webm': ['video/webm; codecs="vp8, vorbis"'],
  'mkv': ['video/x-matroska; codecs="theora, vorbis"'],
  'm3u8': ['application/x-mpegurl']
}
MIMETYPES['ogv'] = MIMETYPES['ogg']
MIMETYPES['3gp'] = MIMETYPES['3gpp']

export default class HTML5Video extends Playback {
  get name() { return 'html5_video' }
  get tagName() { return 'video' }
  get template() { return template(sourceHTML) }

  get attributes() {
    return {
      'data-html5-video': ''
    }
  }

  get events() {
    return {
      'timeupdate': 'timeUpdated',
      'progress': 'progress',
      'ended': 'ended',
      'stalled': 'stalled',
      'waiting': 'waiting',
      'canplaythrough': 'bufferFull',
      'loadedmetadata': 'loadedMetadata',
      'canplay': 'ready',
      'durationchange': 'durationChange',
      'error': 'error',
      'playing': 'playing',
      'pause': 'paused'
    }
  }

  constructor(options) {
    super(options)
    this.options = options
    this.setupSrc(options.src)
    this.el.loop = options.loop
    this.firstBuffer = true
    this.settings = {default: ['seekbar']}
    if (Browser.isSafari) {
      this.setupSafari()
    } else {
      this.el.preload = options.preload ? options.preload: 'metadata'
      this.settings.seekEnabled = true
    }
    this.settings.left = ["playpause", "position", "duration"]
    this.settings.right = ["fullscreen", "volume", "hd-indicator"]
  }

  /**
   * Sets the source url on the <video> element, and also the 'src' property.
   * @method setupSrc
   * @param {String} srcUrl The source URL.
   */
  setupSrc(srcUrl) {
    this.src = srcUrl
    this.el.src = srcUrl
  }

  setupSafari() {
    this.el.preload = 'auto'
  }

  loadedMetadata(e) {
    this.durationChange()
    var autoSeekFromUrl = typeof(this.options.autoSeekFromUrl) === "undefined" || this.options.autoSeekFromUrl
    if (this.getPlaybackType() !== Playback.LIVE && autoSeekFromUrl) {
      this.checkInitialSeek()
    }
    this.trigger(Events.PLAYBACK_LOADEDMETADATA, {duration: e.target.duration, data: e})
  }

  durationChange() {
    // we can't figure out if hls resource is VoD or not until it is being loaded or duration has changed.
    // that's why we check it again and update media control accordingly.
    if (this.getPlaybackType() === Playback.VOD) {
      this.settings.left = ["playpause", "position", "duration"]
    } else {
      this.settings.left = ["playstop"]
    }
    this.settings.seekEnabled = this.isSeekEnabled()
    this.trigger(Events.PLAYBACK_SETTINGSUPDATE)
  }

  isSeekEnabled() {
    return isFinite(this.getDuration())
  }

  getPlaybackType() {
    return [0, undefined, Infinity].indexOf(this.el.duration) >= 0 ? Playback.LIVE : Playback.VOD
  }

  isHighDefinitionInUse() {
    return false
  }

  play() {
    this.el.play()
  }

  pause() {
    this.el.pause()
  }

  stop() {
    this.pause()
    if (this.el.readyState !== 0) {
      this.el.currentTime = 0
      this.trigger(Events.PLAYBACK_STOP)
    }
  }

  volume(value) {
    this.el.volume = value / 100
  }

  mute() {
    this.el.volume = 0
  }

  unmute() {
    this.el.volume = 1
  }

  isMuted() {
    return !!this.el.volume
  }

  isPlaying() {
    return !this.el.paused && !this.el.ended
  }

  playing() {
    this.trigger(Events.PLAYBACK_PLAY);
  }

  paused() {
    this.trigger(Events.PLAYBACK_PAUSE);
  }

  ended() {
    this.trigger(Events.PLAYBACK_BUFFERFULL, this.name)
    this.trigger(Events.PLAYBACK_ENDED, this.name)
    this.trigger(Events.PLAYBACK_TIMEUPDATE, { current: 0, total: this.el.duration }, this.name)
  }

  stalled() {
    if (this.getPlaybackType() === Playback.VOD && this.el.readyState < this.el.HAVE_FUTURE_DATA) {
      this.trigger(Events.PLAYBACK_BUFFERING, this.name)
    }
  }

  waiting() {
    if(this.el.readyState < this.el.HAVE_FUTURE_DATA) {
      this.trigger(Events.PLAYBACK_BUFFERING, this.name)
    }
  }

  bufferFull() {
    if (this.options.poster && this.firstBuffer) {
      this.firstBuffer = false
      if (!this.isPlaying()) {
        this.el.poster = this.options.poster
      }
    } else {
      this.el.poster = ''
    }
    this.trigger(Events.PLAYBACK_BUFFERFULL, this.name)
  }

  error(event) {
    this.trigger(Events.PLAYBACK_ERROR, this.el.error, this.name)
  }

  destroy() {
    this.stop()
    this.el.src = ''
    this.$el.remove()
  }

  seek(time) {
    this.el.currentTime = time
  }

  seekPercentage(percentage) {
    var time = this.el.duration * (percentage / 100)
    this.seek(time)
  }

  checkInitialSeek() {
    var seekTime = seekStringToSeconds(window.location.href)
    this.seek(seekTime)
  }

  getCurrentTime() {
    return this.el.currentTime
  }

  getDuration() {
    return this.el.duration
  }

  timeUpdated() {
    if (this.getPlaybackType() === Playback.LIVE) {
      this.trigger(Events.PLAYBACK_TIMEUPDATE, {current: 1, total: 1}, this.name)
    } else {
      this.trigger(Events.PLAYBACK_TIMEUPDATE, {current: this.el.currentTime, total: this.el.duration}, this.name)
    }
  }

  progress() {
    if (!this.el.buffered.length) return
    var bufferedPos = 0
    for (var i = 0;  i < this.el.buffered.length; i++) {
      if (this.el.currentTime >= this.el.buffered.start(i) && this.el.currentTime <= this.el.buffered.end(i)) {
        bufferedPos = i
        break
      }
    }
    this.checkBufferState(this.el.buffered.end(bufferedPos))
    this.trigger(Events.PLAYBACK_PROGRESS, {
      start: this.el.buffered.start(bufferedPos),
      current: this.el.buffered.end(bufferedPos),
      total: this.el.duration
    })
  }

  checkBufferState(bufferedPos) {
    var playbackPos = this.el.currentTime + 0.05; // 50 ms threshold
    if (this.isPlaying() && playbackPos >= bufferedPos) {
      this.trigger(Events.PLAYBACK_BUFFERING, this.name)
      this.buffering = true
    } else if (this.buffering) {
      this.trigger(Events.PLAYBACK_BUFFERFULL, this.name)
      this.buffering = false
    }
  }

  typeFor(src) {
    return (src.indexOf('.m3u8') > 0) ? 'application/vnd.apple.mpegurl' : 'video/mp4'
  }

  ready() {
    this.trigger(Events.PLAYBACK_READY, this.name)
  }

  render() {
    var style = Styler.getStyleFor(tagStyle)

    this.src && this.$el.html(this.template({ src: this.src, type: this.typeFor(this.src) }))

    if (this.options.useVideoTagDefaultControls) {
      this.$el.attr('controls', 'controls')
    }

    if (this.options.disableVideoTagContextMenu) {
      this.$el.on("contextmenu", () => {
        return false
      })
    }

    this.$el.append(style)

    process.nextTick(() => this.options.autoPlay && this.play())
    if (this.el.readyState === this.el.HAVE_ENOUGH_DATA) {
      this.ready()
    }
    return this
  }
}

HTML5Video._canPlay = function(type, mimeTypesByExtension, resourceUrl, mimeType) {
  var extension = (resourceUrl.split('?')[0].match(/.*\.(.*)$/) || [])[1]
  var mimeTypes = mimeType || mimeTypesByExtension[extension] || []
  mimeTypes = (mimeTypes.constructor === Array) ? mimeTypes : [mimeTypes]

  var media = document.createElement(type)
  return !!find(mimeTypes, (mediaType) => !!media.canPlayType(mediaType).replace(/no/, ''))
}

HTML5Video.canPlay = function(resourceUrl, mimeType) {
  return HTML5Video._canPlay('video', MIMETYPES, resourceUrl, mimeType)
}

module.exports = HTML5Video
<commit_msg>emits bufferfull on canplay fixes #696<commit_after>// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

import {seekStringToSeconds} from 'base/utils'

import Playback from 'base/playback'
import template from 'base/template'
import Styler from 'base/styler'
import Browser from 'components/browser'
import Events from 'base/events'
import tagStyle from './public/style.scss'
import sourceHTML from './public/index.html'
import find from 'lodash.find'

const MIMETYPES = {
  'mp4': ["avc1.42E01E", "avc1.58A01E", "avc1.4D401E", "avc1.64001E", "mp4v.20.8", "mp4v.20.240", "mp4a.40.2"].map(
    (codec) => { return 'video/mp4; codecs="' + codec + ', mp4a.40.2"'}),
  'ogg': ['video/ogg; codecs="theora, vorbis"', 'video/ogg; codecs="dirac"', 'video/ogg; codecs="theora, speex"'],
  '3gpp': ['video/3gpp; codecs="mp4v.20.8, samr"'],
  'webm': ['video/webm; codecs="vp8, vorbis"'],
  'mkv': ['video/x-matroska; codecs="theora, vorbis"'],
  'm3u8': ['application/x-mpegurl']
}
MIMETYPES['ogv'] = MIMETYPES['ogg']
MIMETYPES['3gp'] = MIMETYPES['3gpp']

export default class HTML5Video extends Playback {
  get name() { return 'html5_video' }
  get tagName() { return 'video' }
  get template() { return template(sourceHTML) }

  get attributes() {
    return {
      'data-html5-video': ''
    }
  }

  get events() {
    return {
      'timeupdate': 'timeUpdated',
      'progress': 'progress',
      'ended': 'ended',
      'stalled': 'stalled',
      'waiting': 'waiting',
      'canplaythrough': 'bufferFull',
      'loadedmetadata': 'loadedMetadata',
      'canplay': 'ready',
      'durationchange': 'durationChange',
      'error': 'error',
      'playing': 'playing',
      'pause': 'paused'
    }
  }

  constructor(options) {
    super(options)
    this.options = options
    this.setupSrc(options.src)
    this.el.loop = options.loop
    this.firstBuffer = true
    this.settings = {default: ['seekbar']}
    if (Browser.isSafari) {
      this.setupSafari()
    } else {
      this.el.preload = options.preload ? options.preload: 'metadata'
      this.settings.seekEnabled = true
    }
    this.settings.left = ["playpause", "position", "duration"]
    this.settings.right = ["fullscreen", "volume", "hd-indicator"]
  }

  /**
   * Sets the source url on the <video> element, and also the 'src' property.
   * @method setupSrc
   * @param {String} srcUrl The source URL.
   */
  setupSrc(srcUrl) {
    this.src = srcUrl
    this.el.src = srcUrl
  }

  setupSafari() {
    this.el.preload = 'auto'
  }

  loadedMetadata(e) {
    this.durationChange()
    var autoSeekFromUrl = typeof(this.options.autoSeekFromUrl) === "undefined" || this.options.autoSeekFromUrl
    if (this.getPlaybackType() !== Playback.LIVE && autoSeekFromUrl) {
      this.checkInitialSeek()
    }
    this.trigger(Events.PLAYBACK_LOADEDMETADATA, {duration: e.target.duration, data: e})
  }

  durationChange() {
    // we can't figure out if hls resource is VoD or not until it is being loaded or duration has changed.
    // that's why we check it again and update media control accordingly.
    if (this.getPlaybackType() === Playback.VOD) {
      this.settings.left = ["playpause", "position", "duration"]
    } else {
      this.settings.left = ["playstop"]
    }
    this.settings.seekEnabled = this.isSeekEnabled()
    this.trigger(Events.PLAYBACK_SETTINGSUPDATE)
  }

  isSeekEnabled() {
    return isFinite(this.getDuration())
  }

  getPlaybackType() {
    return [0, undefined, Infinity].indexOf(this.el.duration) >= 0 ? Playback.LIVE : Playback.VOD
  }

  isHighDefinitionInUse() {
    return false
  }

  play() {
    this.el.play()
  }

  pause() {
    this.el.pause()
  }

  stop() {
    this.pause()
    if (this.el.readyState !== 0) {
      this.el.currentTime = 0
      this.trigger(Events.PLAYBACK_STOP)
    }
  }

  volume(value) {
    this.el.volume = value / 100
  }

  mute() {
    this.el.volume = 0
  }

  unmute() {
    this.el.volume = 1
  }

  isMuted() {
    return !!this.el.volume
  }

  isPlaying() {
    return !this.el.paused && !this.el.ended
  }

  playing() {
    this.trigger(Events.PLAYBACK_PLAY);
  }

  paused() {
    this.trigger(Events.PLAYBACK_PAUSE);
  }

  ended() {
    this.trigger(Events.PLAYBACK_BUFFERFULL, this.name)
    this.trigger(Events.PLAYBACK_ENDED, this.name)
    this.trigger(Events.PLAYBACK_TIMEUPDATE, { current: 0, total: this.el.duration }, this.name)
  }

  stalled() {
    if (this.getPlaybackType() === Playback.VOD && this.el.readyState < this.el.HAVE_FUTURE_DATA) {
      this.trigger(Events.PLAYBACK_BUFFERING, this.name)
    }
  }

  waiting() {
    if(this.el.readyState < this.el.HAVE_FUTURE_DATA) {
      this.trigger(Events.PLAYBACK_BUFFERING, this.name)
    }
  }

  bufferFull() {
    if (this.options.poster && this.firstBuffer) {
      this.firstBuffer = false
      if (!this.isPlaying()) {
        this.el.poster = this.options.poster
      }
    } else {
      this.el.poster = ''
    }
    this.trigger(Events.PLAYBACK_BUFFERFULL, this.name)
  }

  error(event) {
    this.trigger(Events.PLAYBACK_ERROR, this.el.error, this.name)
  }

  destroy() {
    this.stop()
    this.el.src = ''
    this.$el.remove()
  }

  seek(time) {
    this.el.currentTime = time
  }

  seekPercentage(percentage) {
    var time = this.el.duration * (percentage / 100)
    this.seek(time)
  }

  checkInitialSeek() {
    var seekTime = seekStringToSeconds(window.location.href)
    this.seek(seekTime)
  }

  getCurrentTime() {
    return this.el.currentTime
  }

  getDuration() {
    return this.el.duration
  }

  timeUpdated() {
    if (this.getPlaybackType() === Playback.LIVE) {
      this.trigger(Events.PLAYBACK_TIMEUPDATE, {current: 1, total: 1}, this.name)
    } else {
      this.trigger(Events.PLAYBACK_TIMEUPDATE, {current: this.el.currentTime, total: this.el.duration}, this.name)
    }
  }

  progress() {
    if (!this.el.buffered.length) return
    var bufferedPos = 0
    for (var i = 0;  i < this.el.buffered.length; i++) {
      if (this.el.currentTime >= this.el.buffered.start(i) && this.el.currentTime <= this.el.buffered.end(i)) {
        bufferedPos = i
        break
      }
    }
    this.checkBufferState(this.el.buffered.end(bufferedPos))
    this.trigger(Events.PLAYBACK_PROGRESS, {
      start: this.el.buffered.start(bufferedPos),
      current: this.el.buffered.end(bufferedPos),
      total: this.el.duration
    })
  }

  checkBufferState(bufferedPos) {
    var playbackPos = this.el.currentTime + 0.05; // 50 ms threshold
    if (this.isPlaying() && playbackPos >= bufferedPos) {
      this.trigger(Events.PLAYBACK_BUFFERING, this.name)
      this.buffering = true
    } else if (this.buffering) {
      this.trigger(Events.PLAYBACK_BUFFERFULL, this.name)
      this.buffering = false
    }
  }

  typeFor(src) {
    return (src.indexOf('.m3u8') > 0) ? 'application/vnd.apple.mpegurl' : 'video/mp4'
  }

  ready() {
    this.trigger(Events.PLAYBACK_READY, this.name)
    if (this.firstBuffer) {
      this.trigger(Events.PLAYBACK_BUFFERFULL, this.name)
      this.firstBuffer = this.buffering = false
    }
  }

  render() {
    var style = Styler.getStyleFor(tagStyle)

    this.src && this.$el.html(this.template({ src: this.src, type: this.typeFor(this.src) }))

    if (this.options.useVideoTagDefaultControls) {
      this.$el.attr('controls', 'controls')
    }

    if (this.options.disableVideoTagContextMenu) {
      this.$el.on("contextmenu", () => {
        return false
      })
    }

    this.$el.append(style)

    process.nextTick(() => this.options.autoPlay && this.play())
    if (this.el.readyState === this.el.HAVE_ENOUGH_DATA) {
      this.ready()
    }
    return this
  }
}

HTML5Video._canPlay = function(type, mimeTypesByExtension, resourceUrl, mimeType) {
  var extension = (resourceUrl.split('?')[0].match(/.*\.(.*)$/) || [])[1]
  var mimeTypes = mimeType || mimeTypesByExtension[extension] || []
  mimeTypes = (mimeTypes.constructor === Array) ? mimeTypes : [mimeTypes]

  var media = document.createElement(type)
  return !!find(mimeTypes, (mediaType) => !!media.canPlayType(mediaType).replace(/no/, ''))
}

HTML5Video.canPlay = function(resourceUrl, mimeType) {
  return HTML5Video._canPlay('video', MIMETYPES, resourceUrl, mimeType)
}

module.exports = HTML5Video
<|endoftext|>


<commit_before>define (function (require, exports, module) {
	var ComponentPlugin = require("ide-component-plugin");
	var beautify = require("/bower_components/js-beautify/js/lib/beautify.js");
	var collectionEditor = require("ide-collectioneditor");
	var IgniteUIComponentPlugin = IgniteUIComponentPlugin || ComponentPlugin.extend({
		init: function (options) {
			this._super(options);
		},
		getName: function () {
			return null;
		},
		_getWidgetName: function (type) {
			/*
			var comp;
			if (this.settings && this.settings.packageInfo) {
				comp = this.settings.packageInfo.components[type];
				if (comp && typeof comp.name === "string") {
					return comp.name.replace("ui.", "");
				}
			}*/
			return "ig" + type.charAt(0).toUpperCase() + type.slice(1);
		},
		getHeadMarkup: function () {
			return null;
		},
		getCodeEditorHeadSnippet: function () {
			return null;
		},
		// retrieves the initial markup for the current component
		getMarkup: function (descriptor) {
			return "

";
			//return this.evalTemplate("default.html", descriptor, true);
		},
		getCodeEditorScriptSnippet: function (descriptor) {
			var code = "";
			var opts = descriptor.options;
			var name = this._getWidgetName(descriptor.type);
			if (!name) {
				console.error("Could not get widget name for '" + descriptor.type +"'.");
				return null;
			}
			var lineCount = 5;
			code = "\t\t\t\t$(\"#" + descriptor.id + "\")." + name + "({\n";
				if (opts.height) {
				code += "\t\t\t\t\theight: " + opts.height + ",\n";
				}
				if (opts.width) {
				code += "\t\t\t\t\twidth: " + opts.width;
				}
			if (descriptor.data && window[descriptor.data]) {
				code += ",\n\t\t\t\t\tdataSource: " + descriptor.data;
			}
			var props = this.settings.packageInfo.components[descriptor.type].properties;
			for (var key in opts) {
				if (opts.hasOwnProperty(key) && key !== "dataSource" && key !== "height" && key !== "width") {
					if (props[key].type === "string") {
						code += ",\n\t\t\t\t\t" + key + ": \"" + opts[key] + "\"";
					} else if (props[key].type === "array") {
						var formattedStr = beautify(JSON.stringify(opts[key]));
						//code += ",\n\t\t\t\t\t" + key + ": " + formattedStr;
						code += ",\n";
						formattedStr = key + ": " + formattedStr;
						var formattedStrTabbed = "";
						var tabbedArr = formattedStr.split("\n");
						for (var i = 0; i < tabbedArr.length; i++) {
							formattedStrTabbed += "\t\t\t\t\t" + tabbedArr[i];
							if (i < tabbedArr.length - 1) {
								formattedStrTabbed += "\n";
							}
						}
						code += formattedStrTabbed;
						lineCount += code.split("\n").length;
					} else {
						code += ",\n\t\t\t\t\t" + key + ": " + opts[key];
					}
					lineCount++;
				}
			}
			code += "\n\t\t\t\t});\n";
			return {codeString: code, lineCount: lineCount};
			//return this.evalTemplate("default.code.js", descriptor);
		},
		getCodeEditorMarkupSnippet: function (descriptor) {
			// as a developer, i would like to be able to provide the direct manual formatting, as well as specify a template, and evaluate it
			var extraIndentStr = "", i = 0;
			if (descriptor.extraIndent) {
				for (i = 0; i < descriptor.extraIndent; i++) {
					extraIndentStr += "\t";
				}
			}
			return {codeString: "\t" + extraIndentStr + "

\n", lineCount: 1};
			//return this.evalTemplate("default.code.html", descriptor);
		},
		requiresInitialization: function () {
			return true;
		},
		getPropValue: function (descriptor) {
			var name = this._getWidgetName(descriptor.type);
			var data = typeof window.frames[0].$(descriptor.placeholder).data === "function" && window.frames[0].$(descriptor.placeholder).data(name);
			if (data) {
				return data.options && typeof(data.options[descriptor.propName]) !== "undefined" ? data.options[descriptor.propName] : descriptor.defaultValue;
			}
			return descriptor.defaultValue;
		},
		initComponent: function (descriptor) {
			var name = this._getWidgetName(descriptor.type);
			if (window.frames[0].$(descriptor.placeholder)[name]) {
				window.frames[0].$(descriptor.placeholder)[name](descriptor.options);
			}
		},
		navigate: function (descriptor) {

		},
		update: function (descriptor) {
			//console.log("Updating property or event: " + descriptor.propName);
			var ide = this.settings.ide;
			var name = this._getWidgetName(descriptor.type);
			if (descriptor.args) {
				// handle events
				// try to find an existing marker, for the event, if one doesn't exist, create it
				var component = null;
				for (var i = 0; i < ide.componentIds.length; i++) {
					if (ide.componentIds[i].id === descriptor.id) {
						component = ide.componentIds[i];
						break;
					}
				}
				if (component) {
					if (!component.eventMarkers) {
						component.eventMarkers = {};
					}
					//var codeRange = ide.editor.find("


<commit_before>// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//FIXME review this require approach.
var UIObject = require('../../base/ui_object');
var Styler = require('../../base/styler');
var JST = require('../../base/jst');
var _ = require("underscore");

var HLSVideoPlaybackPlugin = UIObject.extend({
  pluginName: 'hls_playback',
  attributes: {
    'data-hls-playback': ''
  },
  tagName: 'object',
  template: JST.hls_playback,
  initialize: function(options) {
    this.el.src = options.src;
    this.el.id = this.cid;
    this.swfPath = options.swfPath || "assets/HLSPlayer.swf";
    this.container.settings = {
      left: ["playstop"],
      right: ["fullscreen", "volume"]
    };
    this.autoPlay = options.autoPlay;
    this.listenTo(this.container, 'container:play', this.play);
    this.listenTo(this.container, 'container:pause', this.pause);
    this.listenTo(this.container, 'container:stop', this.stop);
    this.listenTo(this.container, 'container:seek', this.seek);
    this.listenTo(this.container, 'container:volume', this.volume);
    this.listenTo(this.container, 'container:fullscreen', this.fullscreen);
    this.listenTo(this.container, 'container:destroyed', this.containerDestroyed);
    this.render();
    this.checkIfFlashIsReady();
  },
  bootstrap: function() {
    clearInterval(this.bootstrapId);
    this.currentState = "IDLE";
    this.timedCheckState();
    this.autoPlay && this.container.play();
  },
  checkIfFlashIsReady: function() {
    this.bootstrapId = setInterval(function() {
      if(this.el.getState) {
        this.bootstrap();
      }
    }.bind(this), 50);
  },
  updateTime: function(interval) {
    return setInterval(function() {
      var time = (100 / (this.el.getDuration() || 1)) * (this.el.getPosition() || 0);
      this.container.timeUpdated(time);
    }.bind(this), interval);
  },
  play: function() {
    if(this.el.getState() === 'IDLE') {
      this.id = this.updateTime(1000);
    }
    if(this.el.getState() === 'PAUSED') {
      this.el.resume();
    } else {
      this.firstPlay();
    }
  },
  timedCheckState: function() {
    this.checkStateId = setInterval(this.checkState.bind(this), 250);
  },
  checkState: function() {
    if (this.el.getState() === "PLAYING_BUFFERING" && this.el.getbufferLength() < 1) {
      this.container.buffering();
      this.currentState = "PLAYING_BUFFERING";
    } else if (this.currentState === "PLAYING_BUFFERING" && this.el.getState() === "PLAYING") {
      this.container.bufferfull();
      this.currentState = "PLAYING";
    } else if (this.el.getState() === "IDLE") {
      this.currentState = "IDLE";
    }
  },
  firstPlay: function() {
    this.el.load(this.el.src);
    this.el.play();
  },
  volume: function(value) {
    this.el.volume(value);
  },
  pause: function() {
    this.el.pause();
  },
  stop: function() {
    this.el.stop();
    clearInterval(this.id);
    this.container.timeUpdated(0);
  },
  seek: function(time) {
    clearInterval(this.id);
    this.el.seek(this.el.getDuration() * (time / 100));
    this.id = this.updateTime(1000);
  },
  timeUpdate: function(time) {
    this.container.timeUpdated(time);
  },
  containerDestroyed: function() {
    console.log('container destroyed');
    clearInterval(this.id);
    clearInterval(this.checkStateId);
  },
  render: function() {
    var style = Styler.getStyleFor(this.pluginName);
    this.$el.html(this.template({swfPath: this.swfPath}));
    this.$el.append(style);
    this.container.$el.append(this.el);
    return this;
  }
});

HLSVideoPlaybackPlugin.canPlay = function(resource) {
  return !!resource.match(/(.*).m3u8/);
}

module.exports = HLSVideoPlaybackPlugin;
<commit_msg>hls playback: inherits from ui plugin instead of ui object<commit_after>// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

var UIPlugin = require('../../base/ui_plugin');
var Styler = require('../../base/styler');
var JST = require('../../base/jst');
var _ = require("underscore");

var HLSVideoPlaybackPlugin = UIPlugin.extend({
  name: 'hls_playback',
  tagName: 'object',
  template: JST.hls_playback,
  attributes: {
    'data-hls-playback': ''
  },

  initialize: function(options) {
    this.super('initialize');
    this.el.src = options.src;
    this.el.id = this.cid;
    this.swfPath = options.swfPath || "assets/HLSPlayer.swf";
    this.container.settings = {
      left: ["playstop"],
      right: ["fullscreen", "volume"]
    };
    this.autoPlay = options.autoPlay;
    this.render();
    this.checkIfFlashIsReady();
  },

  bindEvents: function() {
    this.listenTo(this.container, 'container:play', this.play);
    this.listenTo(this.container, 'container:pause', this.pause);
    this.listenTo(this.container, 'container:stop', this.stop);
    this.listenTo(this.container, 'container:seek', this.seek);
    this.listenTo(this.container, 'container:volume', this.volume);
    this.listenTo(this.container, 'container:fullscreen', this.fullscreen);
    this.listenTo(this.container, 'container:destroyed', this.containerDestroyed);
  },

  bootstrap: function() {
    clearInterval(this.bootstrapId);
    this.currentState = "IDLE";
    this.timedCheckState();
    this.autoPlay && this.container.play();
  },

  checkIfFlashIsReady: function() {
    this.bootstrapId = setInterval(function() {
      if(this.el.getState) {
        this.bootstrap();
      }
    }.bind(this), 50);
  },

  updateTime: function(interval) {
    return setInterval(function() {
      var time = (100 / (this.el.getDuration() || 1)) * (this.el.getPosition() || 0);
      this.container.timeUpdated(time);
    }.bind(this), interval);
  },

  play: function() {
    if(this.el.getState() === 'IDLE') {
      this.id = this.updateTime(1000);
    }
    if(this.el.getState() === 'PAUSED') {
      this.el.resume();
    } else {
      this.firstPlay();
    }
  },

  timedCheckState: function() {
    this.checkStateId = setInterval(this.checkState.bind(this), 250);
  },

  checkState: function() {
    if (this.el.getState() === "PLAYING_BUFFERING" && this.el.getbufferLength() < 1) {
      this.container.buffering();
      this.currentState = "PLAYING_BUFFERING";
    } else if (this.currentState === "PLAYING_BUFFERING" && this.el.getState() === "PLAYING") {
      this.container.bufferfull();
      this.currentState = "PLAYING";
    } else if (this.el.getState() === "IDLE") {
      this.currentState = "IDLE";
    }
  },

  firstPlay: function() {
    this.el.load(this.el.src);
    this.el.play();
  },

  volume: function(value) {
    this.el.volume(value);
  },

  pause: function() {
    this.el.pause();
  },

  stop: function() {
    this.el.stop();
    clearInterval(this.id);
    this.container.timeUpdated(0);
  },

  seek: function(time) {
    clearInterval(this.id);
    this.el.seek(this.el.getDuration() * (time / 100));
    this.id = this.updateTime(1000);
  },

  timeUpdate: function(time) {
    this.container.timeUpdated(time);
  },

  containerDestroyed: function() {
    console.log('container destroyed');
    clearInterval(this.id);
    clearInterval(this.checkStateId);
  },

  render: function() {
    var style = Styler.getStyleFor(this.name);
    this.$el.html(this.template({swfPath: this.swfPath}));
    this.$el.append(style);
    this.container.$el.append(this.el);
    return this;
  }
});

HLSVideoPlaybackPlugin.canPlay = function(resource) {
  return !!resource.match(/(.*).m3u8/);
}

module.exports = HLSVideoPlaybackPlugin;
<|endoftext|>


<commit_before>/**
 * this plugin adds the RxCollection.syncGraphQl()-function to rxdb
 * you can use it to sync collections with remote graphql endpoint
 */

import {
    BehaviorSubject,
    Subject
} from 'rxjs';
import {
    first,
    filter
} from 'rxjs/operators';
import GraphQLClient from 'graphql-client';

import {
    promiseWait
} from '../../util';

import Core from '../../core';
import {
    hash,
    clone
} from '../../util';

import {
    DEFAULT_MODIFIER,
    wasRevisionfromPullReplication,
    createRevisionForPulledDocument,
    getDocsWithRevisionsFromPouch
} from './helper';
import {
    setLastPushSequence,
    getLastPullDocument,
    setLastPullDocument,
    getChangesSinceLastPushSequence
} from './crawling-checkpoint';

import RxDBWatchForChangesPlugin from '../watch-for-changes';
import {
    changeEventfromPouchChange
} from '../../rx-change-event';

/**
 * add the watch-for-changes-plugin
 * so pouchdb will emit events when something gets written to it
 */
Core.plugin(RxDBWatchForChangesPlugin);

export class RxGraphQLReplicationState {
    constructor(
        collection,
        url,
        headers,
        pull,
        push,
        deletedFlag,
        live,
        liveInterval,
        retryTime
    ) {
        this.collection = collection;
        this.client = GraphQLClient({
            url,
            headers
        });
        this.endpointHash = hash(url);

        this.pull = pull;
        this.push = push;

        this.deletedFlag = deletedFlag;
        this.live = live;
        this.liveInterval = liveInterval;
        this.retryTime = retryTime;

        this._runQueueCount = 0;
        this._subs = [];
        this._runningPromise = Promise.resolve();
        this._subjects = {
            recieved: new Subject(), // all documents that are recieved from the endpoint
            send: new Subject(), // all documents that are send to the endpoint
            error: new Subject(), // all errors that are revieced from the endpoint, emits new Error() objects
            canceled: new BehaviorSubject(false), // true when the replication was canceled
            active: new BehaviorSubject(false), // true when something is running, false when not
            initialReplicationComplete: new BehaviorSubject(false) // true the initial replication-cycle is over
        };

        this._prepare();
    }

    /**
     * things that are more complex to not belong into the constructor
     */
    _prepare() {
        // stop sync when collection gets destroyed
        this.collection.onDestroy.then(() => {
            this.cancel();
        });

        // create getters for the observables
        Object.keys(this._subjects).forEach(key => {
            Object.defineProperty(this, key + '$', {
                get: function () {
                    return this._subjects[key].asObservable();
                }
            });
        });
    }

    isStopped() {
        if (!this.live && this._subjects.initialReplicationComplete._value) return true;
        if (this._subjects.canceled._value) return true;
        else return false;
    }

    awaitInitialReplication() {
        return this.initialReplicationComplete$.pipe(
            filter(v => v === true),
            first()
        ).toPromise();
    }

    // ensures this._run() does not run in parallel
    async run() {
        if (this.isStopped()) {
            return;
        }

        if (this._runQueueCount > 2) {
            return this._runningPromise;
        }

        this._runQueueCount++;
        this._runningPromise = this._runningPromise.then(async () => {
            this._subjects.active.next(true);
            const willRetry = await this._run();
            this._subjects.active.next(false);

            if (!willRetry && this._subjects.initialReplicationComplete._value === false)
                this._subjects.initialReplicationComplete.next(true);

            this._runQueueCount--;
        });
        return this._runningPromise;
    }

    async _run() {
        let willRetry = false;

        if (this.push) {
            const ok = await this.runPush();
            if (!ok) {
                willRetry = true;
                setTimeout(() => this.run(), this.retryTime);
            }
        }

        if (this.pull) {
            const ok = await this.runPull();
            if (!ok) {
                willRetry = true;
                setTimeout(() => this.run(), this.retryTime);
            }
        }

        return willRetry;

    }

    /**
     * @return {boolean} true if no errors occured
     */
    async runPull() {
        // console.log('RxGraphQLReplicationState.runPull(): start');
        if (this.isStopped()) return;

        const latestDocument = await getLastPullDocument(this.collection, this.endpointHash);
        const latestDocumentData = latestDocument ? latestDocument : null;
        const pullGraphQL = this.pull.queryBuilder(latestDocumentData);

        let result;
        try {
            result = await this.client.query(pullGraphQL.query, pullGraphQL.variables);
            if (result.errors) {
                throw new Error(result.errors);
            }
        } catch (err) {
            this._subjects.error.next(err);
            setTimeout(() => this.run(), this.retryTime);
            return false;
        }

        // this assumes that there will be always only one property in the response
        // is this correct?
        const data = result.data[Object.keys(result.data)[0]];

        const modified = data.map(doc => this.pull.modifier(doc));

        const docIds = modified.map(doc => doc[this.collection.schema.primaryPath]);
        const docsWithRevisions = await getDocsWithRevisionsFromPouch(
            this.collection,
            docIds
        );
        await Promise.all(modified.map(doc => this.handleDocumentFromRemote(doc, docsWithRevisions)));
        modified.map(doc => this._subjects.recieved.next(doc));

        if (modified.length === 0) {
            if (this.live) {
                // console.log('no more docs, wait for ping');
            } else {
                // console.log('RxGraphQLReplicationState._run(): no more docs and not live; complete = true');
            }
        } else {
            const newLatestDocument = modified[modified.length - 1];
            await setLastPullDocument(
                this.collection,
                this.endpointHash,
                newLatestDocument
            );

            // we have more docs, re-run
            await this.runPull();
        }

        return true;
    }

    async runPush() {
        // console.log('RxGraphQLReplicationState.runPush(): start');

        const changes = await getChangesSinceLastPushSequence(
            this.collection,
            this.endpointHash,
            this.push.batchSize
        );

        const changesWithDocs = changes.results.map(change => {
            let doc = change.doc;

            doc[this.deletedFlag] = !!change.deleted;
            delete doc._rev;
            delete doc._deleted;
            delete doc._attachments;

            doc = this.push.modifier(doc);

            const seq = change.seq;
            return {
                doc,
                seq
            };
        });

        let lastSuccessfullChange = null;
        try {
            /**
             * we cannot run all queries parallel
             * because then we would not know
             * where to start again on errors
             * so we run through the docs in series
             */
            for (let i = 0; i < changesWithDocs.length; i++) {
                const changeWithDoc = changesWithDocs[i];
                const pushObj = this.push.queryBuilder(changeWithDoc.doc);
                const result = await this.client.query(pushObj.query, pushObj.variables);
                if (result.errors) {
                    throw new Error(result.errors);
                } else {
                    this._subjects.send.next(changeWithDoc.doc);
                    lastSuccessfullChange = changeWithDoc;
                }
            }
        } catch (err) {

            if (lastSuccessfullChange) {
                await setLastPushSequence(
                    this.collection,
                    this.endpointHash,
                    lastSuccessfullChange.seq
                );
            }

            this._subjects.error.next(err);
            setTimeout(() => this.run(), this.retryTime);
            return false;
        }

        // all docs where successfull, so we use the seq of the changes-fetch
        await setLastPushSequence(
            this.collection,
            this.endpointHash,
            changes.last_seq
        );

        if (changes.results.length === 0) {
            if (this.live) {
                // console.log('no more docs to push, wait for ping');
            } else {
                // console.log('RxGraphQLReplicationState._runPull(): no more docs to push and not live; complete = true');
            }
        } else {
            // we have more docs, re-run
            await this.runPush();
        }

        return true;
    }

    async handleDocumentFromRemote(doc, docsWithRevisions) {
        const deletedValue = doc[this.deletedFlag];
        const toPouch = this.collection._handleToPouch(doc);
        // console.log('handleDocumentFromRemote(' + toPouch._id + ') start');
        toPouch._deleted = deletedValue;
        delete toPouch[this.deletedFlag];
        const primaryValue = toPouch._id;

        const pouchState = docsWithRevisions[primaryValue];
        let newRevision = createRevisionForPulledDocument(
            this.endpointHash,
            toPouch
        );
        if (pouchState) {
            const newRevisionHeight = pouchState.revisions.start + 1;
            const revisionId = newRevision;
            newRevision = newRevisionHeight + '-' + newRevision;
            toPouch._revisions = {
                start: newRevisionHeight,
                ids: pouchState.revisions.ids
            };
            toPouch._revisions.ids.unshift(revisionId);
        } else {
            newRevision = '1-' + newRevision;
        }
        toPouch._rev = newRevision;

        await this.collection.pouch.bulkDocs(
            [
                toPouch
            ], {
                new_edits: false
            }
        );

        /**
         * because bulkDocs with new_edits: false
         * does not stream changes to the pouchdb,
         * we create the event and emit it,
         * so other instances get informed about it
         */
        const originalDoc = clone(toPouch);
        originalDoc._deleted = deletedValue;
        delete originalDoc[this.deletedFlag];
        originalDoc._rev = newRevision;
        const cE = changeEventfromPouchChange(
            originalDoc,
            this.collection
        );
        this.collection.$emit(cE);
    }

    cancel() {
        if (this.isStopped()) return;

        if (this.changesSub) this.changesSub.cancel();

        this._subjects.canceled.next(true);
        // TODO
    }
}

export function syncGraphQL({
    url,
    headers = {},
    waitForLeadership = true,
    pull,
    push,
    deletedFlag,
    live = false,
    liveInterval = 1000 * 10, // in ms
    retryTime = 1000 * 5, // in ms
    autoStart = true // if this is false, the replication does nothing at start
}) {
    console.log('syncGraphQL(): 1');
    const collection = this;

    // fill in defaults for pull & push
    if (pull) {
        if (!pull.modifier) pull.modifier = DEFAULT_MODIFIER;
    }
    if (push) {
        if (!push.modifier) push.modifier = DEFAULT_MODIFIER;
    }

    // ensure the collection is listening to plain-pouchdb writes
    console.log('syncGraphQL(): 2');
    collection.watchForChanges();

    console.log('syncGraphQL(): 3');
    const replicationState = new RxGraphQLReplicationState(
        collection,
        url,
        headers,
        pull,
        push,
        deletedFlag,
        live,
        liveInterval,
        retryTime
    );
    console.log('syncGraphQL(): 4');

    if (!autoStart) return replicationState;

    // run internal so .sync() does not have to be async
    const waitTillRun = waitForLeadership ? this.database.waitForLeadership() : promiseWait(0);
    waitTillRun.then(() => {

        // trigger run once
        replicationState.run();

        // start sync-interval
        if (replicationState.live) {

            if (pull) {
                (async () => {
                    while (!replicationState.isStopped()) {
                        await promiseWait(replicationState.liveInterval);
                        if (replicationState.isStopped()) return;
                        await replicationState.run();
                    }
                })();
            }

            if (push) {
                replicationState.changesSub = collection.pouch.changes({
                    since: 'now',
                    live: true,
                    include_docs: true
                }).on('change', function (change) {
                    if (replicationState.isStopped()) return;

                    const rev = change.doc._rev;
                    if (!wasRevisionfromPullReplication(
                        replicationState.endpointHash,
                        rev
                    )) {
                        replicationState.run();
                    }
                });

            }
        }
    });

    return replicationState;
}

export const rxdb = true;
export const prototypes = {
    RxCollection: proto => {
        proto.syncGraphQL = syncGraphQL;
    }
};

export default {
    rxdb,
    prototypes
};<commit_msg>CHORE 2<commit_after>/**
 * this plugin adds the RxCollection.syncGraphQl()-function to rxdb
 * you can use it to sync collections with remote graphql endpoint
 */

import {
    BehaviorSubject,
    Subject
} from 'rxjs';
import {
    first,
    filter
} from 'rxjs/operators';
import GraphQLClient from 'graphql-client';

import {
    promiseWait
} from '../../util';

import Core from '../../core';
import {
    hash,
    clone
} from '../../util';

import {
    DEFAULT_MODIFIER,
    wasRevisionfromPullReplication,
    createRevisionForPulledDocument,
    getDocsWithRevisionsFromPouch
} from './helper';
import {
    setLastPushSequence,
    getLastPullDocument,
    setLastPullDocument,
    getChangesSinceLastPushSequence
} from './crawling-checkpoint';

import RxDBWatchForChangesPlugin from '../watch-for-changes';
import {
    changeEventfromPouchChange
} from '../../rx-change-event';

/**
 * add the watch-for-changes-plugin
 * so pouchdb will emit events when something gets written to it
 */
Core.plugin(RxDBWatchForChangesPlugin);

export class RxGraphQLReplicationState {
    constructor(
        collection,
        url,
        headers,
        pull,
        push,
        deletedFlag,
        live,
        liveInterval,
        retryTime
    ) {
        this.collection = collection;
        this.client = GraphQLClient({
            url,
            headers
        });
        this.endpointHash = hash(url);

        this.pull = pull;
        this.push = push;

        this.deletedFlag = deletedFlag;
        this.live = live;
        this.liveInterval = liveInterval;
        this.retryTime = retryTime;

        this._runQueueCount = 0;
        this._subs = [];
        this._runningPromise = Promise.resolve();
        this._subjects = {
            recieved: new Subject(), // all documents that are recieved from the endpoint
            send: new Subject(), // all documents that are send to the endpoint
            error: new Subject(), // all errors that are revieced from the endpoint, emits new Error() objects
            canceled: new BehaviorSubject(false), // true when the replication was canceled
            active: new BehaviorSubject(false), // true when something is running, false when not
            initialReplicationComplete: new BehaviorSubject(false) // true the initial replication-cycle is over
        };

        this._prepare();
    }

    /**
     * things that are more complex to not belong into the constructor
     */
    _prepare() {
        // stop sync when collection gets destroyed
        this.collection.onDestroy.then(() => {
            this.cancel();
        });

        // create getters for the observables
        Object.keys(this._subjects).forEach(key => {
            Object.defineProperty(this, key + '$', {
                get: function () {
                    return this._subjects[key].asObservable();
                }
            });
        });
    }

    isStopped() {
        if (!this.live && this._subjects.initialReplicationComplete._value) return true;
        if (this._subjects.canceled._value) return true;
        else return false;
    }

    awaitInitialReplication() {
        return this.initialReplicationComplete$.pipe(
            filter(v => v === true),
            first()
        ).toPromise();
    }

    // ensures this._run() does not run in parallel
    async run() {
        if (this.isStopped()) {
            return;
        }

        if (this._runQueueCount > 2) {
            return this._runningPromise;
        }

        this._runQueueCount++;
        this._runningPromise = this._runningPromise.then(async () => {
            this._subjects.active.next(true);
            const willRetry = await this._run();
            this._subjects.active.next(false);

            if (!willRetry && this._subjects.initialReplicationComplete._value === false)
                this._subjects.initialReplicationComplete.next(true);

            this._runQueueCount--;
        });
        return this._runningPromise;
    }

    async _run() {
        let willRetry = false;

        if (this.push) {
            const ok = await this.runPush();
            if (!ok) {
                willRetry = true;
                setTimeout(() => this.run(), this.retryTime);
            }
        }

        if (this.pull) {
            const ok = await this.runPull();
            if (!ok) {
                willRetry = true;
                setTimeout(() => this.run(), this.retryTime);
            }
        }

        return willRetry;

    }

    /**
     * @return {boolean} true if no errors occured
     */
    async runPull() {
        // console.log('RxGraphQLReplicationState.runPull(): start');
        if (this.isStopped()) return;

        const latestDocument = await getLastPullDocument(this.collection, this.endpointHash);
        const latestDocumentData = latestDocument ? latestDocument : null;
        const pullGraphQL = this.pull.queryBuilder(latestDocumentData);

        let result;
        try {
            result = await this.client.query(pullGraphQL.query, pullGraphQL.variables);
            if (result.errors) {
                throw new Error(result.errors);
            }
        } catch (err) {
            this._subjects.error.next(err);
            setTimeout(() => this.run(), this.retryTime);
            return false;
        }

        // this assumes that there will be always only one property in the response
        // is this correct?
        const data = result.data[Object.keys(result.data)[0]];

        const modified = data.map(doc => this.pull.modifier(doc));

        const docIds = modified.map(doc => doc[this.collection.schema.primaryPath]);
        const docsWithRevisions = await getDocsWithRevisionsFromPouch(
            this.collection,
            docIds
        );
        await Promise.all(modified.map(doc => this.handleDocumentFromRemote(doc, docsWithRevisions)));
        modified.map(doc => this._subjects.recieved.next(doc));

        if (modified.length === 0) {
            if (this.live) {
                // console.log('no more docs, wait for ping');
            } else {
                // console.log('RxGraphQLReplicationState._run(): no more docs and not live; complete = true');
            }
        } else {
            const newLatestDocument = modified[modified.length - 1];
            await setLastPullDocument(
                this.collection,
                this.endpointHash,
                newLatestDocument
            );

            // we have more docs, re-run
            await this.runPull();
        }

        return true;
    }

    async runPush() {
        // console.log('RxGraphQLReplicationState.runPush(): start');

        const changes = await getChangesSinceLastPushSequence(
            this.collection,
            this.endpointHash,
            this.push.batchSize
        );

        const changesWithDocs = changes.results.map(change => {
            let doc = change.doc;

            doc[this.deletedFlag] = !!change.deleted;
            delete doc._rev;
            delete doc._deleted;
            delete doc._attachments;

            doc = this.push.modifier(doc);

            const seq = change.seq;
            return {
                doc,
                seq
            };
        });

        let lastSuccessfullChange = null;
        try {
            /**
             * we cannot run all queries parallel
             * because then we would not know
             * where to start again on errors
             * so we run through the docs in series
             */
            for (let i = 0; i < changesWithDocs.length; i++) {
                const changeWithDoc = changesWithDocs[i];
                const pushObj = this.push.queryBuilder(changeWithDoc.doc);
                const result = await this.client.query(pushObj.query, pushObj.variables);
                if (result.errors) {
                    throw new Error(result.errors);
                } else {
                    this._subjects.send.next(changeWithDoc.doc);
                    lastSuccessfullChange = changeWithDoc;
                }
            }
        } catch (err) {

            if (lastSuccessfullChange) {
                await setLastPushSequence(
                    this.collection,
                    this.endpointHash,
                    lastSuccessfullChange.seq
                );
            }

            this._subjects.error.next(err);
            setTimeout(() => this.run(), this.retryTime);
            return false;
        }

        // all docs where successfull, so we use the seq of the changes-fetch
        await setLastPushSequence(
            this.collection,
            this.endpointHash,
            changes.last_seq
        );

        if (changes.results.length === 0) {
            if (this.live) {
                // console.log('no more docs to push, wait for ping');
            } else {
                // console.log('RxGraphQLReplicationState._runPull(): no more docs to push and not live; complete = true');
            }
        } else {
            // we have more docs, re-run
            await this.runPush();
        }

        return true;
    }

    async handleDocumentFromRemote(doc, docsWithRevisions) {
        const deletedValue = doc[this.deletedFlag];
        const toPouch = this.collection._handleToPouch(doc);
        // console.log('handleDocumentFromRemote(' + toPouch._id + ') start');
        toPouch._deleted = deletedValue;
        delete toPouch[this.deletedFlag];
        const primaryValue = toPouch._id;

        const pouchState = docsWithRevisions[primaryValue];
        let newRevision = createRevisionForPulledDocument(
            this.endpointHash,
            toPouch
        );
        if (pouchState) {
            const newRevisionHeight = pouchState.revisions.start + 1;
            const revisionId = newRevision;
            newRevision = newRevisionHeight + '-' + newRevision;
            toPouch._revisions = {
                start: newRevisionHeight,
                ids: pouchState.revisions.ids
            };
            toPouch._revisions.ids.unshift(revisionId);
        } else {
            newRevision = '1-' + newRevision;
        }
        toPouch._rev = newRevision;

        await this.collection.pouch.bulkDocs(
            [
                toPouch
            ], {
                new_edits: false
            }
        );

        /**
         * because bulkDocs with new_edits: false
         * does not stream changes to the pouchdb,
         * we create the event and emit it,
         * so other instances get informed about it
         */
        const originalDoc = clone(toPouch);
        originalDoc._deleted = deletedValue;
        delete originalDoc[this.deletedFlag];
        originalDoc._rev = newRevision;
        const cE = changeEventfromPouchChange(
            originalDoc,
            this.collection
        );
        this.collection.$emit(cE);
    }

    cancel() {
        if (this.isStopped()) return;

        if (this.changesSub) this.changesSub.cancel();

        this._subjects.canceled.next(true);
        // TODO
    }
}

export function syncGraphQL({
    url,
    headers = {},
    waitForLeadership = true,
    pull,
    push,
    deletedFlag,
    live = false,
    liveInterval = 1000 * 10, // in ms
    retryTime = 1000 * 5, // in ms
    autoStart = true // if this is false, the replication does nothing at start
}) {
    console.log('syncGraphQL(): 1');
    const collection = this;

    // fill in defaults for pull & push
    if (pull) {
        if (!pull.modifier) pull.modifier = DEFAULT_MODIFIER;
    }
    if (push) {
        if (!push.modifier) push.modifier = DEFAULT_MODIFIER;
    }

    // ensure the collection is listening to plain-pouchdb writes
    collection.watchForChanges();

    const replicationState = new RxGraphQLReplicationState(
        collection,
        url,
        headers,
        pull,
        push,
        deletedFlag,
        live,
        liveInterval,
        retryTime
    );

    if (!autoStart) return replicationState;

    // run internal so .sync() does not have to be async
    const waitTillRun = waitForLeadership ? this.database.waitForLeadership() : promiseWait(0);
    waitTillRun.then(() => {

        // trigger run once
        replicationState.run();

        // start sync-interval
        if (replicationState.live) {

            if (pull) {
                (async () => {
                    while (!replicationState.isStopped()) {
                        await promiseWait(replicationState.liveInterval);
                        if (replicationState.isStopped()) return;
                        await replicationState.run();
                    }
                })();
            }

            if (push) {
                replicationState.changesSub = collection.pouch.changes({
                    since: 'now',
                    live: true,
                    include_docs: true
                }).on('change', function (change) {
                    if (replicationState.isStopped()) return;

                    const rev = change.doc._rev;
                    if (!wasRevisionfromPullReplication(
                        replicationState.endpointHash,
                        rev
                    )) {
                        replicationState.run();
                    }
                });

            }
        }
    });

    return replicationState;
}

export const rxdb = true;
export const prototypes = {
    RxCollection: proto => {
        proto.syncGraphQL = syncGraphQL;
    }
};

export default {
    rxdb,
    prototypes
};<|endoftext|>


<commit_before>(function (w, $) {
	w.toolbelt = {};

	w.toolbelt.resizeFunctions = [];
	w.toolbelt.addEventOnWindowResize = function (f) {
		$(window).unbind('resize').bind('resize', w.toolbelt.runWindowResizeEvents);
		w.toolbelt.resizeFunctions.push(f);
		f();
	};
	w.toolbelt.runWindowResizeEvents = function () {
		for (var i = 0, l = w.toolbelt.resizeFunctions.length; i < l; i++) {
			w.toolbelt.resizeFunctions[i]();
		}
	};

	w.toolbelt.checkSWFObject = function (cb) {
		if (typeof swfobject === 'undefined' || swfobject === null) {
			$.getScript(window.location.protocol + '//ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js', function () {
				if (!!cb) {
					cb();
				}
			});
		} else {
			if (!!cb) {
				cb();
			}
		}
	};

	w.toolbelt.exists = function(obj) {
		return typeof obj !== 'undefined' && !!obj;
	};

	//I know people don't like this, but sometimes you need to know if it's iOS, IE or Android, sorry
	w.toolbelt.checkBrowser = function(browser) {
		return navigator.userAgent.toLowerCase().match(new RegExp(type)) != null;
	};

	w.toolbelt.visible = function(obj) {
		if (typeof obj === 'string') {
			obj = $(obj);
		}

		if (!obj.is(':visible')) {
			return false;
		} else if (obj.is(':hidden')) {
			return true;
		}

		var $window = $(w),
			docViewTop = $window.scrollTop(),
			docViewBottom = docViewTop + $window.height(),
			elemTop = obj.offset().top,
			elemBottom = elemTop + obj.height();

		return elemBottom >= docViewTop && elemTop <= docViewBottom;
	};
}(window, jQuery));<commit_msg>Add some sample browser sniffing<commit_after>(function (w, $) {
	w.toolbelt = {};

	w.toolbelt.resizeFunctions = [];
	w.toolbelt.addEventOnWindowResize = function (f) {
		$(window).unbind('resize').bind('resize', w.toolbelt.runWindowResizeEvents);
		w.toolbelt.resizeFunctions.push(f);
		f();
	};
	w.toolbelt.runWindowResizeEvents = function () {
		for (var i = 0, l = w.toolbelt.resizeFunctions.length; i < l; i++) {
			w.toolbelt.resizeFunctions[i]();
		}
	};

	w.toolbelt.checkSWFObject = function (cb) {
		if (typeof swfobject === 'undefined' || swfobject === null) {
			$.getScript(window.location.protocol + '//ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js', function () {
				if (!!cb) {
					cb();
				}
			});
		} else {
			if (!!cb) {
				cb();
			}
		}
	};

	w.toolbelt.exists = function(obj) {
		return typeof obj !== 'undefined' && !!obj;
	};

	//I know people don't like this, but sometimes you need to know if it's iOS, IE or Android, sorry
	w.toolbelt.checkBrowser = function(browser) {
		return navigator.userAgent.toLowerCase().match(new RegExp(browser)) != null;
	};
	w.toolbelt.isMobile = function() {
		return w.toolbelt.checkBrowser('iphone|ipod|ipad|mobile|android');
	};
	w.toolbelt.isIOS = function() {
		return w.toolbelt.checkBrowser('iphone|ipod|ipad|mobile safari');
	};
	w.toolbelt.isIPad = function() {
		return w.toolbelt.checkBrowser('ipad');
	};
	w.toolbelt.isIPod = function() {
		return w.toolbelt.checkBrowser('ipod');
	};
	w.toolbelt.isIPhone = function() {
		return w.toolbelt.checkBrowser('iphone');
	};
	w.toolbelt.isAndroid = function() {
		return w.toolbelt.checkBrowser('android');
	};
	w.toolbelt.isWindowsPhone = function() {
		return w.toolbelt.checkBrowser('iemobile|Windows Phone');
	};

	w.toolbelt.visible = function(obj) {
		if (typeof obj === 'string') {
			obj = $(obj);
		}

		if (!obj.is(':visible')) {
			return false;
		} else if (obj.is(':hidden')) {
			return true;
		}

		var $window = $(w),
			docViewTop = $window.scrollTop(),
			docViewBottom = docViewTop + $window.height(),
			elemTop = obj.offset().top,
			elemBottom = elemTop + obj.height();

		return elemBottom >= docViewTop && elemTop <= docViewBottom;
	};
}(window, jQuery));<|endoftext|>