<commit_before>(function($) {
    "use strict";

    var _isInit = false;
    var _baseID = null;
    var _pdf = null;
    var _pageNum = 1;
    var _canvas = null;
    var _context = null;
    var _pageRendering = false;
    var _pageNumPending = null;
    var _scale = 1.5;

    function onPrevPage() {
        if (_pageNum <= 1) {
            return;
        }
        --_pageNum;
        queueRenderPage(_pageNum);
    }

    function onNextPage() {
        if (_pageNum >= _pdf.numPages) {
            return;
        }
        ++_pageNum;
        queueRenderPage(_pageNum);
    }

    function queueRenderPage(num) {
        if (_pageRendering) {
            _pageNumPending = num;
        } else {
            renderPage(num);
        }
    }

    function adjustPageControls(num) {
        $("#" + _baseID + "Num").text(num);

        if (num <= 1) {
            $("#" + _baseID + "Pre").attr("disabled", true);
        } else {
            $("#" + _baseID + "Pre").attr("disabled", false);
        }

        if (num >= _pdf.numPages) {
            $("#" + _baseID + "Next").attr("disabled", true);
        } else {
            $("#" + _baseID + "Next").attr("disabled", false);
        }
    }

    function renderPage(num) {
        _pageRendering = true;
        _pdf.getPage(num).then(function(page) {
            W3LogDebug("Page rendering: " + num);

            var scale = _scale;
            var viewport = page.getViewport({scale: scale});

            _canvas.height = viewport.height;
            _canvas.width = viewport.width;

            var renderContext = {
                canvasContext: _context,
                viewport: viewport
            };
            var renderTask = page.render(renderContext);
            renderTask.promise.then(function () {
                W3LogDebug("Page rendered: " + num);

                _pageRendering = false;
                adjustPageControls(num);

                if (_pageNumPending !== null) {
                    renderPage(_pageNumPending);
                    _pageNumPending = null;
                }
            });
        });
    }

    function InitPDFUIImpl()
    {
        var thisElement = $(this);
        var thisHtml = thisElement.get(0).outerHTML;
        _baseID = thisElement.attr("id");

        var canvasPattern = new RegExp("^<canvas.*canvas>$");
        if (!canvasPattern.test(thisHtml)) {
            W3LogError("Init PDF on wrong element");
            return;
        }

        thisHtml =
            "
" +
            " <canvas id='" + _baseID + "Canvas' style='display:none'></canvas>" +
            " <textarea id='" + _baseID + "Log' rows='32' cols='80' style='display:none' disabled='disabled'></textarea>" +
            "

" +
            "  <button id='" + _baseID + "Pre' onclick='' type='button' disabled='disabled'><</button>	" +
            "  0	" +
            "   / 	" +
            "  0	" +
            "  <button id='" + _baseID + "Next' onclick='' type='button' disabled='disabled'>></button>	" +
            "

" +
            " <textarea id='" + _baseID + "' style='display:none'></textarea>" +
            "
";
        thisElement.replaceWith(thisHtml);

        var nextBtn = document.getElementById(_baseID + "Next");
        if (nextBtn != null) {
            nextBtn.addEventListener('click', onNextPage);
        } else {
            W3LogWarning("PDF next button is not found");
        }

        var preBtn = document.getElementById(_baseID + "Pre");
        if (nextBtn != null) {
            preBtn.addEventListener('click', onPrevPage);
        } else {
            W3LogWarning("PDF previous button is not found");
        }

        _isInit = true;
    }

    function Validate(errorLog)
    {
        return errorLog == "";
    }

    function DisplayErrorLog(errorLog)
    {
        var errorLogDecode = W3DecodePHP(errorLog);
        $("#" + _baseID + "Log").val(errorLogDecode);

        W3DisplayUI(_baseID + "Log");
        W3HideUI(_baseID + "Canvas");

        $("#" + _baseID + "TotalNum").text(0);
        $("#" + _baseID + "Num").text(0);
        $("#" + _baseID + "Pre").attr("disabled", true);
        $("#" + _baseID + "Next").attr("disabled", true);
    }

    function DisplayPDFImpl(noteID, errorLog)
    {
        if (!_isInit) {
            W3LogError("Init PDF UI first!");
        }

        if (!Validate(errorLog)) {
            DisplayErrorLog(errorLog);
            return;
        }

        W3DisplayUI(_baseID + "Canvas");

        var url = "tmp/" + noteID + ".pdf";
        var canvasID = _baseID + "Canvas";
        _canvas = document.getElementById(canvasID);
        if (_canvas == null) {
            W3LogError("No PDF canvas found");
            return;
        }

        _context = _canvas.getContext('2d');
        var pdfjsLib = window['pdfjs-dist/build/pdf'];
        pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdf/build/pdf.worker.js';

        // Asynchronous download of PDF
        var loadingTask = pdfjsLib.getDocument(url);
        loadingTask.promise.then(
            function(pdf) {
                W3LogDebug('PDF loaded');

                _pdf = pdf;

                $("#" + _baseID + "TotalNum").text(_pdf.numPages);
                _pageNum = 1;

                renderPage(_pageNum);
            },
            function (reason) {
                W3LogError(reason);
            }
        );
    }

    $.fn.extend({
        W3DisplayPDF: DisplayPDFImpl,
        W3PDF: InitPDFUIImpl
    });

} (jQuery));
<commit_msg>Add border for pdf canvas<commit_after>(function($) {
    "use strict";

    var _isInit = false;
    var _baseID = null;
    var _pdf = null;
    var _pageNum = 1;
    var _canvas = null;
    var _context = null;
    var _pageRendering = false;
    var _pageNumPending = null;
    var _scale = 1.5;

    function onPrevPage() {
        if (_pageNum <= 1) {
            return;
        }
        --_pageNum;
        queueRenderPage(_pageNum);
    }

    function onNextPage() {
        if (_pageNum >= _pdf.numPages) {
            return;
        }
        ++_pageNum;
        queueRenderPage(_pageNum);
    }

    function queueRenderPage(num) {
        if (_pageRendering) {
            _pageNumPending = num;
        } else {
            renderPage(num);
        }
    }

    function adjustPageControls(num) {
        $("#" + _baseID + "Num").text(num);

        if (num <= 1) {
            $("#" + _baseID + "Pre").attr("disabled", true);
        } else {
            $("#" + _baseID + "Pre").attr("disabled", false);
        }

        if (num >= _pdf.numPages) {
            $("#" + _baseID + "Next").attr("disabled", true);
        } else {
            $("#" + _baseID + "Next").attr("disabled", false);
        }
    }

    function renderPage(num) {
        _pageRendering = true;
        _pdf.getPage(num).then(function(page) {
            W3LogDebug("Page rendering: " + num);

            var scale = _scale;
            var viewport = page.getViewport({scale: scale});

            _canvas.height = viewport.height;
            _canvas.width = viewport.width;

            var renderContext = {
                canvasContext: _context,
                viewport: viewport
            };
            var renderTask = page.render(renderContext);
            renderTask.promise.then(function () {
                W3LogDebug("Page rendered: " + num);

                _pageRendering = false;
                adjustPageControls(num);

                if (_pageNumPending !== null) {
                    renderPage(_pageNumPending);
                    _pageNumPending = null;
                }
            });
        });
    }

    function InitPDFUIImpl()
    {
        var thisElement = $(this);
        var thisHtml = thisElement.get(0).outerHTML;
        _baseID = thisElement.attr("id");

        var canvasPattern = new RegExp("^<canvas.*canvas>$");
        if (!canvasPattern.test(thisHtml)) {
            W3LogError("Init PDF on wrong element");
            return;
        }

        thisHtml =
            "
" +
            " <canvas id='" + _baseID + "Canvas' style='display:none; border:1px dashed'></canvas>" +
            " <textarea id='" + _baseID + "Log' rows='32' cols='80' style='display:none' disabled='disabled'></textarea>" +
            "

" +
            "  <button id='" + _baseID + "Pre' onclick='' type='button' disabled='disabled'><</button>	" +
            "  0	" +
            "   / 	" +
            "  0	" +
            "  <button id='" + _baseID + "Next' onclick='' type='button' disabled='disabled'>></button>	" +
            "

" +
            " <textarea id='" + _baseID + "' style='display:none'></textarea>" +
            "
";
        thisElement.replaceWith(thisHtml);

        var nextBtn = document.getElementById(_baseID + "Next");
        if (nextBtn != null) {
            nextBtn.addEventListener('click', onNextPage);
        } else {
            W3LogWarning("PDF next button is not found");
        }

        var preBtn = document.getElementById(_baseID + "Pre");
        if (nextBtn != null) {
            preBtn.addEventListener('click', onPrevPage);
        } else {
            W3LogWarning("PDF previous button is not found");
        }

        _isInit = true;
    }

    function Validate(errorLog)
    {
        return errorLog == "";
    }

    function DisplayErrorLog(errorLog)
    {
        var errorLogDecode = W3DecodePHP(errorLog);
        $("#" + _baseID + "Log").val(errorLogDecode);

        W3DisplayUI(_baseID + "Log");
        W3HideUI(_baseID + "Canvas");

        $("#" + _baseID + "TotalNum").text(0);
        $("#" + _baseID + "Num").text(0);
        $("#" + _baseID + "Pre").attr("disabled", true);
        $("#" + _baseID + "Next").attr("disabled", true);
    }

    function DisplayPDFImpl(noteID, errorLog)
    {
        if (!_isInit) {
            W3LogError("Init PDF UI first!");
        }

        if (!Validate(errorLog)) {
            DisplayErrorLog(errorLog);
            return;
        }

        W3DisplayUI(_baseID + "Canvas");

        var url = "tmp/" + noteID + ".pdf";
        var canvasID = _baseID + "Canvas";
        _canvas = document.getElementById(canvasID);
        if (_canvas == null) {
            W3LogError("No PDF canvas found");
            return;
        }

        _context = _canvas.getContext('2d');
        var pdfjsLib = window['pdfjs-dist/build/pdf'];
        pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdf/build/pdf.worker.js';

        // Asynchronous download of PDF
        var loadingTask = pdfjsLib.getDocument(url);
        loadingTask.promise.then(
            function(pdf) {
                W3LogDebug('PDF loaded');

                _pdf = pdf;

                $("#" + _baseID + "TotalNum").text(_pdf.numPages);
                _pageNum = 1;

                renderPage(_pageNum);
            },
            function (reason) {
                W3LogError(reason);
            }
        );
    }

    $.fn.extend({
        W3DisplayPDF: DisplayPDFImpl,
        W3PDF: InitPDFUIImpl
    });

} (jQuery));
<|endoftext|>


<commit_before>var ServiceVM = function(model)
{
	var self = this;

	self.Name = ko.observable(model.ServiceName);
	self.Status = ko.observable(model.ServiceStatus)
	self.Desc = ko.observable(model.ServiceDesc);

    self.IsActive = ko.observable(model.IsActive);
    self.IsRunning = ko.observable(model.IsRunning);
    self.IsLoaded = ko.observable(model.IsLoaded);

	self.Waiting = ko.observable(false);

	self.Timer = null;

	self.Refresh = function()
	{
		self.Waiting(true);
		setTimeout(self.EndRefresh, 1000);
	}

	self.EndRefresh = function()
	{
		self.Waiting(false);

		if(self.Timer != null)
		{
			clearTimeout(self.Timer);
		}

		self.Timer = setTimeout(self.Refresh, 5000);

	}

	self.RunService = function()
	{
		if(!self.IsRunning())
		{
			$.ajax({
			    method: 'POST',
			    dataType: "json",
			    url: "/service/" + self.Name() + "/on",
			    contentType: "application/json",
			    beforeSend: function () {
			    },
			    complete: function () {
				    self.EndRefresh();
			    },
			    success:
				function (allData) {
				    self.IsRunning(true);
				},
			    error: function (XMLHttpRequest, textStatus, errorThrown) {
				alert("Erreur");
				//self.actionEnCoursDocuments(false);
			    }
			});
		}
	}

	self.StopService = function()
	{
		if(self.IsRunning())
		{
			$.ajax({
			    method: 'POST',
			    dataType: "json",
			    url: "/service/" + self.Name() + "/off",
			    contentType: "application/json",
			    beforeSend: function () {
			    },
			    complete: function () {
				    	self.EndRefresh();
			    },
			    success:
				function (allData) {
					self.IsRunning(false);
				},
			    error: function (XMLHttpRequest, textStatus, errorThrown) {
				alert("Erreur");
				//self.actionEnCoursDocuments(false);
			    }
			});
		}
	}

	self.ToggleService = function()
	{
		if(self.IsRunning())
			self.StopService();
		else
			self.RunService();
	}

	self.UpdateInfos = function()
	{
		$.ajax({
			    method: 'POST',
			    dataType: "json",
			    url: "/service/" + self.Name(),
			    contentType: "application/json",
			    beforeSend: function () {
			    },
			    complete: function () {
				    	self.EndRefresh();
			    },
			    success:
				function (allData) {
					alert(allData);
				},
			    error: function (XMLHttpRequest, textStatus, errorThrown) {
				alert("Erreur");
				//self.actionEnCoursDocuments(false);
			    }
			});
	}

	self.EndRefresh();
}
<commit_msg>Update ServiceVM.js<commit_after>var ServiceVM = function(model)
{
	var self = this;

	self.Name = ko.observable(model.ServiceName);
	self.Status = ko.observable(model.ServiceStatus)
	self.Desc = ko.observable(model.ServiceDesc);

    self.IsActive = ko.observable(model.IsActive);
    self.IsRunning = ko.observable(model.IsRunning);
    self.IsLoaded = ko.observable(model.IsLoaded);

	self.Waiting = ko.observable(false);

	self.Timer = null;

	self.Refresh = function()
	{
		self.Waiting(true);
		setTimeout(self.EndRefresh, 1000);
	}

	self.EndRefresh = function()
	{
		self.Waiting(false);

		if(self.Timer != null)
		{
			clearTimeout(self.Timer);
		}

		self.Timer = setTimeout(self.Refresh, 5000);

	}

	self.RunService = function()
	{
		if(!self.IsRunning())
		{
			$.ajax({
			    method: 'GET',
			    dataType: "json",
			    url: "/service/" + self.Name() + "/on",
			    contentType: "application/json",
			    beforeSend: function () {
			    },
			    complete: function () {
				    self.EndRefresh();
			    },
			    success:
				function (allData) {
				    self.IsRunning(true);
				},
			    error: function (XMLHttpRequest, textStatus, errorThrown) {
				alert("Erreur");
				//self.actionEnCoursDocuments(false);
			    }
			});
		}
	}

	self.StopService = function()
	{
		if(self.IsRunning())
		{
			$.ajax({
			    method: 'GET',
			    dataType: "json",
			    url: "/service/" + self.Name() + "/off",
			    contentType: "application/json",
			    beforeSend: function () {
			    },
			    complete: function () {
				    	self.EndRefresh();
			    },
			    success:
				function (allData) {
					self.IsRunning(false);
				},
			    error: function (XMLHttpRequest, textStatus, errorThrown) {
				alert("Erreur");
				//self.actionEnCoursDocuments(false);
			    }
			});
		}
	}

	self.ToggleService = function()
	{
		if(self.IsRunning())
			self.StopService();
		else
			self.RunService();
	}

	self.UpdateInfos = function()
	{
		$.ajax({
			    method: 'GET',
			    dataType: "json",
			    url: "/service/" + self.Name(),
			    contentType: "application/json",
			    beforeSend: function () {
			    },
			    complete: function () {
				    	self.EndRefresh();
			    },
			    success:
				function (allData) {
					alert(allData);
				},
			    error: function (XMLHttpRequest, textStatus, errorThrown) {
				alert("Erreur");
				//self.actionEnCoursDocuments(false);
			    }
			});
	}

	self.EndRefresh();
}
<|endoftext|>


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

    var API_SERVER = window.location.hostname + ':9003',
        SITE_URL = 'http://lunasync.ajf.me';

    var mode, socket, player, ytReady = false;

    var state = {
        playing: false,
        current: null,
        playlist: []
    }, haveControl = false, chatNick = null;

    function $(id) {
        return document.getElementById(id);
    }

    window.onYouTubePlayerReady = function () {
        ytReady = true;
        player = $('player');
    };

    window.onload = function () {
        var id, control;

        // get them before IE errors out
        if (!Object.prototype.hasOwnProperty.call(window, 'WebSocket')) {
            document.innerHTML = 'lunasync requires a browser that supports WebSocket, such as Google Chrome, Mozilla Firefox, Apple Safari, Opera or Internet Explorer 10. Sorry :(';
            return;
        }

        // disable document-hiding style
        $('site').className = '';

        // display basic UI elements present on all pages
        initBasic();

        // display correct page for URL
        if (window.location.pathname === '/') {
            mode = 'home';
            initHome();
        } else if (window.location.pathname[0] === '/') {
            mode = 'view';
            id = window.location.pathname.substr(1);
            if (window.location.search.substr(0, 9) === '?control=') {
                control = window.location.search.substr(9);
            }
            initView(id, control);
        } else {
            mode = '404';
            init404();
        }
    };

    function doAJAX(method, url, data, callback, errback) {
        var xhr;

        xhr = new XMLHttpRequest();
        xhr.open(method, 'http://' + API_SERVER + url);
        xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    callback(JSON.parse(xhr.responseText));
                } else {
                    errback(xhr);
                }
            }
        };
        if (data) {
            xhr.send(JSON.stringify(data));
        } else {
            xhr.send();
        }
    }

    function send(msg) {
        console.log(msg);
        socket.send(JSON.stringify(msg));
    }

    function initBasic() {
        $('create-new').onclick = function () {
            var title;

            title = prompt('Enter a title:', '');
            doAJAX('POST', '/new', {
                title: title
            }, function (response) {
                window.location = '/' + response.stream.id + '?control=' + response.stream.secret;
            }, function (xhr) {
                alert('Error while trying to create new sync:\nResponse code:\n' + xhr.status + '\nError message:' + xhr.responseText);
            });
        };
    }

    // homepage
    function initHome() {
        // unhide home page
        $('homepage').className = '';
    }

    // sync viewing page
    function initView(id, control) {
        // unhide view page
        $('viewpage').className = '';

        if (!ytReady) {
            window.onYouTubePlayerReady = function () {
                player = $('player');
                initRestView(id, control);
            };
        } else {
            initRestView(id, control);
        }
    }

    function initRestView(id, control) {
        var url;

        window.onStateChange = function (pstate) {
            var cueIndex;

            // if we are controlling
            if (haveControl) {
                // if video paused and it was playing according to known state
                if (pstate === 2 /* YT.PlayerState.PAUSED */ && state.playing) {
                    // broadcast state change
                    send({
                        type: 'stop',
                        time: player.getCurrentTime()
                    });
                    state.playing = false;
                    console.log('stop!');
                // if video started playing and it was paused according to known state
                } else if (pstate === 1 /*YT.PlayerState.PLAYING*/ && !state.playing) {
                    // broadcast state change
                    send({
                        type: 'play',
                        time: player.getCurrentTime()
                    });
                    state.playing = true;
                    console.log('play!');
                // if the video ended
                } else if (pstate === 0 /*YT.PlayerState.ENDED*/) {
                    // if we haven't reached the end of the playlist
                    if (state.current + 1 < state.playlist.length) {
                        // cue next video
                        cueIndex = state.current + 1;
                    } else {
                        // cue first video
                        cueIndex = 0;
                    }

                    state.current = cueIndex;
                    state.playing = true;

                    send({
                        type: 'cue',
                        current: cueIndex
                    });

                    player.loadVideoById(state.playlist[cueIndex].id);

                    updatePlaylist();
                }
            }
        };
        player.addEventListener('onStateChange', 'onStateChange');

        socket = new WebSocket('ws://' + API_SERVER, ['lunasync']);
        socket.onopen = function () {
            // set our stream (subscribe to events) and get info on it
            send({
                type: 'set_stream',
                id: id,
                control: control
            });
        };
        socket.onerror = function (err) {
            $('viewpage').innerHTML = 'Error communicating with server, lost connection:\n' + err;
        };
        socket.onmessage = function (event) {
            var msg, stream, elem;

            msg = JSON.parse(event.data);

            console.log(msg.type);

            switch (msg.type) {
                case 'stream_info':
                    stream = msg.stream;

                    // display stream title
                    $('title').value = stream.title;

                    // display stream viewing URL
                    $('view-link').value = SITE_URL + '/' + stream.id;

                    // update state
                    state.playing = stream.playing;
                    state.current = stream.current;
                    state.playlist = stream.playlist;

                    // display playlist
                    updatePlaylist();

                    // cue and play correct video
                    state.playing = stream.playing;
                    state.current = stream.current;
                    if (state.current !== null) {
                        if (state.playing) {
                            player.loadVideoById(state.playlist[state.current].id);
                        } else {
                            player.cueVideoById(state.playlist[state.current].id);
                        }
                        player.seekTo(stream.time);
                    }

                    // if we have control of the stream
                    if (msg.control) {
                        haveControl = true;

                        // allow changing title
                        $('title').disabled = false;
                        $('title').onchange = function () {
                            send({
                                type: 'change_title',
                                title: $('title').value
                            });
                        };

                        // unhide control box
                        $('control').className = '';

                        // display stream control URL
                        $('control-link').value = SITE_URL + '/' + stream.id + '?control=' + control;

                        // enable stream controls
                        $('rm-button').disabled = false;
                        $('rm-button').onclick = function () {
                            var i, items = $('playlist').selectedOptions, oldCurrent, current;

                            oldCurrent, current = state.playlist[state.current];

                            // remove selected playlist items
                            for (i = 0; i < items.length; i++) {
                                // if currently playing video being deleted, switch to next
                                if (state.playlist[items[i].index] === current) {
                                    // loop around
                                    if (items[i].index + 1 < state.playlist.length) {
                                        current = state.playlist[items[i].index + 1];
                                    } else {
                                        current = state.playlist[0];
                                    }
                                }
                                state.playlist.splice(items[i].index, 1);
                            }

                            updatePlaylist();

                            // push update to server by overwriting playlist
                            // (this may create race conditions...)
                            send({
                                type: 'update_playlist',
                                playlist: state.playlist
                            });

                            if (current !== state.playlist[state.current]) {
                                if (current !== null && state.playlist.indexOf(current) !== -1) {
                                    current = state.playlist.indexOf(current);
                                    send({
                                        type: 'cue',
                                        current: current
                                    });
                                } else {
                                    send({
                                        type: 'cue',
                                        current: null
                                    });
                                }
                            }
                        };

                        $('play-button').disabled = false;
                        $('play-button').onclick = $('playlist').ondblclick = function () {
                            var cueIndex;

                            if ($('playlist').selectedOptions.length === 0) {
                                cueIndex = 0;
                            } else {
                                cueIndex = $('playlist').selectedOptions[0].index;
                            }

                            send({
                                type: 'cue',
                                current: cueIndex
                            });
                        };

                        $('add-url').disabled = false;
                        $('add-url').onkeypress = function (e) {
                            var videoID;

                            // enter
                            if (e.which === 13) {
                                e.preventDefault();
                                $('add-url').blur();
                                videoID = getVideoID($('add-url').value);
                                if (videoID !== false) {
                                    send({
                                        type: 'add_url',
                                        id: videoID
                                    });
                                    $('add-url').value = '';
                                } else {
                                    alert($('add-url').value + ' is not a valid youtube URL!');
                                }
                                return false;
                            }
                        };
                        $('playlist').disabled = false;
                    }

                    // make chat work
                    $('chatbox').placeholder = 'choose a nick (press enter)';
                    $('chatbox').onkeypress = function (e) {
                        // enter
                        if (e.which === 13) {
                            e.preventDefault();
                            if (chatNick === null) {
                                send({
                                    type: 'set_nick',
                                    nick: $('chatbox').value
                                });
                                $('chatbox').disabled = true;
                            } else {
                                send({
                                    type: 'msg',
                                    msg: $('chatbox').value
                                });
                            }
                            $('chatbox').value = '';
                            return false;
                        }
                    };
                break;
                case 'update_playlist':
                    state.playlist = msg.playlist;
                    updatePlaylist();
                break;
                case 'change_title':
                    $('title').value = msg.title;
                break;
                case 'cue':
                    state.playing = true;
                    state.current = msg.current;
                    if (state.current === null) {
                        player.cueVideoById('');
                    } else {
                        player.loadVideoById(state.playlist[state.current].id);
                    }
                    updatePlaylist();
                break;
                case 'play':
                    console.log('play');
                    state.playing = true;
                    player.seekTo(msg.time, true);
                    player.playVideo();
                break;
                case 'stop':
                    console.log('stop');
                    state.playing = false;
                    player.seekTo(msg.time, true);
                    player.pauseVideo();
                break;
                case 'join':
                    elem = document.createElement('div');
                    console.log(msg.nick + ' joined chat');
                    elem.appendChild(document.createTextNode(msg.nick + ' joined chat'));
                    $('chatlog').appendChild(elem);
                    $('chatlog').scrollTop = $('chatlog').scrollHeight;
                break;
                case 'leave':
                    elem = document.createElement('div');
                    console.log(msg.nick + ' left chat');
                    elem.appendChild(document.createTextNode(msg.nick + ' left chat'));
                    $('chatlog').appendChild(elem);
                    $('chatlog').scrollTop = $('chatlog').scrollHeight;
                break;
                case 'msg':
                    elem = document.createElement('div');
                    console.log(msg.nick + ': ' + msg.msg);
                    elem.appendChild(document.createTextNode(msg.nick + ': ' + msg.msg));
                    $('chatlog').appendChild(elem);
                    $('chatlog').scrollTop = $('chatlog').scrollHeight;
                break;
                case 'nick_taken':
                    alert('The nick "' + msg.nick + '" ids already taken - choose another one!');
                    $('chatbox').disabled = false;
                break;
                case 'nick_chosen':
                    $('chatbox').placeholder = 'say something (press enter)';
                    chatNick = msg.nick;
                    $('chatbox').disabled = false;
                break;
                case 'error':
                    if (msg.error === 'not_found') {
                        $('viewpage').innerHTML = 'Error: no such stream exists. Did you copy the URL correctly?';
                    } else {
                        $('viewpage').innerHTML = 'Error communicating with server, lost connection:\n' + msg.error;
                    }
                break;
            }
        };
    }

    // "404" page
    function init404() {
        // unhide 404 page
        $('page404').className = '';
    }

    function updatePlaylist() {
        var i, option;

        $('playlist').innerHTML = '';
        for (i = 0; i < state.playlist.length; i++) {
            option = document.createElement('option');
            if (state.current === i) {
                option.appendChild(document.createTextNode('▶ '));
                option.className = 'now-playing';
            }
            option.appendChild(document.createTextNode(state.playlist[i].title));
            option.index = i;
            $('playlist').appendChild(option);
        }
    }

    function getVideoID(url) {
        var pos, pos2;

        if (url.substr(0, 7) === 'http://') {
            url = url.substr(7);
        } else if (url.substr(0, 8) === 'https://') {
            url = url.substr(8);
        } else {
            return false;
        }
        if (url.substr(0, 4) === 'www.') {
            url = url.substr(4);
        }
        if (url.substr(0, 9) === 'youtu.be/') {
            return url.substr(9);
        } else if (url.substr(0, 18) === 'youtube.com/watch?') {
            url = url.substr(18);
            pos = url.indexOf('v=');
            if (pos !== -1) {
                url = url.substr(pos + 2);
                pos2 = url.indexOf('&');
                if (pos2 === -1) {
                    return url;
                } else {
                    return url.substr(0, pos2);
                }
            } else {
                return false;
            }
        }
    }
}());
<commit_msg>sync time correctly on load<commit_after>(function () {
    'use strict';

    var API_SERVER = window.location.hostname + ':9003',
        SITE_URL = 'http://lunasync.ajf.me';

    var mode, socket, player, ytReady = false;

    var state = {
        playing: false,
        current: null,
        playlist: []
    }, haveControl = false, chatNick = null;

    function $(id) {
        return document.getElementById(id);
    }

    window.onYouTubePlayerReady = function () {
        ytReady = true;
        player = $('player');
    };

    window.onload = function () {
        var id, control;

        // get them before IE errors out
        if (!Object.prototype.hasOwnProperty.call(window, 'WebSocket')) {
            document.innerHTML = 'lunasync requires a browser that supports WebSocket, such as Google Chrome, Mozilla Firefox, Apple Safari, Opera or Internet Explorer 10. Sorry :(';
            return;
        }

        // disable document-hiding style
        $('site').className = '';

        // display basic UI elements present on all pages
        initBasic();

        // display correct page for URL
        if (window.location.pathname === '/') {
            mode = 'home';
            initHome();
        } else if (window.location.pathname[0] === '/') {
            mode = 'view';
            id = window.location.pathname.substr(1);
            if (window.location.search.substr(0, 9) === '?control=') {
                control = window.location.search.substr(9);
            }
            initView(id, control);
        } else {
            mode = '404';
            init404();
        }
    };

    function doAJAX(method, url, data, callback, errback) {
        var xhr;

        xhr = new XMLHttpRequest();
        xhr.open(method, 'http://' + API_SERVER + url);
        xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    callback(JSON.parse(xhr.responseText));
                } else {
                    errback(xhr);
                }
            }
        };
        if (data) {
            xhr.send(JSON.stringify(data));
        } else {
            xhr.send();
        }
    }

    function send(msg) {
        console.log(msg);
        socket.send(JSON.stringify(msg));
    }

    function initBasic() {
        $('create-new').onclick = function () {
            var title;

            title = prompt('Enter a title:', '');
            doAJAX('POST', '/new', {
                title: title
            }, function (response) {
                window.location = '/' + response.stream.id + '?control=' + response.stream.secret;
            }, function (xhr) {
                alert('Error while trying to create new sync:\nResponse code:\n' + xhr.status + '\nError message:' + xhr.responseText);
            });
        };
    }

    // homepage
    function initHome() {
        // unhide home page
        $('homepage').className = '';
    }

    // sync viewing page
    function initView(id, control) {
        // unhide view page
        $('viewpage').className = '';

        if (!ytReady) {
            window.onYouTubePlayerReady = function () {
                player = $('player');
                initRestView(id, control);
            };
        } else {
            initRestView(id, control);
        }
    }

    function initRestView(id, control) {
        var url;

        window.onStateChange = function (pstate) {
            var cueIndex;

            // if we are controlling
            if (haveControl) {
                // if video paused and it was playing according to known state
                if (pstate === 2 /* YT.PlayerState.PAUSED */ && state.playing) {
                    // broadcast state change
                    send({
                        type: 'stop',
                        time: player.getCurrentTime()
                    });
                    state.playing = false;
                    console.log('stop!');
                // if video started playing and it was paused according to known state
                } else if (pstate === 1 /*YT.PlayerState.PLAYING*/ && !state.playing) {
                    // broadcast state change
                    send({
                        type: 'play',
                        time: player.getCurrentTime()
                    });
                    state.playing = true;
                    console.log('play!');
                // if the video ended
                } else if (pstate === 0 /*YT.PlayerState.ENDED*/) {
                    // if we haven't reached the end of the playlist
                    if (state.current + 1 < state.playlist.length) {
                        // cue next video
                        cueIndex = state.current + 1;
                    } else {
                        // cue first video
                        cueIndex = 0;
                    }

                    state.current = cueIndex;
                    state.playing = true;

                    send({
                        type: 'cue',
                        current: cueIndex
                    });

                    player.loadVideoById(state.playlist[cueIndex].id);

                    updatePlaylist();
                }
            }
        };
        player.addEventListener('onStateChange', 'onStateChange');

        socket = new WebSocket('ws://' + API_SERVER, ['lunasync']);
        socket.onopen = function () {
            // set our stream (subscribe to events) and get info on it
            send({
                type: 'set_stream',
                id: id,
                control: control
            });
        };
        socket.onerror = function (err) {
            $('viewpage').innerHTML = 'Error communicating with server, lost connection:\n' + err;
        };
        socket.onmessage = function (event) {
            var msg, stream, elem;

            msg = JSON.parse(event.data);

            console.log(msg.type);

            switch (msg.type) {
                case 'stream_info':
                    stream = msg.stream;

                    // display stream title
                    $('title').value = stream.title;

                    // display stream viewing URL
                    $('view-link').value = SITE_URL + '/' + stream.id;

                    // update state
                    state.playing = stream.playing;
                    state.current = stream.current;
                    state.playlist = stream.playlist;

                    // display playlist
                    updatePlaylist();

                    // cue and play correct video
                    state.playing = stream.playing;
                    state.current = stream.current;
                    if (state.current !== null) {
                        if (state.playing) {
                            player.loadVideoById(state.playlist[state.current].id, stream.time);
                        } else {
                            player.cueVideoById(state.playlist[state.current].id, stream.time);
                        }
                    }

                    // if we have control of the stream
                    if (msg.control) {
                        haveControl = true;

                        // allow changing title
                        $('title').disabled = false;
                        $('title').onchange = function () {
                            send({
                                type: 'change_title',
                                title: $('title').value
                            });
                        };

                        // unhide control box
                        $('control').className = '';

                        // display stream control URL
                        $('control-link').value = SITE_URL + '/' + stream.id + '?control=' + control;

                        // enable stream controls
                        $('rm-button').disabled = false;
                        $('rm-button').onclick = function () {
                            var i, items = $('playlist').selectedOptions, oldCurrent, current;

                            oldCurrent, current = state.playlist[state.current];

                            // remove selected playlist items
                            for (i = 0; i < items.length; i++) {
                                // if currently playing video being deleted, switch to next
                                if (state.playlist[items[i].index] === current) {
                                    // loop around
                                    if (items[i].index + 1 < state.playlist.length) {
                                        current = state.playlist[items[i].index + 1];
                                    } else {
                                        current = state.playlist[0];
                                    }
                                }
                                state.playlist.splice(items[i].index, 1);
                            }

                            updatePlaylist();

                            // push update to server by overwriting playlist
                            // (this may create race conditions...)
                            send({
                                type: 'update_playlist',
                                playlist: state.playlist
                            });

                            if (current !== state.playlist[state.current]) {
                                if (current !== null && state.playlist.indexOf(current) !== -1) {
                                    current = state.playlist.indexOf(current);
                                    send({
                                        type: 'cue',
                                        current: current
                                    });
                                } else {
                                    send({
                                        type: 'cue',
                                        current: null
                                    });
                                }
                            }
                        };

                        $('play-button').disabled = false;
                        $('play-button').onclick = $('playlist').ondblclick = function () {
                            var cueIndex;

                            if ($('playlist').selectedOptions.length === 0) {
                                cueIndex = 0;
                            } else {
                                cueIndex = $('playlist').selectedOptions[0].index;
                            }

                            send({
                                type: 'cue',
                                current: cueIndex
                            });
                        };

                        $('add-url').disabled = false;
                        $('add-url').onkeypress = function (e) {
                            var videoID;

                            // enter
                            if (e.which === 13) {
                                e.preventDefault();
                                $('add-url').blur();
                                videoID = getVideoID($('add-url').value);
                                if (videoID !== false) {
                                    send({
                                        type: 'add_url',
                                        id: videoID
                                    });
                                    $('add-url').value = '';
                                } else {
                                    alert($('add-url').value + ' is not a valid youtube URL!');
                                }
                                return false;
                            }
                        };
                        $('playlist').disabled = false;
                    }

                    // make chat work
                    $('chatbox').placeholder = 'choose a nick (press enter)';
                    $('chatbox').onkeypress = function (e) {
                        // enter
                        if (e.which === 13) {
                            e.preventDefault();
                            if (chatNick === null) {
                                send({
                                    type: 'set_nick',
                                    nick: $('chatbox').value
                                });
                                $('chatbox').disabled = true;
                            } else {
                                send({
                                    type: 'msg',
                                    msg: $('chatbox').value
                                });
                            }
                            $('chatbox').value = '';
                            return false;
                        }
                    };
                break;
                case 'update_playlist':
                    state.playlist = msg.playlist;
                    updatePlaylist();
                break;
                case 'change_title':
                    $('title').value = msg.title;
                break;
                case 'cue':
                    state.playing = true;
                    state.current = msg.current;
                    if (state.current === null) {
                        player.cueVideoById('');
                    } else {
                        player.loadVideoById(state.playlist[state.current].id);
                    }
                    updatePlaylist();
                break;
                case 'play':
                    console.log('play');
                    state.playing = true;
                    player.seekTo(msg.time, true);
                    player.playVideo();
                break;
                case 'stop':
                    console.log('stop');
                    state.playing = false;
                    player.seekTo(msg.time, true);
                    player.pauseVideo();
                break;
                case 'join':
                    elem = document.createElement('div');
                    console.log(msg.nick + ' joined chat');
                    elem.appendChild(document.createTextNode(msg.nick + ' joined chat'));
                    $('chatlog').appendChild(elem);
                    $('chatlog').scrollTop = $('chatlog').scrollHeight;
                break;
                case 'leave':
                    elem = document.createElement('div');
                    console.log(msg.nick + ' left chat');
                    elem.appendChild(document.createTextNode(msg.nick + ' left chat'));
                    $('chatlog').appendChild(elem);
                    $('chatlog').scrollTop = $('chatlog').scrollHeight;
                break;
                case 'msg':
                    elem = document.createElement('div');
                    console.log(msg.nick + ': ' + msg.msg);
                    elem.appendChild(document.createTextNode(msg.nick + ': ' + msg.msg));
                    $('chatlog').appendChild(elem);
                    $('chatlog').scrollTop = $('chatlog').scrollHeight;
                break;
                case 'nick_taken':
                    alert('The nick "' + msg.nick + '" ids already taken - choose another one!');
                    $('chatbox').disabled = false;
                break;
                case 'nick_chosen':
                    $('chatbox').placeholder = 'say something (press enter)';
                    chatNick = msg.nick;
                    $('chatbox').disabled = false;
                break;
                case 'error':
                    if (msg.error === 'not_found') {
                        $('viewpage').innerHTML = 'Error: no such stream exists. Did you copy the URL correctly?';
                    } else {
                        $('viewpage').innerHTML = 'Error communicating with server, lost connection:\n' + msg.error;
                    }
                break;
            }
        };
    }

    // "404" page
    function init404() {
        // unhide 404 page
        $('page404').className = '';
    }

    function updatePlaylist() {
        var i, option;

        $('playlist').innerHTML = '';
        for (i = 0; i < state.playlist.length; i++) {
            option = document.createElement('option');
            if (state.current === i) {
                option.appendChild(document.createTextNode('▶ '));
                option.className = 'now-playing';
            }
            option.appendChild(document.createTextNode(state.playlist[i].title));
            option.index = i;
            $('playlist').appendChild(option);
        }
    }

    function getVideoID(url) {
        var pos, pos2;

        if (url.substr(0, 7) === 'http://') {
            url = url.substr(7);
        } else if (url.substr(0, 8) === 'https://') {
            url = url.substr(8);
        } else {
            return false;
        }
        if (url.substr(0, 4) === 'www.') {
            url = url.substr(4);
        }
        if (url.substr(0, 9) === 'youtu.be/') {
            return url.substr(9);
        } else if (url.substr(0, 18) === 'youtube.com/watch?') {
            url = url.substr(18);
            pos = url.indexOf('v=');
            if (pos !== -1) {
                url = url.substr(pos + 2);
                pos2 = url.indexOf('&');
                if (pos2 === -1) {
                    return url;
                } else {
                    return url.substr(0, pos2);
                }
            } else {
                return false;
            }
        }
    }
}());
<|endoftext|>


<commit_before>import asyncCommand from '../lib/async-command';
import fs from 'fs.promised';
import copy from 'recursive-copy';
import mkdirp from 'mkdirp';
import ora from 'ora';
import promisify from 'es6-promisify';
import spawn from 'cross-spawn-promise';
import path from 'path';

const TEMPLATES = {
	default: 'examples/root',
	empty: 'examples/empty',
	full: 'examples/full',
	simple: 'examples/simple'
};

export default asyncCommand({
	command: 'create <name> [dest]',

	desc: 'Create a new application.',

	builder: {
		name: {
			description: 'directory and package name for the new app'
		},
		dest: {
			description: 'Directory to create the app within',
			defaultDescription: '<name>'
		},
		type: {
			description: 'A project template to start from',
			choices: [
				'default',
				'full',
				'simple',
				'empty'
			],
			default: 'default'
		},
		less: {
			description: 'Pre-install LESS support',
			type: 'boolean',
			default: false
		},
		sass: {
			description: 'Pre-install SASS/SCSS support',
			type: 'boolean',
			default: false
		}
	},

	async handler(argv) {
		let template = TEMPLATES[argv.type];

		if (!template) {
			throw Error(`Unknown app template "${argv.type}".`);
		}

		let target = path.resolve(process.cwd(), argv.dest || argv.name);

		let exists = false;
		try {
			exists = (await fs.stat(target)).isDirectory();
		}
		catch (err) {}

		if (exists) {
			throw Error('Directory already exists.');
		}

		let spinner = ora({
			text: 'Creating project',
			color: 'magenta'
		}).start();

		await promisify(mkdirp)(target);

		await copy(
			path.resolve(__dirname, '../..', template),
			target,
			{ filter: ['**/*', '!build'] }
		);

		spinner.text = 'Initializing project';

		await npm(target, ['init', '-y']);

		let pkg = JSON.parse(await fs.readFile(path.resolve(target, 'package.json')));

		pkg.scripts = {
			start: 'if-env NODE_ENV=production && npm run -s serve || npm run -s dev',
			build: 'preact build',
			serve: 'preact build && preact serve',
			dev: 'preact watch',
			test: 'eslint src && preact test'
		};

		pkg.eslintConfig = {
			extends: 'eslint-config-synacor'
		};

		await fs.writeFile(path.resolve(target, 'package.json'), JSON.stringify(pkg, null, 2));

		spinner.text = 'Installing dev dependencies';

		await npm(target, [
			'install', '--save-dev',
			'preact-cli',
			'if-env',
			'eslint'
		]);

		spinner.text = 'Installing dependencies';

		await npm(target, [
			'install', '--save',
			'preact',
			'preact-compat',
			'preact-router'
		]);

		spinner.succeed('Done!\n');

		return `
			To get started, cd into the new directory:
			  \u001b[32mcd ${path.relative(process.cwd(), target)}\u001b[39m

			To start a development live-reload server:
			  \u001b[32mnpm start\u001b[39m

			To create a production build in build/:
			  \u001b[32mnpm run build\u001b[39m

			To start a production HTTP/2 server:
			  \u001b[32mnpm run serve\u001b[39m
		`.trim().replace(/^\t+/gm, '') + '\n';
	}
})

const npm = (cwd, args) => spawn('npm', args, { cwd, stdio: 'ignore' });
<commit_msg>Install eslint-config-synacor as devDependency when running create<commit_after>import asyncCommand from '../lib/async-command';
import fs from 'fs.promised';
import copy from 'recursive-copy';
import mkdirp from 'mkdirp';
import ora from 'ora';
import promisify from 'es6-promisify';
import spawn from 'cross-spawn-promise';
import path from 'path';

const TEMPLATES = {
	default: 'examples/root',
	empty: 'examples/empty',
	full: 'examples/full',
	simple: 'examples/simple'
};

export default asyncCommand({
	command: 'create <name> [dest]',

	desc: 'Create a new application.',

	builder: {
		name: {
			description: 'directory and package name for the new app'
		},
		dest: {
			description: 'Directory to create the app within',
			defaultDescription: '<name>'
		},
		type: {
			description: 'A project template to start from',
			choices: [
				'default',
				'full',
				'simple',
				'empty'
			],
			default: 'default'
		},
		less: {
			description: 'Pre-install LESS support',
			type: 'boolean',
			default: false
		},
		sass: {
			description: 'Pre-install SASS/SCSS support',
			type: 'boolean',
			default: false
		}
	},

	async handler(argv) {
		let template = TEMPLATES[argv.type];

		if (!template) {
			throw Error(`Unknown app template "${argv.type}".`);
		}

		let target = path.resolve(process.cwd(), argv.dest || argv.name);

		let exists = false;
		try {
			exists = (await fs.stat(target)).isDirectory();
		}
		catch (err) {}

		if (exists) {
			throw Error('Directory already exists.');
		}

		let spinner = ora({
			text: 'Creating project',
			color: 'magenta'
		}).start();

		await promisify(mkdirp)(target);

		await copy(
			path.resolve(__dirname, '../..', template),
			target,
			{ filter: ['**/*', '!build'] }
		);

		spinner.text = 'Initializing project';

		await npm(target, ['init', '-y']);

		let pkg = JSON.parse(await fs.readFile(path.resolve(target, 'package.json')));

		pkg.scripts = {
			start: 'if-env NODE_ENV=production && npm run -s serve || npm run -s dev',
			build: 'preact build',
			serve: 'preact build && preact serve',
			dev: 'preact watch',
			test: 'eslint src && preact test'
		};

		pkg.eslintConfig = {
			extends: 'eslint-config-synacor'
		};

		await fs.writeFile(path.resolve(target, 'package.json'), JSON.stringify(pkg, null, 2));

		spinner.text = 'Installing dev dependencies';

		await npm(target, [
			'install', '--save-dev',
			'preact-cli',
			'if-env',
			'eslint',
			'eslint-config-synacor'
		]);

		spinner.text = 'Installing dependencies';

		await npm(target, [
			'install', '--save',
			'preact',
			'preact-compat',
			'preact-router'
		]);

		spinner.succeed('Done!\n');

		return `
			To get started, cd into the new directory:
			  \u001b[32mcd ${path.relative(process.cwd(), target)}\u001b[39m

			To start a development live-reload server:
			  \u001b[32mnpm start\u001b[39m

			To create a production build in build/:
			  \u001b[32mnpm run build\u001b[39m

			To start a production HTTP/2 server:
			  \u001b[32mnpm run serve\u001b[39m
		`.trim().replace(/^\t+/gm, '') + '\n';
	}
})

const npm = (cwd, args) => spawn('npm', args, { cwd, stdio: 'ignore' });
<|endoftext|>


<commit_before>/**
 * Extends dojo/has with a few additional tests, and makes sure dojo/sniff is loaded.
 */

define([ 'dojo/has', 'dojo/sniff' ],
       function( has ) {

           // does the browser support typed arrays?
           has.add( 'typed-arrays', function() {
                        try {
                            var a = new Uint8Array(1);
                            return !!a;
                        } catch(e) {};
                        return false;
                    });

           // does it support canvas?
           has.add( 'canvas', function() {
                        try {
                            return !! document.createElement('canvas').getContext('2d');
                        } catch(e) {}
                        return false;
                    });

           // some browsers don't do a very good job with
           // percentage-based and fractional-pixel HTML coordinates
           // and sizes
           has.add( 'inaccurate-html-layout', function() {
               return has('safari') || has('ie') < 9;
           }, true );

           has.add( 'save-generated-files', function() {
                        var canSave = false;
                        try {
                            canSave = Blob && !( has('ie') < 10 ) && ! has('safari');
                        } catch(e) {}
                        return canSave;
           },true);

           // similar to the inaccurate-html-layout problem, but specifically related to width being 100%
           // rounding on canvas features at the time being
           has.add( 'inaccurate-html-width', function() {
               return has('safari')||has('chrome');
           }, true);

           return has;
       }
);
<commit_msg>enable generated file saving in safari 11 and above<commit_after>/**
 * Extends dojo/has with a few additional tests, and makes sure dojo/sniff is loaded.
 */

define([ 'dojo/has', 'dojo/sniff' ],
       function( has ) {

           // does the browser support typed arrays?
           has.add( 'typed-arrays', function() {
                        try {
                            var a = new Uint8Array(1);
                            return !!a;
                        } catch(e) {};
                        return false;
                    });

           // does it support canvas?
           has.add( 'canvas', function() {
                        try {
                            return !! document.createElement('canvas').getContext('2d');
                        } catch(e) {}
                        return false;
                    });

           // some browsers don't do a very good job with
           // percentage-based and fractional-pixel HTML coordinates
           // and sizes
           has.add( 'inaccurate-html-layout', function() {
               return has('safari') || has('ie') < 9;
           }, true );

           has.add( 'save-generated-files', function() {
                        var canSave = false;
                        try {
                            canSave = Blob && !( has('ie') < 10 ) && !( has('safari') < 11 )
                        } catch(e) {}
                        return canSave;
           },true);

           // similar to the inaccurate-html-layout problem, but specifically related to width being 100%
           // rounding on canvas features at the time being
           has.add( 'inaccurate-html-width', function() {
               return has('safari')||has('chrome');
           }, true);

           return has;
       }
);
<|endoftext|>


<commit_before>import {isNode, isBrowser} from './environment.js';
import getPromise from './getPromise.js';

export default function getLoadControlClass(log, onResourceLoadComplete, onLoadingComplete) {
    class LoadControl {
        constructor(resourceName, status, resolution, errorInfo) {
            const promise = getPromise();
            if (!status) {
                status = 'started';
            } else if (resolution !== undefined) {
                promise.resolve(resolution);
            }
            Object.defineProperties(this, {
                order: { value: ++LoadControl.counter },
                resourceName: { value: resourceName },
                loadedName: { value: resolution !== undefined ? resourceName : false, writable: true },
                status: { value: status, writable: true },
                promise: { value: promise },
                errorInfo: { value: errorInfo || false, writable: true },
            });

            // make sure we have a live loading promise so that check done can work...
            if (!LoadControl.loadingPromise || LoadControl.loadingPromise.resolved) {
                LoadControl.loadingError = undefined;
                LoadControl.loadingPromise = getPromise();
            }
            promise.then(LoadControl.checkDone);
        }

        get isRequested() {
            return !!this.loadedName;
        }

        resolve(resolution, errorInfo) {
            if (this.promise.resolved) {
                // eslint-disable-next-line no-console
                console.warn('unexpected');
                return;
            }
            this.status = resolution;
            if (resolution === false) {
                const logEntry = [this.resourceName, 'failed to load'];
                if (this.loadedName !== this.resourceName) {
                    logEntry.push(`(loaded as ${this.loadedName})`);
                }

                if (errorInfo) {
                    this.errorInfo = errorInfo;
                    logEntry.push(errorInfo);
                }
                log(...logEntry);
            } else if (resolution !== true) {
                this.loadedValue = resolution;
                this.status = true;
            }
            onResourceLoadComplete(this);
            this.promise.resolve(resolution);
        }

        static isLegalResourceName(name, type) {
            if (typeof name !== 'string') {
                log(`a resource name was given to load a ${type} resource that is not a string`);
                return false;
            }
            if (type === 'AMD' && /^\./.test(name)) {
                log(`illegal to AMD require a relative module name (${name})`);
                return false;
            }
            return true;
        }

        static getControl(resourceName, type) {
            // false if (resourceName, type) is illegal; otherwise the control record for resourceName
            if (!LoadControl.isLegalResourceName(resourceName, type)) {
                return false;
            }
            if (LoadControl.injections.has(resourceName)) {
                return LoadControl.injections.get(resourceName);
            } else {
                let control;
                if (type === 'CSS' && isNode) {
                    control = new LoadControl(resourceName, 'ignored', true, 'CSS resources are ignored when running on node');
                } else if (/.+\.es6\.js$/.test(resourceName) && isNode) {
                    // .js6 types indicate the resource may include import or export directives
                    // what about when node does support import/export ?
                    control = new LoadControl(resourceName, 'ignored', true, 'js6 resources are ignored when running on node');
                } else {
                    control = new LoadControl(resourceName);
                }
                LoadControl.injections.set(resourceName, control);
                return control;
            }
        }

        static checkDone() {
            let done = true;
            let error = false;
            // eslint-disable-next-line no-restricted-syntax
            for (const control of LoadControl.injections.values()) {
                if (!done || !control.promise.resolved) {
                    done = false;
                }
                error = error || control.status === false;
            }
            if (done) {
                onLoadingComplete();
                LoadControl.loadingError = error;
                LoadControl.loadingPromise.resolve(error);
            }
        }

        static load(resourceName, type, proc) {
            const control = LoadControl.getControl(resourceName, type);
            if (!control) {
                return false;
            }
            if (control.isRequested) {
                return control.promise.proxy;
            }
            let name = resourceName;
            if (/^\./.test(name)) {
                // if resourceName is relative, then it"s relative to the project directory
                // TODO: make this an option?
                name = LoadControl.injectRelativePrefix + name;
            }
            proc(control, name);
            return control.promise.proxy;
        }

        static browserInject(control, tag, props) {
            let node;
            const handler = e => {
                control.loadedName = node.src;
                if (e.type === 'load') {
                    // TODO: make sure this kind of failure is detected on node also
                    control.resolve(!LoadControl.windowErrors[node.src]);
                } else {
                    control.resolve(false, e);
                }
            };
            try {
                node = document.createElement(tag);
                node.addEventListener('load', handler, false);
                node.addEventListener('error', handler, false);
                Object.keys(props).forEach(p => (node[p] = props[p]));
                document.getElementsByTagName('script')[0].parentNode.appendChild(node);
            } catch (e) {
                control.resolve(false, e);
            }
        }

        static injectScript(resourceName, type) {
            return LoadControl.load(resourceName, 'script', (control, src) => {
                if (type === undefined) {
                    type = /\.es5\.js$/.test(src) ? '' : 'module';
                }
                LoadControl.browserInject(control, 'script', { src: control.loadedName = src, type: type || '' });
            });
        }

        static injectCss(resourceName) {
            return LoadControl.load(resourceName, 'CSS', (control, href) => {
                LoadControl.browserInject(control, 'link', {
                    href: control.loadedName = href,
                    type: 'text/css',
                    rel: 'stylesheet'
                });
            });
        }

        static loadNodeModule(moduleName) {
            return LoadControl.load(moduleName, 'node module', (control, fileName) => {
                try {
                    // eslint-disable-next-line global-require,import/no-dynamic-require
                    require((control.loadedName = fileName));
                    control.resolve(true);
                } catch (e) {
                    control.resolve(false, e);
                }
            });
        }

        static loadAmdModule(moduleName) {
            return LoadControl.load(moduleName, 'script', (control, module) => {
                try {
                    // eslint-disable-next-line global-require,import/no-dynamic-require
                    require([module], () => {
                        control.resolve(true);
                    });
                } catch (e) {
                    control.resolve(false, e);
                }
            });
        }
    }

    LoadControl.counter = 0;
    LoadControl.injections = new Map();
    LoadControl.loadingPromise = getPromise();
    LoadControl.loadingPromise.resolve(true);
    LoadControl.loadingError = false;
    LoadControl.injectRelativePrefix = '';
    LoadControl.windowErrors = {};

    if (isBrowser) {
        window.addEventListener('error', e => {
            if (e.filename) {
                LoadControl.windowErrors[e.filename] = true;
            }
        });
    }

    return LoadControl;
}
<commit_msg>improve LoadControl.injectRelativePrefix<commit_after>import {isNode, isBrowser} from './environment.js';
import getPromise from './getPromise.js';

export default function getLoadControlClass(log, onResourceLoadComplete, onLoadingComplete) {
    class LoadControl {
        constructor(resourceName, status, resolution, errorInfo) {
            const promise = getPromise();
            if (!status) {
                status = 'started';
            } else if (resolution !== undefined) {
                promise.resolve(resolution);
            }
            Object.defineProperties(this, {
                order: { value: ++LoadControl.counter },
                resourceName: { value: resourceName },
                loadedName: { value: resolution !== undefined ? resourceName : false, writable: true },
                status: { value: status, writable: true },
                promise: { value: promise },
                errorInfo: { value: errorInfo || false, writable: true },
            });

            // make sure we have a live loading promise so that check done can work...
            if (!LoadControl.loadingPromise || LoadControl.loadingPromise.resolved) {
                LoadControl.loadingError = undefined;
                LoadControl.loadingPromise = getPromise();
            }
            promise.then(LoadControl.checkDone);
        }

        get isRequested() {
            return !!this.loadedName;
        }

        get injectRelativePrefix() {
            return LoadControl.injectRelativePrefix;
        }

        set injectRelativePrefix(injectRelativePrefix) {
            if (injectRelativePrefix !== LoadControl.injectRelativePrefix) {
                LoadControl.injectRelativePrefix = injectRelativePrefix;
                log(`root directory for relative injection paths: '${LoadControl.injectRelativePrefix}'`);
            }
        }

        resolve(resolution, errorInfo) {
            if (this.promise.resolved) {
                // eslint-disable-next-line no-console
                console.warn('unexpected');
                return;
            }
            this.status = resolution;
            if (resolution === false) {
                const logEntry = [this.resourceName, 'failed to load'];
                if (this.loadedName !== this.resourceName) {
                    logEntry.push(`(loaded as ${this.loadedName})`);
                }

                if (errorInfo) {
                    this.errorInfo = errorInfo;
                    logEntry.push(errorInfo);
                }
                log(...logEntry);
            } else if (resolution !== true) {
                this.loadedValue = resolution;
                this.status = true;
            }
            onResourceLoadComplete(this);
            this.promise.resolve(resolution);
        }

        static isLegalResourceName(name, type) {
            if (typeof name !== 'string') {
                log(`a resource name was given to load a ${type} resource that is not a string`);
                return false;
            }
            if (type === 'AMD' && /^\./.test(name)) {
                log(`illegal to AMD require a relative module name (${name})`);
                return false;
            }
            return true;
        }

        static getControl(resourceName, type) {
            // false if (resourceName, type) is illegal; otherwise the control record for resourceName
            if (!LoadControl.isLegalResourceName(resourceName, type)) {
                return false;
            }
            if (LoadControl.injections.has(resourceName)) {
                return LoadControl.injections.get(resourceName);
            } else {
                let control;
                if (type === 'CSS' && isNode) {
                    control = new LoadControl(resourceName, 'ignored', true, 'CSS resources are ignored when running on node');
                } else if (/.+\.es6\.js$/.test(resourceName) && isNode) {
                    // .js6 types indicate the resource may include import or export directives
                    // what about when node does support import/export ?
                    control = new LoadControl(resourceName, 'ignored', true, 'js6 resources are ignored when running on node');
                } else {
                    control = new LoadControl(resourceName);
                }
                LoadControl.injections.set(resourceName, control);
                return control;
            }
        }

        static checkDone() {
            let done = true;
            let error = false;
            // eslint-disable-next-line no-restricted-syntax
            for (const control of LoadControl.injections.values()) {
                if (!done || !control.promise.resolved) {
                    done = false;
                }
                error = error || control.status === false;
            }
            if (done) {
                onLoadingComplete();
                LoadControl.loadingError = error;
                LoadControl.loadingPromise.resolve(error);
            }
        }

        static load(resourceName, type, proc) {
            const control = LoadControl.getControl(resourceName, type);
            if (!control) {
                return false;
            }
            if (control.isRequested) {
                return control.promise.proxy;
            }
            let name = resourceName;
            if (/^\./.test(name)) {
                // if resourceName is relative, then it"s relative to the project directory
                // TODO: make this an option?
                name = LoadControl.injectRelativePrefix + name;
            }
            proc(control, name);
            return control.promise.proxy;
        }

        static browserInject(control, tag, props) {
            let node;
            const handler = e => {
                control.loadedName = node.src;
                if (e.type === 'load') {
                    // TODO: make sure this kind of failure is detected on node also
                    control.resolve(!LoadControl.windowErrors[node.src]);
                } else {
                    control.resolve(false, e);
                }
            };
            try {
                node = document.createElement(tag);
                node.addEventListener('load', handler, false);
                node.addEventListener('error', handler, false);
                Object.keys(props).forEach(p => (node[p] = props[p]));
                document.getElementsByTagName('script')[0].parentNode.appendChild(node);
            } catch (e) {
                control.resolve(false, e);
            }
        }

        static injectScript(resourceName, type) {
            return LoadControl.load(resourceName, 'script', (control, src) => {
                if (type === undefined) {
                    type = /\.es5\.js$/.test(src) ? '' : 'module';
                }
                LoadControl.browserInject(control, 'script', { src: control.loadedName = src, type: type || '' });
            });
        }

        static injectCss(resourceName) {
            return LoadControl.load(resourceName, 'CSS', (control, href) => {
                LoadControl.browserInject(control, 'link', {
                    href: control.loadedName = href,
                    type: 'text/css',
                    rel: 'stylesheet'
                });
            });
        }

        static loadNodeModule(moduleName) {
            return LoadControl.load(moduleName, 'node module', (control, fileName) => {
                try {
                    // eslint-disable-next-line global-require,import/no-dynamic-require
                    require((control.loadedName = fileName));
                    control.resolve(true);
                } catch (e) {
                    control.resolve(false, e);
                }
            });
        }

        static loadAmdModule(moduleName) {
            return LoadControl.load(moduleName, 'script', (control, module) => {
                try {
                    // eslint-disable-next-line global-require,import/no-dynamic-require
                    require([module], () => {
                        control.resolve(true);
                    });
                } catch (e) {
                    control.resolve(false, e);
                }
            });
        }
    }

    LoadControl.counter = 0;
    LoadControl.injections = new Map();
    LoadControl.loadingPromise = getPromise();
    LoadControl.loadingPromise.resolve(true);
    LoadControl.loadingError = false;
    LoadControl.injectRelativePrefix = '';
    LoadControl.windowErrors = {};

    if (isBrowser) {
        window.addEventListener('error', e => {
            if (e.filename) {
                LoadControl.windowErrors[e.filename] = true;
            }
        });
    }

    return LoadControl;
}
<|endoftext|>


<commit_before>import asyncCommand from '../lib/async-command';
import fs from 'fs.promised';
import copy from 'recursive-copy';
import mkdirp from 'mkdirp';
import ora from 'ora';
import promisify from 'es6-promisify';
import spawn from 'cross-spawn-promise';
import path from 'path';

const TEMPLATES = {
	default: 'examples/root',
	empty: 'examples/empty',
	full: 'examples/full',
	simple: 'examples/simple'
};

export default asyncCommand({
	command: 'create <name> [dest]',

	desc: 'Create a new application.',

	builder: {
		name: {
			description: 'directory and package name for the new app'
		},
		dest: {
			description: 'Directory to create the app within',
			defaultDescription: '<name>'
		},
		type: {
			description: 'A project template to start from',
			choices: [
				'default',
				'full',
				'simple',
				'empty'
			],
			default: 'default'
		},
		less: {
			description: 'Pre-install LESS support',
			type: 'boolean',
			default: false
		},
		sass: {
			description: 'Pre-install SASS/SCSS support',
			type: 'boolean',
			default: false
		}
	},

	async handler(argv) {
		let template = TEMPLATES[argv.type];

		if (!template) {
			throw Error(`Unknown app template "${argv.type}".`);
		}

		let target = path.resolve(process.cwd(), argv.dest || argv.name);

		let exists = false;
		try {
			exists = (await fs.stat(target)).isDirectory();
		}
		catch (err) {}

		if (exists) {
			throw Error('Directory already exists.');
		}

		let spinner = ora({
			text: 'Creating project',
			color: 'magenta'
		}).start();

		await promisify(mkdirp)(target);

		await copy(
			path.resolve(__dirname, '../..', template),
			target,
			{ filter: ['**/*', '!build'] }
		);

		spinner.text = 'Initializing project';

		await npm(target, ['init', '-y']);

		let pkg = JSON.parse(await fs.readFile(path.resolve(target, 'package.json')));

		pkg.scripts = {
			start: 'if-env NODE_ENV=production && npm run -s serve || npm run -s dev',
			build: 'preact build',
			serve: 'preact build && preact serve',
			dev: 'preact watch',
			test: 'eslint src && preact test'
		};

		pkg.eslintConfig = {
			extends: 'eslint-config-synacor'
		};

		await fs.writeFile(path.resolve(target, 'package.json'), JSON.stringify(pkg, null, 2));

		spinner.text = 'Installing dev dependencies';

		await npm(target, [
			'install', '--save-dev',
			'preact-cli',
			'if-env',
			'eslint',
			'eslint-config-synacor',

			// install sass setup if --sass
			...(argv.sass ? [
				'node-sass',
				'sass-loader'
			] : []),

			// install less setup if --less
			...(argv.less ? [
				'less',
				'less-loader'
			] : [])
		].filter(Boolean));

		spinner.text = 'Installing dependencies';

		await npm(target, [
			'install', '--save',
			'preact',
			'preact-compat',
			'preact-router'
		]);

		spinner.succeed('Done!\n');

		return `
			To get started, cd into the new directory:
			  \u001b[32mcd ${path.relative(process.cwd(), target)}\u001b[39m

			To start a development live-reload server:
			  \u001b[32mnpm start\u001b[39m

			To create a production build in build/:
			  \u001b[32mnpm run build\u001b[39m

			To start a production HTTP/2 server:
			  \u001b[32mnpm run serve\u001b[39m
		`.trim().replace(/^\t+/gm, '') + '\n';
	}
});

const npm = (cwd, args) => spawn('npm', args, { cwd, stdio: 'ignore' });
<commit_msg>Preserve any scripts in a template's package.json.<commit_after>import asyncCommand from '../lib/async-command';
import fs from 'fs.promised';
import copy from 'recursive-copy';
import mkdirp from 'mkdirp';
import ora from 'ora';
import promisify from 'es6-promisify';
import spawn from 'cross-spawn-promise';
import path from 'path';

const TEMPLATES = {
	default: 'examples/root',
	empty: 'examples/empty',
	full: 'examples/full',
	simple: 'examples/simple'
};

export default asyncCommand({
	command: 'create <name> [dest]',

	desc: 'Create a new application.',

	builder: {
		name: {
			description: 'directory and package name for the new app'
		},
		dest: {
			description: 'Directory to create the app within',
			defaultDescription: '<name>'
		},
		type: {
			description: 'A project template to start from',
			choices: [
				'default',
				'full',
				'simple',
				'empty'
			],
			default: 'default'
		},
		less: {
			description: 'Pre-install LESS support',
			type: 'boolean',
			default: false
		},
		sass: {
			description: 'Pre-install SASS/SCSS support',
			type: 'boolean',
			default: false
		}
	},

	async handler(argv) {
		let template = TEMPLATES[argv.type];

		if (!template) {
			throw Error(`Unknown app template "${argv.type}".`);
		}

		let target = path.resolve(process.cwd(), argv.dest || argv.name);

		let exists = false;
		try {
			exists = (await fs.stat(target)).isDirectory();
		}
		catch (err) {}

		if (exists) {
			throw Error('Directory already exists.');
		}

		let spinner = ora({
			text: 'Creating project',
			color: 'magenta'
		}).start();

		await promisify(mkdirp)(target);

		await copy(
			path.resolve(__dirname, '../..', template),
			target,
			{ filter: ['**/*', '!build'] }
		);

		spinner.text = 'Initializing project';

		await npm(target, ['init', '-y']);

		let pkg = JSON.parse(await fs.readFile(path.resolve(target, 'package.json')));

		pkg.scripts = {
			...(pkg.scripts || {}),
			start: 'if-env NODE_ENV=production && npm run -s serve || npm run -s dev',
			build: 'preact build',
			serve: 'preact build && preact serve',
			dev: 'preact watch',
			test: 'eslint src && preact test'
		};

		pkg.eslintConfig = {
			extends: 'eslint-config-synacor'
		};

		await fs.writeFile(path.resolve(target, 'package.json'), JSON.stringify(pkg, null, 2));

		spinner.text = 'Installing dev dependencies';

		await npm(target, [
			'install', '--save-dev',
			'preact-cli',
			'if-env',
			'eslint',
			'eslint-config-synacor',

			// install sass setup if --sass
			...(argv.sass ? [
				'node-sass',
				'sass-loader'
			] : []),

			// install less setup if --less
			...(argv.less ? [
				'less',
				'less-loader'
			] : [])
		].filter(Boolean));

		spinner.text = 'Installing dependencies';

		await npm(target, [
			'install', '--save',
			'preact',
			'preact-compat',
			'preact-router'
		]);

		spinner.succeed('Done!\n');

		return `
			To get started, cd into the new directory:
			  \u001b[32mcd ${path.relative(process.cwd(), target)}\u001b[39m

			To start a development live-reload server:
			  \u001b[32mnpm start\u001b[39m

			To create a production build in build/:
			  \u001b[32mnpm run build\u001b[39m

			To start a production HTTP/2 server:
			  \u001b[32mnpm run serve\u001b[39m
		`.trim().replace(/^\t+/gm, '') + '\n';
	}
});

const npm = (cwd, args) => spawn('npm', args, { cwd, stdio: 'ignore' });
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';

import lazyLoadComponent from 'common/lazyLoadComponent';
import { hasPremium } from 'Interface/selectors/user';
import Ad from 'Interface/common/Ad';
import makeNewsUrl from 'Interface/News/makeUrl';
import { title as AboutArticleTitle } from 'Interface/News/Articles/2017-01-31-About';
import { title as UnlistedLogsTitle } from 'Interface/News/Articles/2017-01-31-UnlistedLogs';

import ReportSelecter from './ReportSelecter';
// import ServiceStatus from './ServiceStatus';

import './Header.css';

const CharacterSearch = lazyLoadComponent(() => import(/* webpackChunkName: 'CharacterSearch', webpackPrefetch: true */ 'Interface/Character/Search').then(exports => exports.default));

class Header extends React.PureComponent {
  static propTypes = {
    showReportSelecter: PropTypes.bool.isRequired,
    premium: PropTypes.bool,
  };
  static defaultProps = {
    premium: false,
  };

  constructor(props) {
    super(props);
    this.state = {
      reportActive: true,
    };
  }

  render() {
    const { showReportSelecter, premium } = this.props;

    return (

WoWAnalyzer

                Analyze your raid logs to get personal suggestions and metrics to improve your performance. Just enter a Warcraft Logs report:

              {showReportSelecter && (

                     this.setState({ reportActive: true })} className={this.state.reportActive ? 'selected' : ''}>Report
                     this.setState({ reportActive: false })} className={this.state.reportActive ? '' : 'selected'}>Character

                  {this.state.reportActive ? (
                    <ReportSelecter />
                  ) : (
                    <CharacterSearch />
                  )}

              )}

              {/*{process.env.NODE_ENV !== 'test' && <ServiceStatus style={{ marginBottom: 5 }} />}*/}

                <Link to={makeNewsUrl(AboutArticleTitle)}>About WoWAnalyzer</Link>
                {' '}| <Link to={makeNewsUrl(UnlistedLogsTitle)}>About unlisted logs</Link>
                {' '}| Premium

            {!premium && (

                <Ad format="mediumrectangle" />

            )}

    );
  }
}

const mapStateToProps = state => ({
  premium: hasPremium(state),
});

export default connect(
  mapStateToProps
)(Header);
<commit_msg>Premium link is a React link (avoid reloading the app bundle)<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';

import lazyLoadComponent from 'common/lazyLoadComponent';
import { hasPremium } from 'Interface/selectors/user';
import Ad from 'Interface/common/Ad';
import makeNewsUrl from 'Interface/News/makeUrl';
import { title as AboutArticleTitle } from 'Interface/News/Articles/2017-01-31-About';
import { title as UnlistedLogsTitle } from 'Interface/News/Articles/2017-01-31-UnlistedLogs';

import ReportSelecter from './ReportSelecter';
// import ServiceStatus from './ServiceStatus';

import './Header.css';

const CharacterSearch = lazyLoadComponent(() => import(/* webpackChunkName: 'CharacterSearch', webpackPrefetch: true */ 'Interface/Character/Search').then(exports => exports.default));

class Header extends React.PureComponent {
  static propTypes = {
    showReportSelecter: PropTypes.bool.isRequired,
    premium: PropTypes.bool,
  };
  static defaultProps = {
    premium: false,
  };

  constructor(props) {
    super(props);
    this.state = {
      reportActive: true,
    };
  }

  render() {
    const { showReportSelecter, premium } = this.props;

    return (

WoWAnalyzer

                Analyze your raid logs to get personal suggestions and metrics to improve your performance. Just enter a Warcraft Logs report:

              {showReportSelecter && (

                     this.setState({ reportActive: true })} className={this.state.reportActive ? 'selected' : ''}>Report
                     this.setState({ reportActive: false })} className={this.state.reportActive ? '' : 'selected'}>Character

                  {this.state.reportActive ? (
                    <ReportSelecter />
                  ) : (
                    <CharacterSearch />
                  )}

              )}

              {/*{process.env.NODE_ENV !== 'test' && <ServiceStatus style={{ marginBottom: 5 }} />}*/}

                <Link to={makeNewsUrl(AboutArticleTitle)}>About WoWAnalyzer</Link>
                {' '}| <Link to={makeNewsUrl(UnlistedLogsTitle)}>About unlisted logs</Link>
                {' '}| <Link to="/premium">Premium</Link>

            {!premium && (

                <Ad format="mediumrectangle" />

            )}

    );
  }
}

const mapStateToProps = state => ({
  premium: hasPremium(state),
});

export default connect(
  mapStateToProps
)(Header);
<|endoftext|>


<commit_before>module.exports = {
  root: true,
  parser: 'babel-eslint',
  env: {
    browser: true,
    commonjs: true,
    es6: true,
    node: true
  },
  extends: [
    require.resolve('eslint-config-airbnb')
  ],
  parserOptions: {
    ecmaVersion: 2016,
    sourceType: 'module',
    ecmaFeatures: {
      jsx: true,
      generators: true,
      experimentalObjectRestSpread: true,
    },
  },
  plugins: ['flowtype'],
  rules: {
    'generator-star-spacing': 'off',
    'import/*': 'off',
    'react/require-extension': 'off',
    'flowtype/define-flow-type': 'warn',
    'flowtype/require-valid-file-annotation': 'warn',
    'flowtype/use-flow-type': 'warn'
  },
  "settings": {
    "flowtype": {
      "onlyFilesWithFlowAnnotation": false
    }
  }
};
<commit_msg>disable forcing jsx extension in eslint<commit_after>module.exports = {
  root: true,
  parser: 'babel-eslint',
  env: {
    browser: true,
    commonjs: true,
    es6: true,
    node: true
  },
  extends: [
    require.resolve('eslint-config-airbnb')
  ],
  parserOptions: {
    ecmaVersion: 2016,
    sourceType: 'module',
    ecmaFeatures: {
      jsx: true,
      generators: true,
      experimentalObjectRestSpread: true,
    },
  },
  plugins: ['flowtype'],
  rules: {
    'generator-star-spacing': 'off',
    'import/*': 'off',
    'react/jsx-filename-extension': 'off',
    'react/require-extension': 'off',
    'flowtype/define-flow-type': 'warn',
    'flowtype/require-valid-file-annotation': 'warn',
    'flowtype/use-flow-type': 'warn'
  },
  "settings": {
    "flowtype": {
      "onlyFilesWithFlowAnnotation": false
    }
  }
};
<|endoftext|>