<commit_before>// ==UserScript==
// @name         Stack Overflow Extras (SOX)
// @namespace    https://github.com/soscripted/sox
// @version      DEV
// @description  Adds a bunch of optional features to sites in the Stack Overflow Network.
// @contributor  ᴉʞuǝ (stackoverflow.com/users/1454538/)
// @contributor  ᔕᖺᘎᕊ (stackexchange.com/users/4337810/)
// @updateURL    https://rawgit.com/soscripted/sox/master/sox.user.js
// @match        *://*.stackoverflow.com/*
// @match        *://*.stackexchange.com/*
// @match        *://*.superuser.com/*
// @match        *://*.serverfault.com/*
// @match        *://*.askubuntu.com/*
// @match        *://*.stackapps.com/*
// @match        *://*.mathoverflow.net/*
// @require      https://code.jquery.com/jquery-2.1.4.min.js
// @require      https://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js
// @require      https://cdn.rawgit.com/timdown/rangyinputs/master/rangyinputs-jquery-src.js
// @require      https://cdn.rawgit.com/jeresig/jquery.hotkeys/master/jquery.hotkeys.js
// @require      https://cdn.rawgit.com/camagu/jquery-feeds/master/jquery.feeds.js
// @require      https://rawgit.com/soscripted/sox/master/sox.helpers.js
// @require      https://rawgit.com/soscripted/sox/master/sox.features.js
// @resource     settingsDialog https://rawgit.com/soscripted/sox/master/sox-dialog.html
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_getResourceText
// ==/UserScript==
/*jshint multistr: true */
(function(sox, $, undefined) {
    const SOX = "Stack Overflow Extras";
    const SOX_SETTINGS = "SOXSETTINGS";

    var $settingsDialog = $(GM_getResourceText("settingsDialog")),
        $soxSettingsDialog = $settingsDialog.find("#sox-settings-dialog"),
        $soxSettingsDialogFeatures = $settingsDialog.find("#sox-settings-dialog-features"),
        $soxSettingsSave = $settingsDialog.find("#sox-settings-dialog-save"),
        $soxSettingsReset = $settingsDialog.find("#sox-settings-dialog-reset"),
        $soxSettingsClose = $settingsDialog.find("#sox-settings-dialog-close");

    sox.init = function() {

        // add extra CSS file and font-awesome CSS file
        $("head").append("<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css'>")
            .append("<link rel='stylesheet' type='text/css' href='https://rawgit.com/soscripted/sox/master/sox.css' />");
        $("body").append($settingsDialog);

        loadFeatures(); //load all the features in the settings dialog

        // add settings icon to navbar
        var $soxSettingsButton = $("<a/>", {
                id: "soxSettingsButton",
                class: "topbar-icon",
                style: "background-image: none; padding: 10px 0 0 10px; font-size: 14px; color: #999;",
                title: "Change sox Settings",
                click: function(e) {
                    e.preventDefault();
                    $("#sox-settings-dialog").toggle();
                }
            }),
            $icon = $("<i/>", {
                class: "fa fa-cogs"
            });
        $soxSettingsButton.append($icon).appendTo("div.network-items");

        // add click handlers for the buttons in the settings dialog
        $soxSettingsClose.on("click", function() {
            $soxSettingsDialog.hide();
        });
        $soxSettingsReset.on("click", function() {
            reset();
            location.reload(); // reload page to reflect changed settings
        });
        $soxSettingsSave.on("click", function() {
            extras = [];
            $soxSettingsDialogFeatures.find('input[type=checkbox]:checked').each(function() {
                var x = $(this).attr('id');
                extras.push(x); //Add the function's ID (also the checkbox's ID) to the array
            });
            save(extras);
            location.reload(); // reload page to reflect changed settings
        });

        // check if settings exist and execute desired functions
        if (isAvailable()) {
            extras = getSettings();
            if (isDeprecated()) { //if the setting is set but a deprecated, non-existent feature exists, then delete the setting and act as if it is new
                reset();
            } else {
                for (i = 0; i < extras.length; ++i) {
                    console.log(extras[i]);
                    $('#sox-settings-dialog #' + extras[i]).prop('checked', true);
                    features[extras[i]](); //Call the functions that were chosen
                }
            }
        } else {
            // no settings found, mark all inputs as checked and display settings dialog
            $soxSettingsDialogFeatures.find("input").prop("checked", true);
            $soxSettingsDialog.show(); //not working -- why!?
        }

    };

    function isAvailable() {
        //return ~GM_getValue("sox-featureOptions", -1) ? false : true;
        return (GM_getValue(SOX_SETTINGS, -1) == -1 ? false : true);
    }

    function getSettings() {
        return JSON.parse(GM_getValue(SOX_SETTINGS));
    }

    function reset() {
        GM_deleteValue(SOX_SETTINGS);
    }

    function save(options) {
        GM_setValue(SOX_SETTINGS, JSON.stringify(options));
        console.log("sox settings saved: " + JSON.stringify(options));
    }

    function isDeprecated() { //checks whether the saved settings contain a deprecated feature
        //TODO: add function names to an array and loop instead of || .. || .. ||
        settings = getSettings();
        if (settings.indexOf('answerCountSidebar') != -1 ||
            settings.indexOf('highlightClosedQuestions') != -1 ||
            settings.indexOf('unHideAnswer') != -1 ||
            settings.indexOf('flaggingPercentages') != -1) {
            return true;
        }
    }

    function addFeatures(features) {
        $.each(features, function(i, o) {
            var $div = $("<div/>"),
                $label = $("<label/>"),
                $input = $("<input/>", {
                    id: o[0],
                    "type": "checkbox",
                    style: "margin-right: 5px;"
                });
            $div.append($label);
            $label.append($input);
            $input.after(o[1]);
            $soxSettingsDialogFeatures.append($div);
        });
    }

    function addCategory(name) {
        var $div = $("<div/>"),
            $h3 = $("<h3/>", {
                text: name
            });
        $div.append($h3);
        $soxSettingsDialogFeatures.append($div);
    }

    function loadFeatures() {
        addCategory("Appearance");
        addFeatures([
            ["grayOutVotes", "Gray out deleted votes"],
            ["moveBounty", "Move 'start bounty' to top"],
            ["dragBounty", "Make bounty box draggable"],
            ["renameChat", "Prepend 'Chat - ' to chat tabs' titles"],
            ["exclaim", "Remove exclamation mark on message"],
            ["employeeStar", "Add star after employee names"],
            ["bulletReplace", "Replace triangluar bullets with normal ones"],
            ["addEllipsis", "Add ellipsis to long names"],
            ["fixedTopbar", "Fix topbar position"],
            ["displayName", "Display name before gravatar"],
            ["unspoil", "Add a link to show all spoilers in a post"],
            ["spoilerTip", "Differentiate spoilers from empty blockquotes"],
            ["stickyVoteButtons", "Make vote buttons next to posts sticky whilst scrolling on that post"],
            ["betterCSS", "Add extra CSS for voting signs and favourite button (currently only on Android SE)"],
            ["standOutDupeCloseMigrated", "Add colourful, more apparent signs to questions that are on hold, duplicates, closed or migrated on question lists"],
            ["colorAnswerer", "Color answerer's comments"],
            ["highlightQuestions", "Alternate favourite questions highlighing"],
            ["metaNewQuestionAlert", "Add an extra mod diamond to the topbar that alerts you if there is a new question posted on the child-meta of the current site"],
            ["hideHotNetworkQuestions", "Hide the Hot Network Questions module"],
            ["hideHireMe", "Hide the Looking for a Job module"],
            ["hideCommunityBulletin", "Hide the Community Bulletin module"]
        ]);

        addCategory("Flags");
        addFeatures([
            ["flagOutcomeTime", "Show the flag outcome time when viewing your Flag History"],
            ["flagPercentages", "Show flagging percentages for each type in the Flag Summary"]
            // lots more to come
        ]);

        addCategory("Editing");
        addFeatures([
            ["kbdAndBullets", "Add KBD and list buttons to editor toolbar"],
            ["editComment", "Pre-defined edit comment options"],
            ["editReasonTooltip", "Add a tooltip to posts showing the latest revision's comment on 'edited [date] at [time]'"],
            ["addSBSBtn", "Add a button the the editor toolbar to start side-by-side editing"],
            ["linkQuestionAuthorName", "Add a button in the editor toolbar to insert a hyperlink to a post and add the author automatically"],
            ["titleEditDiff", "Make title edits show seperately rather than merged"]
        ]);
        addCategory("Comments");

        addFeatures([
            ["moveCommentsLink", "Move 'show x more comments' to the top"],
            ["commentShortcuts", "Use Ctrl+I,B,K (to italicise, bolden and add code backticks) in comments"],
            ["quickCommentShortcutsMain", "Add shortcuts to add pre-defined comments to comment fields"],
            ["commentReplies", "Add reply links to comments for quick replying (without having to type someone's username)"],
            ["autoShowCommentImages", "View linked images (to imgur) in comments inline"],
            ["showCommentScores", "Show your comment and comment replies scores in your profile tabs"]
        ]);

        addCategory("Unsorted");
        addFeatures([
            //other
            ["shareLinksMarkdown", "Change 'share' link to format of [post-name](url)"],
            ["parseCrossSiteLinks", "Parse titles to links cross-SE-sites"],
            ["confirmNavigateAway", "Add a confirmation dialog before navigating away on pages whilst you are still typing a comment"],
            ["sortByBountyAmount", "Add an option to filter bounties by their amount"],
            ["isQuestionHot", "Add a label on questions which are hot-network questions"],
            ["answerTagsSearch", "Show tags for the question an answer belongs to on search pages (for better context)"],
            ["metaChatBlogStackExchangeButton", "Show meta, chat and blog buttons on hover of a site under the StackExchange button"],
            ["alwaysShowImageUploadLinkBox", "Always show the 'Link from the web' box when uploading an image"],
            ["addAuthorNameToInboxNotifications", "Add the author's name to notifications in the inbox"],
            ["scrollToTop", "Add Scroll To Top button"],
            ["linkedPostsInline", "Display linked posts inline (with an arrow)"]
        ]);
    }

}(window.sox = window.sox || {}, jQuery));

// initialize sox
sox.init(); //TODO: not sure if we really need this to be a function, script is wrapped in IIFE so init code could just go at the top.
<commit_msg>more changes......<commit_after>// ==UserScript==
// @name         Stack Overflow Extras (SOX)
// @namespace    https://github.com/soscripted/sox
// @version      DEV
// @description  Adds a bunch of optional features to sites in the Stack Overflow Network.
// @contributor  ᴉʞuǝ (stackoverflow.com/users/1454538/)
// @contributor  ᔕᖺᘎᕊ (stackexchange.com/users/4337810/)
// @updateURL    https://rawgit.com/soscripted/sox/master/sox.user.js
// @match        *://*.stackoverflow.com/*
// @match        *://*.stackexchange.com/*
// @match        *://*.superuser.com/*
// @match        *://*.serverfault.com/*
// @match        *://*.askubuntu.com/*
// @match        *://*.stackapps.com/*
// @match        *://*.mathoverflow.net/*
// @require      https://code.jquery.com/jquery-2.1.4.min.js
// @require      https://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js
// @require      https://cdn.rawgit.com/timdown/rangyinputs/master/rangyinputs-jquery-src.js
// @require      https://cdn.rawgit.com/jeresig/jquery.hotkeys/master/jquery.hotkeys.js
// @require      https://cdn.rawgit.com/camagu/jquery-feeds/master/jquery.feeds.js
// @require      https://rawgit.com/soscripted/sox/master/sox.helpers.js?v=02092016a
// @require      https://rawgit.com/soscripted/sox/master/sox.features.js?v=02092016a
// @resource     settingsDialog https://rawgit.com/soscripted/sox/master/sox-dialog.html
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_getResourceText
// ==/UserScript==
/*jshint multistr: true */
(function(sox, $, undefined) {
    const SOX = "Stack Overflow Extras";
    const SOX_SETTINGS = "SOXSETTINGS";

    var $settingsDialog = $(GM_getResourceText("settingsDialog")),
        $soxSettingsDialog,
        $soxSettingsDialogFeatures,
        $soxSettingsSave,
        $soxSettingsReset,
        $soxSettingsClose;

    sox.init = function() {

        // add sox CSS file and font-awesome CSS file
        $("head").append("<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css'>")
                 .append("<link rel='stylesheet' type='text/css' href='https://rawgit.com/soscripted/sox/master/sox.css' />");
        $("body").append($settingsDialog);

        $soxSettingsDialog = $("#sox-settings-dialog");
        $soxSettingsDialogFeatures = $soxSettingsDialog.find("#sox-settings-dialog-features");
        $soxSettingsSave = $soxSettingsDialog.find("#sox-settings-dialog-save");
        $soxSettingsReset = $soxSettingsDialog.find("#sox-settings-dialog-reset");
        $soxSettingsClose = $soxSettingsDialog.find("#sox-settings-dialog-close");

        loadFeatures(); //load all the features in the settings dialog

        // add settings icon to navbar
        var $soxSettingsButton = $("<a/>", {
                id: "soxSettingsButton",
                class: "topbar-icon",
                style: "background-image: none; padding: 10px 0 0 10px; font-size: 14px; color: #999;",
                title: "Change sox Settings",
                click: function(e) {
                    e.preventDefault();
                    $("#sox-settings-dialog").toggle();
                }
            }),
            $icon = $("<i/>", {
                class: "fa fa-cogs"
            });
        $soxSettingsButton.append($icon).appendTo("div.network-items");

        // add click handlers for the buttons in the settings dialog
        $soxSettingsClose.on("click", function() {
            $soxSettingsDialog.hide();
        });
        $soxSettingsReset.on("click", function() {
            reset();
            location.reload(); // reload page to reflect changed settings
        });
        $soxSettingsSave.on("click", function() {
            extras = [];
            $soxSettingsDialogFeatures.find('input[type=checkbox]:checked').each(function() {
                var x = $(this).attr('id');
                extras.push(x); //Add the function's ID (also the checkbox's ID) to the array
            });
            save(extras);
            location.reload(); // reload page to reflect changed settings
        });

        // check if settings exist and execute desired functions
        if (isAvailable()) {
            extras = getSettings();
            if (isDeprecated()) { //if the setting is set but a deprecated, non-existent feature exists, then delete the setting and act as if it is new
                reset();
            } else {
                for (i = 0; i < extras.length; ++i) {
                    //console.log(extras[i]);
                    $soxSettingsDialogFeatures.find("#" + extras[i]).prop('checked', true);
                    //$('#sox-settings-dialog #' + extras[i]).prop('checked', true);
                    features[extras[i]](); //Call the functions that were chosen
                }
            }
        } else {
            // no settings found, mark all inputs as checked and display settings dialog
            $soxSettingsDialogFeatures.find("input").prop("checked", true);

            setTimeout(function(){
                $soxSettingsDialog.show();
            }, 500);

            //$soxSettingsDialog.delay(800).show(); //not working -- why!?
        }

    };

    function isAvailable() {
        //return ~GM_getValue("sox-featureOptions", -1) ? false : true;
        return (GM_getValue(SOX_SETTINGS, -1) == -1 ? false : true);
    }

    function getSettings() {
        return JSON.parse(GM_getValue(SOX_SETTINGS));
    }

    function reset() {
        GM_deleteValue(SOX_SETTINGS);
    }

    function save(options) {
        GM_setValue(SOX_SETTINGS, JSON.stringify(options));
        console.log("sox settings saved: " + JSON.stringify(options));
    }

    function isDeprecated() { //checks whether the saved settings contain a deprecated feature
        //TODO: add function names to an array and loop instead of || .. || .. ||
        settings = getSettings();
        if (settings.indexOf('answerCountSidebar') != -1 ||
            settings.indexOf('highlightClosedQuestions') != -1 ||
            settings.indexOf('unHideAnswer') != -1 ||
            settings.indexOf('flaggingPercentages') != -1) {
            return true;
        }
    }

    function addFeatures(features) {
        $.each(features, function(i, o) {
            var $div = $("<div/>"),
                $label = $("<label/>"),
                $input = $("<input/>", {
                    id: o[0],
                    "type": "checkbox",
                    style: "margin-right: 5px;"
                });
            $div.append($label);
            $label.append($input);
            $input.after(o[1]);
            $soxSettingsDialogFeatures.append($div);
        });
    }

    function addCategory(name) {
        var $div = $("<div/>"),
            $h3 = $("<h3/>", {
                text: name
            });
        $div.append($h3);
        $soxSettingsDialogFeatures.append($div);
    }

    function loadFeatures() {
        addCategory("Appearance");
        addFeatures([
            ["grayOutVotes", "Gray out deleted votes"],
            ["moveBounty", "Move 'start bounty' to top"],
            ["dragBounty", "Make bounty box draggable"],
            ["renameChat", "Prepend 'Chat - ' to chat tabs' titles"],
            ["exclaim", "Remove exclamation mark on message"],
            ["employeeStar", "Add star after employee names"],
            ["bulletReplace", "Replace triangluar bullets with normal ones"],
            ["addEllipsis", "Add ellipsis to long names"],
            ["fixedTopbar", "Fix topbar position"],
            ["displayName", "Display name before gravatar"],
            ["unspoil", "Add a link to show all spoilers in a post"],
            ["spoilerTip", "Differentiate spoilers from empty blockquotes"],
            ["stickyVoteButtons", "Make vote buttons next to posts sticky whilst scrolling on that post"],
            ["betterCSS", "Add extra CSS for voting signs and favourite button (currently only on Android SE)"],
            ["standOutDupeCloseMigrated", "Add colourful, more apparent signs to questions that are on hold, duplicates, closed or migrated on question lists"],
            ["colorAnswerer", "Color answerer's comments"],
            ["highlightQuestions", "Alternate favourite questions highlighing"],
            ["metaNewQuestionAlert", "Add an extra mod diamond to the topbar that alerts you if there is a new question posted on the child-meta of the current site"],
            ["hideHotNetworkQuestions", "Hide the Hot Network Questions module"],
            ["hideHireMe", "Hide the Looking for a Job module"],
            ["hideCommunityBulletin", "Hide the Community Bulletin module"]
        ]);

        addCategory("Flags");
        addFeatures([
            ["flagOutcomeTime", "Show the flag outcome time when viewing your Flag History"],
            ["flagPercentages", "Show flagging percentages for each type in the Flag Summary"]
            // lots more to come
        ]);

        addCategory("Editing");
        addFeatures([
            ["kbdAndBullets", "Add KBD and list buttons to editor toolbar"],
            ["editComment", "Pre-defined edit comment options"],
            ["editReasonTooltip", "Add a tooltip to posts showing the latest revision's comment on 'edited [date] at [time]'"],
            ["addSBSBtn", "Add a button the the editor toolbar to start side-by-side editing"],
            ["linkQuestionAuthorName", "Add a button in the editor toolbar to insert a hyperlink to a post and add the author automatically"],
            ["titleEditDiff", "Make title edits show seperately rather than merged"]
        ]);
        addCategory("Comments");

        addFeatures([
            ["moveCommentsLink", "Move 'show x more comments' to the top"],
            ["commentShortcuts", "Use Ctrl+I,B,K (to italicise, bolden and add code backticks) in comments"],
            ["quickCommentShortcutsMain", "Add shortcuts to add pre-defined comments to comment fields"],
            ["commentReplies", "Add reply links to comments for quick replying (without having to type someone's username)"],
            ["autoShowCommentImages", "View linked images (to imgur) in comments inline"],
            ["showCommentScores", "Show your comment and comment replies scores in your profile tabs"]
        ]);

        addCategory("Unsorted");
        addFeatures([
            //other
            ["shareLinksMarkdown", "Change 'share' link to format of [post-name](url)"],
            ["parseCrossSiteLinks", "Parse titles to links cross-SE-sites"],
            ["confirmNavigateAway", "Add a confirmation dialog before navigating away on pages whilst you are still typing a comment"],
            ["sortByBountyAmount", "Add an option to filter bounties by their amount"],
            ["isQuestionHot", "Add a label on questions which are hot-network questions"],
            ["answerTagsSearch", "Show tags for the question an answer belongs to on search pages (for better context)"],
            ["metaChatBlogStackExchangeButton", "Show meta, chat and blog buttons on hover of a site under the StackExchange button"],
            ["alwaysShowImageUploadLinkBox", "Always show the 'Link from the web' box when uploading an image"],
            ["addAuthorNameToInboxNotifications", "Add the author's name to notifications in the inbox"],
            ["scrollToTop", "Add Scroll To Top button"],
            ["linkedPostsInline", "Display linked posts inline (with an arrow)"]
        ]);
    }

}(window.sox = window.sox || {}, jQuery));

// initialize sox
sox.init(); //TODO: not sure if we really need this to be a function, script is wrapped in IIFE so init code could just go at the top.
<|endoftext|>


<commit_before>/**
 * Created by david on 01/09/2016.
 */
$(function () {

//  Ajax globals
    $(document).ajaxStart(function () {
        $('#ajax-loading').show();
    });
    $(document).ajaxStop(function () {
        $('#ajax-loading').hide();
    });

//  Sticky table headers
    $('#unittable').stickyTableHeaders();

});<commit_msg>Refactored the js to remove the repeated code<commit_after>/**
 * Created by david on 01/09/2016.
 */
$(function () {

//  Ajax globals
    $(document).ajaxStart(function () {
        $('#ajax-loading').show();
    });
    $(document).ajaxStop(function () {
        $('#ajax-loading').hide();
    });

//  Sticky table headers
    $('#unittable').stickyTableHeaders();

//  Show and hide buttons for slides
    var hide = ' Hide';
    var show = ' Show';

//  Show and or hide a form well element
    function toggleWell(well, form, button) {
        button.click(function (e) {
            e.preventDefault();
            form.slideToggle({
                start: function (animation) {
                    if ($(this).is(':visible')) {
                        button.html(hide);
                    } else {
                        button.html(show);
                    }
                },
                complete: function () {
                    if ($(this).is(':visible')) {
                        button.html(hide);
                    } else {
                        button.html(show);
                    }
                }
            });
        });
    }

//  Show/Hide for the search form
    if (window.location.search.indexOf('search=1') !== -1) {
        $('div.search.well a.show-hide').html(hide);
    } else {
        $('div.search.well div.search-form').hide();
    }

    var searchWell = $('div.search.well');
    var searchForm = $('div.search.well div.search-form');
    var searchButton = $('div.search.well a.show-hide');

    toggleWell(searchWell, searchForm, searchButton);

//  Show/Hide for the add form
    $('div.add-units.well div.add-form').hide();

    var addWell = $('div.add-units.well');
    var addForm = $('div.add-units.well div.add-form');
    var addButton = $('div.add-units.well a.show-hide');

    toggleWell(addWell, addForm, addButton);

});<|endoftext|>


<commit_before>// Copyright © 2012 Martin Ueding <dev@martin-ueding.de>

var animationTime = 300;

var currentData = [];
var animationActive = false;
var ajaxActive = false;

var searchMain = function () {
	showField();
	attachListener();
};

var showField = function () {
	console.debug('showField()');
	$('#navigation').after('
<input id="search_input" type="text" />
');
};

var attachListener = function () {
	console.debug('attachListener()');
	$('#search_input').keyup(formChanged);
};

var formChanged = function () {
	console.debug('formChanged()');
	var input = $(this);
	var query = $(this).val();
	hideResultPane();
	performQuery(query);
};

var performQuery = function (query) {
	console.debug('perfomQuery()');
	ajaxActive = true;
	$.ajax({
		dataType: 'json',
		success: querySuccess,
		type: 'GET',
		url: 'bookmarks/search/'+query,
	});
};

var querySuccess = function (data, textStatus, jqXHR) {
	console.debug('querySuccess()');
	ajaxActive = false;
	console.debug(data);
	currentData = data;
	updateResultPane();
};

var initResultPane = function () {
	console.debug('initResultPane()');
	if ($('#search_result_pane').length == 0) {
		$('#search').append('

');
		$('#search_result_pane').slideUp(0);
	}
};

var hideResultPane = function () {
	console.debug('hideResultPane()');
	if ($('#search_result_pane').length > 0) {
		animationActive = true;
		$('#search_result_pane').slideUp(animationTime, animationDone);
	}
};

var updateResultPane = function () {
	console.debug('updateResultPane()');

	if (ajaxActive || animationActive) {
		console.debug('updateResultPane(): Quitting since active.');
		return;
	}

	initResultPane();
	var searchResultPane = $('#search_result_pane');
	searchResultPane.html(formatResults(currentData));
	searchResultPane.slideDown(animationTime);
};

var animationDone = function () {
	console.debug('animationDone()');
	animationActive = false;
	updateResultPane();
};

var formatResults = function (data) {
	console.debug('formatResults()');
	var parts = [];

	// TODO Limit this in PHP, and not here to save transfer time.
	for (var rowNum = 0; rowNum < data.length && rowNum < 10; rowNum++) {
		var row = data[rowNum];
		var hitTarget = 'bookmarks/visit/'+row.Bookmark.id
		parts.push('
');
		parts.push('');
		parts.push(row.Bookmark.title);
		parts.push('');
		parts.push('');
		parts.push(row.Bookmark.url);
		parts.push('');
		parts.push('
');
	}

	var result = '
'+parts.join('')+'
';
	console.debug(result);
	return result;
};

$(searchMain);
<commit_msg>Run less useless queries<commit_after>// Copyright © 2012 Martin Ueding <dev@martin-ueding.de>

var animationTime = 300;

/**
 * The time that has to elapse since the last AJAX event was sent off.
 */
var idleTime = 500;

var currentData = [];
var animationActive = false;
var ajaxActive = false;
var lastChange = 0;
var currentQuery = "";

var searchMain = function () {
	showField();
	attachListener();
};

var showField = function () {
	console.debug('showField()');
	$('#navigation').after('
<input id="search_input" type="text" />
');
};

var attachListener = function () {
	console.debug('attachListener()');
	$('#search_input').keyup(formChanged);
};

var formChanged = function () {
	console.debug('formChanged()');
	var newQuery = $(this).val();
	if (newQuery == currentQuery) {
		return;
	}
	currentQuery = newQuery;
	lastChange = new Date().getTime();
	waitToQuery();
};

var waitToQuery = function () {
	console.debug('formChanged()');

	if (ajaxActive) {
		return;
	}

	var now = new Date().getTime();
	var elapsed = now - lastChange;

	if (elapsed > idleTime) {
		hideResultPane();
		performQuery();
	}
	else {
		var toWait = idleTime - elapsed;
		console.debug('formChanged(): Still have to wait '+toWait+'.');
		setTimeout(waitToQuery, toWait);
	}
};

var performQuery = function () {
	console.debug('perfomQuery()');
	ajaxActive = true;
	$.ajax({
		dataType: 'json',
		success: querySuccess,
		type: 'GET',
		url: 'bookmarks/search/'+currentQuery,
	});
};

var querySuccess = function (data, textStatus, jqXHR) {
	console.debug('querySuccess()');
	ajaxActive = false;
	console.debug(data);
	currentData = data;
	updateResultPane();
};

var initResultPane = function () {
	console.debug('initResultPane()');
	if ($('#search_result_pane').length == 0) {
		$('#search').append('

');
		$('#search_result_pane').slideUp(0);
	}
};

var hideResultPane = function () {
	console.debug('hideResultPane()');
	if ($('#search_result_pane').length > 0) {
		animationActive = true;
		$('#search_result_pane').slideUp(animationTime, animationDone);
	}
};

var updateResultPane = function () {
	console.debug('updateResultPane()');

	if (ajaxActive || animationActive) {
		console.debug('updateResultPane(): Quitting since active.');
		return;
	}

	initResultPane();
	var searchResultPane = $('#search_result_pane');
	searchResultPane.html(formatResults(currentData));
	searchResultPane.slideDown(animationTime);
};

var animationDone = function () {
	console.debug('animationDone()');
	animationActive = false;
	updateResultPane();
};

var formatResults = function (data) {
	console.debug('formatResults()');
	var parts = [];

	// TODO Limit this in PHP, and not here to save transfer time.
	for (var rowNum = 0; rowNum < data.length && rowNum < 10; rowNum++) {
		var row = data[rowNum];
		var hitTarget = 'bookmarks/visit/'+row.Bookmark.id
		parts.push('
');
		parts.push('');
		parts.push(row.Bookmark.title);
		parts.push('');
		parts.push('');
		parts.push(row.Bookmark.url);
		parts.push('');
		parts.push('
');
	}

	var result = '
'+parts.join('')+'
';
	console.debug(result);
	return result;
};

$(searchMain);
<|endoftext|>


<commit_before>// functions shared by mainTreeView and recordTreeView
ngapp.service('treeService', function($timeout, htmlHelpers) {
    this.buildFunctions = function(scope, element) {
        // helper variables
        let enterKey = 13, leftArrow = 37, upArrow = 38, rightArrow = 39, downArrow = 40, fKey = 70;

        // scope functions
        scope.getNodeForElement = function(handle) {
            let handles = xelib.GetDuplicateHandles(handle);
            for (let i = 0; i < handles.length; i++) {
                let h = handles[i],
                    newNode = scope.tree.find((node) => { return node.handle == h; });
                if (newNode) return newNode;
            }
        };

        scope.expandNode = function(node) {
            if (!node.can_expand || node.expanded) return;
            let start = Date.now();
            node.expanded = true;
            let children = scope.buildNodes(node),
                childrenLength = children.length;
            if (childrenLength > 0) {
                children.forEach((child) => child.parent = node);
                let insertionIndex = scope.tree.indexOf(node) + 1;
                scope.tree.splice(insertionIndex, 0, ...children);
                console.log(`Built ${childrenLength} nodes in ${Date.now() - start}ms`);
            } else {
                node.can_expand = false;
                node.expanded = false;
            }
        };

        scope.collapseNode = function(node) {
            if (!node.expanded) return;
            delete node.expanded;
            let startIndex = scope.tree.indexOf(node) + 1,
                endIndex = startIndex;
            for (; endIndex < scope.tree.length; endIndex++) {
                let child = scope.tree[endIndex];
                if (child.depth <= node.depth) break;
                if (child.selected) scope.selectSingle(child, false);
            }
            let removedNodes = scope.tree.splice(startIndex, endIndex - startIndex);
            removedNodes.forEach(function(node) {
                if (node.handle) xelib.Release(node.handle);
                if (node.handles) {
                    node.handles.forEach((handle) => handle && xelib.Release(handle));
                }
            });
            if (scope.prevNode && scope.prevNode.parent === node) {
                scope.prevNode = undefined;
            }
        };

        scope.toggleNode = function(e, node) {
            if (node.expanded) {
                scope.collapseNode(node);
            } else {
                scope.expandNode(node);
            }
            e && e.stopPropagation();
        };

        let scrollbarWidth = 17;
        scope.treeMouseDown = function(e) {
            let t = scope.treeElement;
            if (e.clientX - t.offsetLeft < t.offsetWidth - scrollbarWidth) {
                scope.clearSelection(true);
            }
        };

        scope.focusSearchInput = function() {
            let searchInput = htmlHelpers.resolveElement(scope.tabView, 'search-bar/input');
            searchInput.focus();
        };

        scope.toggleSearch = function(visible) {
            scope.showSearchBar = visible;
            if (visible) {
                $timeout(scope.focusSearchInput, 50);
            } else {
                scope.treeElement.focus();
            }
        };

        scope.treeKeyDown = function(e) {
            if (e.keyCode == rightArrow) {
                scope.handleRightArrow(e);
            } else if (e.keyCode == leftArrow) {
                scope.handleLeftArrow(e);
            } else if (e.keyCode == downArrow) {
                scope.handleDownArrow(e)
            } else if (e.keyCode == upArrow) {
                scope.handleUpArrow(e);
            } else if (e.keyCode == enterKey) {
                scope.handleEnter(e);
            } else if (e.ctrlKey && !e.shiftKey && e.keyCode == fKey) {
                scope.toggleSearch(true);
            } else {
                return;
            }
            e.stopPropagation();
            e.preventDefault();
        };

        scope.resolveElements = function() {
            scope.tabView = element[0].nextElementSibling;
            scope.treeElement = htmlHelpers.resolveElement(scope.tabView, '.tree-nodes');
            scope.columnsElement = htmlHelpers.resolveElement(scope.tabView, '.column-wrapper');
        };
    }
});<commit_msg>added children caching<commit_after>// functions shared by mainTreeView and recordTreeView
ngapp.service('treeService', function($timeout, htmlHelpers) {
    this.buildFunctions = function(scope, element) {
        // helper variables
        let enterKey = 13, leftArrow = 37, upArrow = 38, rightArrow = 39, downArrow = 40, fKey = 70;

        // scope functions
        scope.getNodeForElement = function(handle) {
            let handles = xelib.GetDuplicateHandles(handle);
            for (let i = 0; i < handles.length; i++) {
                let h = handles[i],
                    newNode = scope.tree.find((node) => { return node.handle == h; });
                if (newNode) return newNode;
            }
        };

        scope.expandNode = function(node) {
            if (!node.can_expand || node.expanded) return;
            let start = Date.now(),
                children = node.children || scope.buildNodes(node),
                childrenLength = children.length;
            if (childrenLength > 0) {
                if (!node.children) {
                    children.forEach((child) => child.parent = node);
                    node.children = children;
                    console.log(`Built ${childrenLength} nodes in ${Date.now() - start}ms`);
                }
                node.expanded = true;
                let insertionIndex = scope.tree.indexOf(node) + 1;
                scope.tree.splice(insertionIndex, 0, ...children);
            } else {
                node.can_expand = false;
            }
        };

        scope.collapseNode = function(node) {
            if (!node.expanded) return;
            delete node.expanded;
            let startIndex = scope.tree.indexOf(node) + 1,
                endIndex = startIndex;
            for (; endIndex < scope.tree.length; endIndex++) {
                let child = scope.tree[endIndex];
                if (child.depth <= node.depth) break;
                if (child.selected) scope.selectSingle(child, false);
            }
            scope.tree.splice(startIndex, endIndex - startIndex);
            if (scope.prevNode && scope.prevNode.parent === node) {
                scope.prevNode = undefined;
            }
        };

        scope.toggleNode = function(e, node) {
            e && e.stopImmediatePropagation();
            scope[node.expanded ? 'collapseNode' : 'expandNode'](node);
        };

        let scrollbarWidth = 17;
        scope.treeMouseDown = function(e) {
            let t = scope.treeElement;
            if (e.clientX - t.offsetLeft < t.offsetWidth - scrollbarWidth) {
                scope.clearSelection(true);
            }
        };

        scope.focusSearchInput = function() {
            let searchInput = htmlHelpers.resolveElement(scope.tabView, 'search-bar/input');
            searchInput.focus();
        };

        scope.toggleSearch = function(visible) {
            scope.showSearchBar = visible;
            if (visible) {
                $timeout(scope.focusSearchInput, 50);
            } else {
                scope.treeElement.focus();
            }
        };

        scope.treeKeyDown = function(e) {
            if (e.keyCode == rightArrow) {
                scope.handleRightArrow(e);
            } else if (e.keyCode == leftArrow) {
                scope.handleLeftArrow(e);
            } else if (e.keyCode == downArrow) {
                scope.handleDownArrow(e)
            } else if (e.keyCode == upArrow) {
                scope.handleUpArrow(e);
            } else if (e.keyCode == enterKey) {
                scope.handleEnter(e);
            } else if (e.ctrlKey && !e.shiftKey && e.keyCode == fKey) {
                scope.toggleSearch(true);
            } else {
                return;
            }
            e.stopPropagation();
            e.preventDefault();
        };

        scope.resolveElements = function() {
            scope.tabView = element[0].nextElementSibling;
            scope.treeElement = htmlHelpers.resolveElement(scope.tabView, '.tree-nodes');
            scope.columnsElement = htmlHelpers.resolveElement(scope.tabView, '.column-wrapper');
        };
    }
});<|endoftext|>


<commit_before>function AnnotationsEventsController(annotations_controller) {

    this.DUMMY_TOOL = 'dummy_tool';
    this.IMAGE_MARKING_TOOL = 'image_marker';

    this.annotation_controller = annotations_controller;
    this.initialized_tools = {};

    this._bind_switch = function(switch_id, tool_label) {
        $("#" + switch_id).bind(
            'click',
            {'events_controller': this, 'tool_label': tool_label},
            function(event) {
                event.data.events_controller.activateTool(event.data.tool_label);
            }
        );
    };

    // Create a tool that simply ignores mouse events, this will be used, for example, to avoid
    // events propagation when capturing events on Shapes
    this.initializeDummyTool = function() {
        if (! (this.DUMMY_TOOL in this.initialized_tools)) {
            this.initialized_tools[this.DUMMY_TOOL] = new paper.Tool();
        }
    };

    this.initializeImageMarkingTool = function(marker_size, markers_limit, switch_id) {
        // by default, initialize dummy tool
        this.initializeDummyTool();

        if(! (this.IMAGE_MARKING_TOOL in this.initialized_tools)) {

            AnnotationsController.prototype.markers_counter = 0;

            if ((typeof markers_limit !== 'undefined') || (markers_limit > 0)) {
                AnnotationsController.prototype.max_markers_count = markers_limit;
            } else {
                AnnotationsController.prototype.max_markers_count = 0;
            }

            AnnotationsController.prototype._check_markers_limit = function() {
                if (this.max_markers_count > 0 && (this.markers_counter >= this.max_markers_count))
                    return false;
                return true;
            };

            AnnotationsController.prototype.removeMarker = function(marker_id) {
                var deleted = this.deleteShape(marker_id);
                if (deleted === true) {
                    this.markers_counter -= 1;
                    $(document).trigger('marker_deleted', [marker_id]);
                }
            };

            AnnotationsController.prototype.addMarker = function(event) {
                var add_new_marker = this._check_markers_limit();
                if (add_new_marker === true) {
                    console.log('Adding marker');
                    var img_x = event.point.x + this.x_offset;
                    var img_y = event.point.y + this.y_offset;
                    var shape_id = this._getShapeId('marker');
                    this.drawCircle(shape_id, img_x, img_y, event.marker_radius, undefined, true);
                    this.markers_counter += 1;
                    $(document).trigger('marker_created', [shape_id]);
                }
            };

            var marking_tool = new paper.Tool();

            marking_tool.annotations_controller = this.annotation_controller;
            marking_tool.marker_size = marker_size;

            marking_tool.onMouseDown = function(event) {
                event.marker_radius = this.marker_size / 2;
                this.annotations_controller.addMarker(event);
            };

            this.initialized_tools[this.IMAGE_MARKING_TOOL] = marking_tool;

            if (typeof switch_id !== 'undefined') {
                this._bind_switch(switch_id, this.IMAGE_MARKING_TOOL);
            }
        } else {
            console.warn('Tool "' + this.IMAGE_MARKING_TOOL + '" already initialized');
        }
    };

    this.activateTool = function(tool_label, disable_events_on_shapes) {
        if (tool_label in this.initialized_tools) {
            var sh_ev_off = (typeof disable_events_on_shapes === 'undefined') ? true : disable_events_on_shapes;
            if (sh_ev_off === true)
                this.annotation_controller.disableEventsOnShapes();
            var tool = this.initialized_tools[tool_label];
            tool.activate();
            this.annotation_controller.enableMouseEvents();
        } else {
            console.warn('Tool ' + tool_label + ' not initialized');
        }
    };
}
<commit_msg>tracking markers ID instead of the simple counter<commit_after>function AnnotationsEventsController(annotations_controller) {

    this.DUMMY_TOOL = 'dummy_tool';
    this.IMAGE_MARKING_TOOL = 'image_marker';

    this.annotation_controller = annotations_controller;
    this.initialized_tools = {};

    this._bind_switch = function(switch_id, tool_label) {
        $("#" + switch_id).bind(
            'click',
            {'events_controller': this, 'tool_label': tool_label},
            function(event) {
                event.data.events_controller.activateTool(event.data.tool_label);
            }
        );
    };

    // Create a tool that simply ignores mouse events, this will be used, for example, to avoid
    // events propagation when capturing events on Shapes
    this.initializeDummyTool = function() {
        if (! (this.DUMMY_TOOL in this.initialized_tools)) {
            this.initialized_tools[this.DUMMY_TOOL] = new paper.Tool();
        }
    };

    this.initializeImageMarkingTool = function(marker_size, markers_limit, switch_id) {
        // by default, initialize dummy tool
        this.initializeDummyTool();

        if(! (this.IMAGE_MARKING_TOOL in this.initialized_tools)) {

            AnnotationsController.prototype.markers_id = [];

            if ((typeof markers_limit !== 'undefined') || (markers_limit > 0)) {
                AnnotationsController.prototype.max_markers_count = markers_limit;
            } else {
                AnnotationsController.prototype.max_markers_count = 0;
            }

            AnnotationsController.prototype._check_markers_limit = function() {
                if (this.markers_id.length > 0 && (this.markers_counter >= this.max_markers_count))
                    return false;
                return true;
            };

            AnnotationsController.prototype.removeMarker = function(marker_id) {
                var deleted = this.deleteShape(marker_id);
                if (deleted === true) {
                    this.markers_id.splice(this.markers_id.indexOf(marker_id), 1);
                    $(document).trigger('marker_deleted', [marker_id]);
                }
            };

            AnnotationsController.prototype.addMarker = function(event) {
                var add_new_marker = this._check_markers_limit();
                if (add_new_marker === true) {
                    console.log('Adding marker');
                    var img_x = event.point.x + this.x_offset;
                    var img_y = event.point.y + this.y_offset;
                    var shape_id = this._getShapeId('marker');
                    this.drawCircle(shape_id, img_x, img_y, event.marker_radius, undefined, true);
                    this.markers_id.push(shape_id);
                    $(document).trigger('marker_created', [shape_id]);
                }
            };

            var marking_tool = new paper.Tool();

            marking_tool.annotations_controller = this.annotation_controller;
            marking_tool.marker_size = marker_size;

            marking_tool.onMouseDown = function(event) {
                event.marker_radius = this.marker_size / 2;
                this.annotations_controller.addMarker(event);
            };

            this.initialized_tools[this.IMAGE_MARKING_TOOL] = marking_tool;

            if (typeof switch_id !== 'undefined') {
                this._bind_switch(switch_id, this.IMAGE_MARKING_TOOL);
            }
        } else {
            console.warn('Tool "' + this.IMAGE_MARKING_TOOL + '" already initialized');
        }
    };

    this.activateTool = function(tool_label, disable_events_on_shapes) {
        if (tool_label in this.initialized_tools) {
            var sh_ev_off = (typeof disable_events_on_shapes === 'undefined') ? true : disable_events_on_shapes;
            if (sh_ev_off === true)
                this.annotation_controller.disableEventsOnShapes();
            var tool = this.initialized_tools[tool_label];
            tool.activate();
            this.annotation_controller.enableMouseEvents();
        } else {
            console.warn('Tool ' + tool_label + ' not initialized');
        }
    };
}
<|endoftext|>


<commit_before>// interface that talks to arduino using the serial port

var events = require('events');
var util = require('util');
var serialport = require('serialport');
var SerialPort = serialport.SerialPort;

// find serial ports:
// serialport.list(function (err, ports) {
//   ports.forEach(function(port) {
//     console.log(port.comName);
//     console.log(port.pnpId);
//     console.log(port.manufacturer);
//   });
// });
// return;

var serialportname = '/dev/ttyATH0';
if( process.env.NODE_ENV != 'production' ){
	var serialportname = '/dev/cu.usbmodemfa131'; // for debugging on local machine
}

// Serial:

// my own parser (waits for 3 times 255):
function ownparser () {
	var data = new Buffer(0);

	return function (emitter, buffer){
		// console.log('rawbuffer', buffer);

		data = Buffer.concat([data, buffer]);

		var checkfornewmessage = false;
		if(data.length > 0)
			checkfornewmessage = true;

		while(checkfornewmessage){
			checkfornewmessage = false;

			var startByteCounter = 0;
			var newMessageInTheMaking = false;
			for (var i = 0; i < data.length; i++) {
				if(data[i] == 255){
					startByteCounter++;
				}else{
					// first byte that's not 255 comes in
					// check if we have 3 start bytes:
					if(startByteCounter >=3){
						// good, this must a new message
						newMessageInTheMaking = true;
						break; // stop looking for other bytes:
					}else{
						// to bad, only 2 or less bytes where 255:
						startByteCounter = 0;
					}
				}
			}

			if(!newMessageInTheMaking) return; //wait some more until we have more bytes

			// find first occurrence of 255:
			var firstOccurenceOf255 = null;
			for (var i = 0; i < data.length; i++) {
				if(data[i] == 255){
					firstOccurenceOf255 = i;
					break;
				}
			}

			// cut of all data before this firstOccurenceOf255:
			data = data.slice(firstOccurenceOf255);

			// check if byte 4 is not 255:
			while(data[3] == 255){
				// cut off the buffer so that byte 4 is not 255:
				data = data.slice(1);
			}

			// now check for type of message:
			// 1 = soundstate
			// 2 = soundlevel
			// 3 = incomming settings

			var out;

			if( data[3] == 1 ){
				// we expect 2 more bytes to follow (so that's 6 bytes in total):
				if(data.length < 6) return; // wait for more bytes
				out = data.slice(0, 6);
				data = data.slice(6);
			}

			if( data[3] == 2 ){
				// we expect 3 more bytes to follow (so that's 7 bytes in total):
				if(data.length < 7) return; // wait for more bytes
				out = data.slice(0, 7);
				data = data.slice(7);
			}

			if( data[3] == 3 ){
				// we expect 9 more bytes to follow (so that's 13 bytes in total):
				if(data.length < 13) return; // wait for more bytes
				out = data.slice(0, 13);
				data = data.slice(13);
			}

			// emit those 6,7 or 13 bytes:
			emitter.emit('data', out);

			if(data.length>0){
				checkfornewmessage = true;
			}
		}
	};
}

Arduino = function(options){
	events.EventEmitter.call(this);

	var self = this;

	this.sp = new SerialPort(serialportname, {
		parser: ownparser(),
		baudrate: 9600
	});

	serialport.on("error", function (err) {
		console.log(err);
	});

	this.sp.on("open", function () {
		console.log('serialport is open');
	});

	this.sp.on('data', function (data) {
		// console.log(data);

		if(data[3] == 1){
			return parseSoundState(data);
		}

		if(data[3] == 2){
			return parseSoundLevel(data);
		}

		if(data[3] == 3){
			return parseSettings(data);
		}
	});

	this.sp.on("close", function () {
		console.log('serialport closed');
	});

	function parseSoundLevel (data) {
		var soundlevel = {
			channel: data[4],
			value: data.readUInt16BE(5) // last two bytes are a 16bit integer encoded big endian
		};
		self.emit("soundlevelChanged", soundlevel);
	}

	function parseSoundState (data) {
		var state = {
			channel: data[4],
			soundstate: (data[5] == 1) // boolean, true or false
		};
		self.emit("soundstateChanged", state);
	}

	function parseSettings (data) {
		var channel = {
			id: data[4],
			silenceThreshold: data.readUInt16BE(5),
			soundThreshold: data.readUInt16BE(7),
			nrOfConsecutiveSilenceSamplesBeforeFlippingToSilence: data.readUInt16BE(9),
			nrOfConsecutiveSoundSamplesBeforeFlippingToSound: data.readUInt16BE(11)
		}
		self.emit("incommingSettings", channel);
	}

	return this;
}

util.inherits(Arduino, events.EventEmitter);

// 'public' functions:
Arduino.prototype.enableSoundgraphMonitoring = function (channelid) {
	var buf = new Buffer(5);
	buf[0] = 255;
	buf[1] = 255;
	buf[2] = 255;
	buf[3] = 1; // enable monitoring
	buf[4] = channelid;

	this.sp.write( buf , function (err, results) {
		if(err) return console.log('serial write err', err);
	});
};

Arduino.prototype.disableSoundgraphMonitoring = function () {
	var buf = new Buffer(4);
	buf[0] = 255;
	buf[1] = 255;
	buf[2] = 255;
	buf[3] = 2; // disable monitoring

	this.sp.write( buf , function (err, results) {
		if(err) return console.log('serial write err', err);
	});
};

Arduino.prototype.sendSettings = function (channel) {
	var buf = new Buffer(13);
	buf[0] = 255;
	buf[1] = 255;
	buf[2] = 255;
	buf[3] = 3; // update settings
	buf[4] = channel.id;
	buf.writeUInt16BE(channel.silenceThreshold, 5);
	buf.writeUInt16BE(channel.soundThreshold, 7);
	buf.writeUInt16BE(channel.nrOfConsecutiveSilenceSamplesBeforeFlippingToSilence, 9);
	buf.writeUInt16BE(channel.nrOfConsecutiveSoundSamplesBeforeFlippingToSound, 11);

	console.log('sending settings of channel ' + channel.id);

	this.sp.write( buf , function (err, results) {
		if(err) return console.log('serial error when sending settings for channel ' + channel.id, err);
	});
}

module.exports = Arduino;<commit_msg>fixed incoming data being parsed the wrong way<commit_after>// interface that talks to arduino using the serial port

var events = require('events');
var util = require('util');
var serialport = require('serialport');
var SerialPort = serialport.SerialPort;

// find serial ports:
// serialport.list(function (err, ports) {
//   ports.forEach(function(port) {
//     console.log(port.comName);
//     console.log(port.pnpId);
//     console.log(port.manufacturer);
//   });
// });
// return;

var serialportname = '/dev/ttyATH0';
if( process.env.NODE_ENV != 'production' ){
	var serialportname = '/dev/cu.usbmodemfa131'; // for debugging on local machine
}

// Serial:

// my own parser (waits for 3 times 255):
function ownparser () {
	var data = new Buffer(0);

	return function (emitter, buffer){
		// console.log('rawbuffer', buffer);

		data = Buffer.concat([data, buffer]);

		var checkfornewmessage = false;
		if(data.length > 0)
			checkfornewmessage = true;

		while(checkfornewmessage){
			checkfornewmessage = false;

			var startByteCounter = 0;
			var newMessageInTheMaking = false;
			for (var i = 0; i < data.length; i++) {
				if(data[i] == 255){
					startByteCounter++;
				}else{
					// first byte that's not 255 comes in
					// check if we have 3 start bytes:
					if(startByteCounter >=3){
						// good, this must a new message
						newMessageInTheMaking = true;
						break; // stop looking for other bytes:
					}else{
						// to bad, only 2 or less bytes where 255:
						startByteCounter = 0;
					}
				}
			}

			if(!newMessageInTheMaking) return; //wait some more until we have more bytes

			// find first occurrence of 255:
			var firstOccurenceOf255 = null;
			for (var i = 0; i < data.length; i++) {
				if(data[i] == 255){
					firstOccurenceOf255 = i;
					break;
				}
			}

			// cut of all data before this firstOccurenceOf255:
			data = data.slice(firstOccurenceOf255);

			// check if byte 4 is not 255:
			while(data[3] == 255){
				// cut off the buffer so that byte 4 is not 255:
				data = data.slice(1);
			}

			// now check for type of message:
			// 1 = soundstate
			// 2 = soundlevel
			// 3 = incomming settings

			var out;

			if( data[3] == 1 ){

				// console.log('should be soundstate');
				// we expect 2 more bytes to follow (so that's 6 bytes in total):
				if(data.length < 6) return; // wait for more bytes
				out = data.slice(0, 6);
				data = data.slice(6);

			} else if( data[3] == 2 ){

				// console.log('should be soundlevel');
				// we expect 3 more bytes to follow (so that's 7 bytes in total):
				if(data.length < 7) return; // wait for more bytes
				out = data.slice(0, 7);
				data = data.slice(7);

			} else if( data[3] == 3 ){

				// console.log('should be incomming settings');
				// we expect 9 more bytes to follow (so that's 13 bytes in total):
				if(data.length < 13) return; // wait for more bytes
				out = data.slice(0, 13);
				data = data.slice(13);

			}

			// emit those 6,7 or 13 bytes:
			emitter.emit('data', out);

			if(data.length>0){
				checkfornewmessage = true;
			}
		}
	};
}

Arduino = function(options){
	events.EventEmitter.call(this);

	var self = this;

	this.sp = new SerialPort(serialportname, {
		parser: ownparser(),
		baudrate: 9600
	});

	serialport.on("error", function (err) {
		console.log(err);
	});

	this.sp.on("open", function () {
		console.log('serialport is open');
	});

	this.sp.on('data', function (data) {
		// console.log(data);

		if(data[3] == 1){
			return parseSoundState(data);
		}

		if(data[3] == 2){
			return parseSoundLevel(data);
		}

		if(data[3] == 3){
			return parseSettings(data);
		}
	});

	this.sp.on("close", function () {
		console.log('serialport closed');
	});

	function parseSoundLevel (data) {
		var soundlevel = {
			channel: data[4],
			value: data.readUInt16BE(5) // last two bytes are a 16bit integer encoded big endian
		};
		self.emit("soundlevelChanged", soundlevel);
	}

	function parseSoundState (data) {
		var state = {
			channel: data[4],
			soundstate: (data[5] == 1) // boolean, true or false
		};
		self.emit("soundstateChanged", state);
	}

	function parseSettings (data) {
		var channel = {
			id: data[4],
			silenceThreshold: data.readUInt16BE(5),
			soundThreshold: data.readUInt16BE(7),
			nrOfConsecutiveSilenceSamplesBeforeFlippingToSilence: data.readUInt16BE(9),
			nrOfConsecutiveSoundSamplesBeforeFlippingToSound: data.readUInt16BE(11)
		}
		self.emit("incommingSettings", channel);
	}

	return this;
}

util.inherits(Arduino, events.EventEmitter);

// 'public' functions:
Arduino.prototype.enableSoundgraphMonitoring = function (channelid) {
	var buf = new Buffer(5);
	buf[0] = 255;
	buf[1] = 255;
	buf[2] = 255;
	buf[3] = 1; // enable monitoring
	buf[4] = channelid;

	this.sp.write( buf , function (err, results) {
		if(err) return console.log('serial write err', err);
	});
};

Arduino.prototype.disableSoundgraphMonitoring = function () {
	var buf = new Buffer(4);
	buf[0] = 255;
	buf[1] = 255;
	buf[2] = 255;
	buf[3] = 2; // disable monitoring

	this.sp.write( buf , function (err, results) {
		if(err) return console.log('serial write err', err);
	});
};

Arduino.prototype.sendSettings = function (channel) {
	var buf = new Buffer(13);
	buf[0] = 255;
	buf[1] = 255;
	buf[2] = 255;
	buf[3] = 3; // update settings
	buf[4] = channel.id;
	buf.writeUInt16BE(channel.silenceThreshold, 5);
	buf.writeUInt16BE(channel.soundThreshold, 7);
	buf.writeUInt16BE(channel.nrOfConsecutiveSilenceSamplesBeforeFlippingToSilence, 9);
	buf.writeUInt16BE(channel.nrOfConsecutiveSoundSamplesBeforeFlippingToSound, 11);

	console.log('sending settings of channel ' + channel.id);

	this.sp.write( buf , function (err, results) {
		if(err) return console.log('serial error when sending settings for channel ' + channel.id, err);
	});
}

module.exports = Arduino;<|endoftext|>


<commit_before>import { isArray } from 'substance'
import ValueComponent from './ValueComponent'
import MultiSelectInput from './MultiSelectInput'

export default class ManyRelationshipComponent extends ValueComponent {
  didMount () {
    // ATTENTION: relationships are unfortunately tricky regarding updates
    // obvious things are covered by the used helper, e.g., if the model is changed
    // or a one of the used targets has been removed
    // Other things are pretty much impossible to detect in a general way
    // e.g. the creation of a new target, or the deletion of an existing one
    // In this case the selection will be out of sync, and hopefully the implementation does react correctly
    // TODO: make sure that this is the case
    this.context.appState.addObserver(['document'], this._rerenderOnModelChangeIfNecessary, this, { stage: 'render' })
  }

  dispose () {
    this.context.appState.removeObserver(this)
  }

  render ($$) {
    const label = this.getLabel('select-item') + ' ' + this.props.label
    const options = this.getAvailableOptions()
    let selected = this._getSelectedOptions(options)
    let el = $$('div').addClass('sc-many-relationship')
    if (this.context.editable) {
      el.append(
        $$(MultiSelectInput, {
          label,
          selected,
          overlayId: this.props.model.id
        })
      )
    } else {
      const selectedLabels = []
      // ATTENTION: doing this with a for loop as it can happen
      // that an item is undefined (if id is not avaiable)
      for (let item of selected) {
        if (item) {
          selectedLabels.push(item.toString())
        }
      }
      let label = selectedLabels.join('; ')
      el.addClass('sm-readonly').append(label)
    }
    return el
  }

  getActionHandlers () {
    return {
      toggleOption: this._toggleTarget,
      toggleOverlay: this._toggleOverlay
    }
  }

  getAvailableOptions () {
    return this.props.model.getAvailableOptions()
  }

  _getSelectedOptions (options) {
    // pick all selected items from options this makes life easier for the MutliSelectComponent
    // because it does not need to map via ids, just can check equality
    let targetIds = this.props.model.getValue()
    let selected = targetIds.map(id => options.find(item => item.id === id)).filter(Boolean)
    return selected
  }

  _toggleTarget (target) {
    if (this.context.editable) {
      this.props.model.toggleTarget(target)
    }
  }

  _toggleOverlay () {
    const appState = this.context.appState
    let overlayId = appState.overlayId
    let modelId = this.props.model.id
    if (overlayId === modelId) {
      this.getParent().send('toggleOverlay')
    } else {
      // ATTENTION: At the moment a reducer maps value selections to appState.overlayId
      // i.e. we must not call toggleOverlay
      // But if we decided to disable the reducer this would break if
      // we used the common implementation.
      // TODO: rethink this approach in general
      this.context.api.selectValue(this._getPath())
      appState.set('overlayId', modelId, 'propagateImmediately')
    }
  }

  _rerenderOnModelChangeIfNecessary (change) {
    let updateNeeded = Boolean(change.updated[this._getPath()])
    if (!updateNeeded) {
      let ids = this.props.model.getValue()
      if (ids) {
        if (!isArray(ids)) {
          ids = [ids]
        }
        for (let id of ids) {
          if (change.deleted[id] || change.updated[id]) {
            updateNeeded = true
            break
          }
        }
      }
    }
    if (updateNeeded) {
      this._rerenderOnModelChange()
    }
  }

  _rerenderOnModelChange () {
    // console.log('Rerendering RelationshipComponent because model has changed', this._getPath())
    this.rerender()
  }
}
<commit_msg>Fix ManyRelationshipComponent<commit_after>import { isArray } from 'substance'
import ValueComponent from './ValueComponent'
import MultiSelectInput from './MultiSelectInput'

export default class ManyRelationshipComponent extends ValueComponent {
  didMount () {
    // ATTENTION: relationships are unfortunately tricky regarding updates
    // obvious things are covered by the used helper, e.g., if the model is changed
    // or a one of the used targets has been removed
    // Other things are pretty much impossible to detect in a general way
    // e.g. the creation of a new target, or the deletion of an existing one
    // In this case the selection will be out of sync, and hopefully the implementation does react correctly
    // TODO: make sure that this is the case
    this.context.appState.addObserver(['document'], this._rerenderOnModelChangeIfNecessary, this, { stage: 'render' })
  }

  dispose () {
    this.context.appState.removeObserver(this)
  }

  render ($$) {
    const label = this.getLabel('select-item') + ' ' + this.props.label
    const options = this.getAvailableOptions()
    let selected = this._getSelectedOptions(options)
    let el = $$('div').addClass('sc-many-relationship')
    if (this.context.editable) {
      el.append(
        $$(MultiSelectInput, {
          label,
          selected,
          overlayId: this.props.model.id
        })
      )
    } else {
      const selectedLabels = []
      // ATTENTION: doing this with a for loop as it can happen
      // that an item is undefined (if id is not avaiable)
      for (let item of selected) {
        if (item) {
          selectedLabels.push(item.toString())
        }
      }
      let label = selectedLabels.join('; ')
      el.addClass('sm-readonly').append(label)
    }
    return el
  }

  getActionHandlers () {
    return {
      toggleOption: this._toggleTarget,
      toggleOverlay: this._toggleOverlay
    }
  }

  getAvailableOptions () {
    return this.props.model.getAvailableOptions()
  }

  _getSelectedOptions (options) {
    // pick all selected items from options this makes life easier for the MutliSelectComponent
    // because it does not need to map via ids, just can check equality
    let targetIds = this.props.model.getValue()
    let selected = targetIds.map(id => options.find(item => item.id === id)).filter(Boolean)
    return selected
  }

  _toggleTarget (target) {
    if (this.context.editable) {
      this.props.model.toggleTarget(target)
    }
  }

  _toggleOverlay () {
    const appState = this.context.appState
    let overlayId = appState.overlayId
    let modelId = this.props.model.id
    if (overlayId === modelId) {
      this.getParent().send('toggleOverlay')
    } else {
      // ATTENTION: At the moment a reducer maps value selections to appState.overlayId
      // i.e. we must not call toggleOverlay
      // But if we decided to disable the reducer this would break if
      // we used the common implementation.
      // TODO: rethink this approach in general
      this.context.api.selectValue(this._getPath())
      // DO NOT UNCOMMENT THIS LINE
      // appState.set('overlayId', modelId, 'propagateImmediately')
    }
  }

  _rerenderOnModelChangeIfNecessary (change) {
    let updateNeeded = Boolean(change.updated[this._getPath()])
    if (!updateNeeded) {
      let ids = this.props.model.getValue()
      if (ids) {
        if (!isArray(ids)) {
          ids = [ids]
        }
        for (let id of ids) {
          if (change.deleted[id] || change.updated[id]) {
            updateNeeded = true
            break
          }
        }
      }
    }
    if (updateNeeded) {
      this._rerenderOnModelChange()
    }
  }

  _rerenderOnModelChange () {
    // console.log('Rerendering RelationshipComponent because model has changed', this._getPath())
    this.rerender()
  }
}
<|endoftext|>


<commit_before>'use strict';

/**
 * @file
 * Configure main routes
 * Includes utility routes (example: /moreinfo)
 */

import {isEmpty} from 'lodash';
import express from 'express';
const MainRoutes = express.Router();
import React from 'react';
import ReactDOM from 'react-dom/server';

import {stringToObject} from '../../utils/QueryParser.util.js';

// loading components
// import SearchServer from '../../client/components/searchpage/Search.server.js';
import Search from '../../client/components/searchpage/SearchPageLayout.component.js';
import {defaultLikes} from '../../client/components/Recommend/Recommendations.store.js';
import FrontpageContainer from '../../client/components/FrontPage/FrontpageContainer.component.js';

import dbcMiddleware from './../middlewares/middleware.js';

function getRecommendations(res, likes) {
  res.callServiceProvider('getRecommendations', {likes: likes, dislikes: []}, 50).then((recommendations) => {
    // got recommendations
    const content = ReactDOM.renderToString(<FrontpageContainer application='mobilsoeg' recommendations={recommendations} />);
    res.render('frontpage', {
      frontpageContainerString: content,
      recommendations: JSON.stringify(recommendations)
    });
  }, () => {
    // error occurred (most likely a timeout hit)
    res.render('frontpage');
  });
}

MainRoutes.get('/', (req, res) => {
  if (req.isAuthenticated()) {
    // user is logged in, get profile.
    res.callServiceProvider('findMobilSoegProfile', null, 400).then((profile) => {
      let likes = profile[0].body.likes.map((like) => {
        return like.item_id;
      });

      likes = likes.length > 0 ? likes : defaultLikes;
      // got profile, get recommendations
      getRecommendations(res, likes);
    }, () => {
      res.render('frontpage');
    });
  }
  else {
    getRecommendations(res, defaultLikes);
  }
});

MainRoutes.get(['/search', '/search/*'], (req, res) => {
  res.render('search', {
    props: JSON.stringify({query: stringToObject(req.query || []) || []})
  });
});

MainRoutes.post(['/report-violation'], function(req, res) {
  const logger = req.app.get('logger');
  if (req.body) {
    logger.log('warning', 'CSP Violation: ', req.body);
  }
  else {
    logger.log('warning', 'CSP Violation: No data received!');
  }
  res.status(204).end();
});

export default MainRoutes;
<commit_msg>bug-1234: remove ssr errors<commit_after>'use strict';

/**
 * @file
 * Configure main routes
 * Includes utility routes (example: /moreinfo)
 */

import express from 'express';
const MainRoutes = express.Router();
import React from 'react';
import ReactDOM from 'react-dom/server';

import {stringToObject} from '../../utils/QueryParser.util.js';

// loading components
import {defaultLikes} from '../../client/components/Recommend/Recommendations.store.js';
import FrontpageContainer from '../../client/components/FrontPage/FrontpageContainer.component.js';

function getRecommendations(res, likes) {
  res.callServiceProvider('getRecommendations', {likes: likes, dislikes: []}, 50).then((recommendations) => {
    // got recommendations
    const content = ReactDOM.renderToString(<FrontpageContainer application='mobilsoeg' recommendations={recommendations} />);
    res.render('frontpage', {
      frontpageContainerString: content,
      recommendations: JSON.stringify(recommendations)
    });
  }, () => {
    // error occurred (most likely a timeout hit)
    res.render('frontpage');
  });
}

MainRoutes.get('/', (req, res) => {
  if (req.isAuthenticated()) {
    // user is logged in, get profile.
    res.callServiceProvider('findMobilSoegProfile', null, 400).then((profile) => {
      let likes = profile[0].body.likes.map((like) => {
        return like.item_id;
      });

      likes = likes.length > 0 ? likes : defaultLikes;
      // got profile, get recommendations
      getRecommendations(res, likes);
    }, () => {
      res.render('frontpage');
    });
  }
  else {
    getRecommendations(res, defaultLikes);
  }
});

MainRoutes.get(['/search', '/search/*'], (req, res) => {
  res.render('search', {
    props: JSON.stringify({query: stringToObject(req.query || []) || []})
  });
});

MainRoutes.post(['/report-violation'], function(req, res) {
  const logger = req.app.get('logger');
  if (req.body) {
    logger.log('warning', 'CSP Violation: ', req.body);
  }
  else {
    logger.log('warning', 'CSP Violation: No data received!');
  }
  res.status(204).end();
});

export default MainRoutes;
<|endoftext|>


<commit_before>// @flow

import superagent from 'superagent';

const methods = ['get', 'post', 'put', 'patch', 'del'];

function formatUrl(path) {
  const adjustedPath = path[0] !== '/' ? `/${path}` : path;
  if (SERVER) {
    return `http://${process.env.APIHOST || 'localhost'}:3000${adjustedPath}`;
  }
  // Prepend `/api` to relative URL, to proxy to API server.
  return adjustedPath;
}

export default class ApiClient {
  constructor(req) {
    methods.forEach(method => (
      this[method] = (path, { params, data } = {}) => new Promise((resolve, reject) => {
        const request = superagent[method](formatUrl(path));

        if (params) {
          request.query(params);
        }

        if (SERVER && req.headers.cookie) {
          request.set('cookie', req.headers.cookie);
        }

        if (data) {
          request.send(data);
        }

        request.end((err, { body } = {}) => (err ? reject(body || err) : resolve(body)));
      })
    ));
  }
  static empty() {}
}
<commit_msg>adjust the server port in production<commit_after>// @flow

import superagent from 'superagent';

const methods = ['get', 'post', 'put', 'patch', 'del'];

// redundant to server/server.js
let port;
if (process.env.PORT) {
  port = process.env.port;
} else if (DEVELOPMENT) {
  port = 3000;
} else {
  port = 8080;
}

function formatUrl(path) {
  const adjustedPath = path[0] !== '/' ? `/${path}` : path;
  if (SERVER) {
    return `http://${process.env.HOST || 'localhost'}:${port}${adjustedPath}`;
  }
  // Prepend `/api` to relative URL, to proxy to API server.
  return adjustedPath;
}

export default class ApiClient {
  constructor(req) {
    methods.forEach(method => (
      this[method] = (path, { params, data } = {}) => new Promise((resolve, reject) => {
        const request = superagent[method](formatUrl(path));

        if (params) {
          request.query(params);
        }

        if (SERVER && req.headers.cookie) {
          request.set('cookie', req.headers.cookie);
        }

        if (data) {
          request.send(data);
        }

        request.end((err, { body } = {}) => (err ? reject(body || err) : resolve(body)));
      })
    ));
  }
  static empty() {}
}
<|endoftext|>


<commit_before>'use strict';

const check = require('check-types');
const pageToGenerator = require('./utils/pageToGenerator');

/**
 * Provides a set of common funcitonality that can be used on most endpoints
 * @param {object} context The context to make requests in. Basically, `this`
 * @param {string} endpoint The endpoint used for this common module
 */
module.exports = function common(context, endpoint) {

    /**
     * Retrieve resource object by ID
     * @param {string} id ID of the object to retrieve
     * @param {object} userOpts option overrides for this request
     * @returns Returns a promise that resolves with the retrieved object
     */
    function get(id, userOpts) {
        check.string(id, 'id must be a string');

        return context.http.makeRequest({
            url: `/v1/apps/${context.applicationId}/${endpoint}/${id}`
        }, userOpts);
    }

    /**
     * Retrieve all objects, returned as an iterator
     * @param {Object} [userOpts] option overrides for this request
     * @return An iterator that returns promises that resolve with the next object
     */
    function* getIterator(userOpts) {
        function pageFn() {
            let url = `/v1/apps/${context.applicationId}/${endpoint}`;
            return function() {
                return context.http.makeRequest({ url }, userOpts).then(function(body) {
                    url = body.pages.next;
                    return body;
                });
            };
        }

        yield* pageToGenerator(pageFn());
    }

    /**
     * Retrieve all objects, returned as an array
     * @param {Object} [userOpts] option overrides for this request
     * @return A promise that resolves to an array of objects
     */
    function getAll(userOpts) {
        let array = [];
        let url = `/v1/apps/${context.applicationId}/${endpoint}`;

        function pageFn() {
            return context.http.makeRequest({ url }, userOpts).then(function(body) {
                array = array.concat(body.data || []); // concatinate the new data

                url = body.pages.next;
                if (url) {
                    return pageFn();
                } else {
                    return array;
                }
            });
        }

        return pageFn();
    }

    /**
     * Updates a resource by ID
     * @param {string} [id] ID of the object to be updated
     * @param {Array<object>} [updates] JSON patch updates
     * @param {Object} [userOpts] option overrides for this request
     * @returns {Promise<Object>} A promise that resolves to the updated object
     */
    function update(id, updates, userOpts) {
        check.string(id, 'id must be a string');
        check.array(updates, 'updates must be an array');

        return context.http.makeRequest({
            method: 'PATCH',
            body: updates,
            url: `/v1/apps/${context.applicationId}/${endpoint}/${id}`
        }, userOpts);
    }

    /**
     * Create a object
     * @param {Object} [object] Sub-resource to object to create
     * @param {Object} [userOpts] option overrides for this request
     * @returns {Promise<Object>} A promise that resolves to the provided object
     */
    function create(object, userOpts) {
        check.object(object, 'object must be an object');

        return context.http.makeRequest({
            method: 'POST',
            body: object,
            url: `/v1/apps/${context.applicationId}/${endpoint}`
        }, userOpts);
    }

    /**
     * Delete a object by ID
     * @param {string} [id] ID of the object to delete
     * @param {Object} [userOpts] option overrides for this request
     * @returns {Promise<Object>} A promise that resolves to the deleted object
     */
    function remove(id, userOpts) {
        check.string(id, 'id must be a string');

        return context.http.makeRequest({
            method: 'DELETE',
            url: `/v1/apps/${context.applicationId}/${endpoint}/${id}`
        }, userOpts);
    }

    return {
        get: get,
        getIterator,
        getAll,
        create,
        update,
        remove
    };
};
<commit_msg>fixing comment docs [ci skip]<commit_after>'use strict';

const check = require('check-types');
const pageToGenerator = require('./utils/pageToGenerator');

/**
 * Provides a set of common funcitonality that can be used on most endpoints
 * @param {object} context The context to make requests in. Basically, `this`
 * @param {string} endpoint The endpoint used for this common module
 */
module.exports = function common(context, endpoint) {

    /**
     * Retrieve resource object by ID
     * @param {string} id ID of the object to retrieve
     * @param {object} userOpts option overrides for this request
     * @returns Returns a promise that resolves with the retrieved object
     */
    function get(id, userOpts) {
        check.string(id, 'id must be a string');

        return context.http.makeRequest({
            url: `/v1/apps/${context.applicationId}/${endpoint}/${id}`
        }, userOpts);
    }

    /**
     * Retrieve all objects, returned as an iterator
     * @param {Object} userOpts option overrides for this request
     * @return An iterator that returns promises that resolve with the next object
     */
    function* getIterator(userOpts) {
        function pageFn() {
            let url = `/v1/apps/${context.applicationId}/${endpoint}`;
            return function() {
                return context.http.makeRequest({ url }, userOpts).then(function(body) {
                    url = body.pages.next;
                    return body;
                });
            };
        }

        yield* pageToGenerator(pageFn());
    }

    /**
     * Retrieve all objects, returned as an array
     * @param {Object} userOpts option overrides for this request
     * @return A promise that resolves to an array of objects
     */
    function getAll(userOpts) {
        let array = [];
        let url = `/v1/apps/${context.applicationId}/${endpoint}`;

        function pageFn() {
            return context.http.makeRequest({ url }, userOpts).then(function(body) {
                array = array.concat(body.data || []); // concatinate the new data

                url = body.pages.next;
                if (url) {
                    return pageFn();
                } else {
                    return array;
                }
            });
        }

        return pageFn();
    }

    /**
     * Updates a resource by ID
     * @param {string} id ID of the object to be updated
     * @param {Array<object>} updates JSON patch updates
     * @param {Object} userOpts option overrides for this request
     * @returns {Promise<Object>} A promise that resolves to the updated object
     */
    function update(id, updates, userOpts) {
        check.string(id, 'id must be a string');
        check.array(updates, 'updates must be an array');

        return context.http.makeRequest({
            method: 'PATCH',
            body: updates,
            url: `/v1/apps/${context.applicationId}/${endpoint}/${id}`
        }, userOpts);
    }

    /**
     * Create a object
     * @param {Object} object Sub-resource to object to create
     * @param {Object} userOpts option overrides for this request
     * @returns {Promise<Object>} A promise that resolves to the provided object
     */
    function create(object, userOpts) {
        check.object(object, 'object must be an object');

        return context.http.makeRequest({
            method: 'POST',
            body: object,
            url: `/v1/apps/${context.applicationId}/${endpoint}`
        }, userOpts);
    }

    /**
     * Delete a object by ID
     * @param {string} id ID of the object to delete
     * @param {Object} userOpts option overrides for this request
     * @returns {Promise<Object>} A promise that resolves to the deleted object
     */
    function remove(id, userOpts) {
        check.string(id, 'id must be a string');

        return context.http.makeRequest({
            method: 'DELETE',
            url: `/v1/apps/${context.applicationId}/${endpoint}/${id}`
        }, userOpts);
    }

    return {
        get: get,
        getIterator,
        getAll,
        create,
        update,
        remove
    };
};
<|endoftext|>


<commit_before>var CommonData = {
  links: {
    home: 'https://watchout.tw/',
    support: 'https://watchout.tw/#support',
  },
  footer: {
    island: {
      image: 'asset/island.png',
    },
    social: [
      {
        type: 'facebook',
        image: 'https://graph.facebook.com/WatchoutTW/picture?type=large',
        message: '追蹤我們的最新動態！',
        link: 'https://facebook.com/WatchoutTW/',
      },
      {
        type: 'LINE',
        image: 'asset/goodfriend.png',
        message: '阿草好朋友',
        link: 'https://store.line.me/stickershop/product/1024870/zh-Hant',
      },
      {
        type: 'LINE',
        image: 'asset/goodcitizen.png',
        message: '阿草督督好',
        link: 'https://store.line.me/stickershop/product/1224270/zh-Hant',
      },
    ],
    other: [
      {
        title: '2014 透明報告',
        image: '',
        link: 'http://watchout.tw/transparency/2014',
      },
      {
        title: '歷年影響力報告',
        image: '',
        link: 'http://watchout.tw/impact/',
      },
      {
        title: '聯絡我們',
        image: '',
        link: 'http://watchout.tw/contact',
      },
      {
        title: '授權條款',
        image: '',
        link: 'http://watchout.tw/license',
      }
    ],
  },
};
var mxCommon = {
  props: {
    common: {
      type: Object,
      validator: function(data) {
        return true;
      }
    }
  },
};
Vue.component('w-footer', {
  mixins: [mxCommon],
  computed: {
    cptSocialGroups: function() {
      var arr = this.common.footer.social;
      var result = [];
      var types = {};
      for(var i = 0; i < arr.length; i++) {
        var cur = arr[i];
        if(!(cur.type in types)) {
          types[cur.type] = {type: cur.type, items: []};
          result.push(types[cur.type]);
        }
        types[cur.type].items.push(cur);
      }
      return result;
    },
  },
  template: `

{{ item.message }}

{{ item.title }}

  `
});
Vue.component('w-support', {
  mixins: [mxCommon],
  template: `

    ×

  `,
  methods: {
    close: function(event) {
      $(this.$el).hide();
    }
  }
});
<commit_msg>Use absolute path<commit_after>var CommonData = {
  links: {
    home: 'https://watchout.tw/',
    support: 'https://watchout.tw/#support',
  },
  footer: {
    island: {
      image: 'https://watchout.tw/asset/island.png',
    },
    social: [
      {
        type: 'facebook',
        image: 'https://graph.facebook.com/WatchoutTW/picture?type=large',
        message: '追蹤我們的最新動態！',
        link: 'https://facebook.com/WatchoutTW/',
      },
      {
        type: 'LINE',
        image: 'https://watchout.tw/asset/goodfriend.png',
        message: '阿草好朋友',
        link: 'https://store.line.me/stickershop/product/1024870/zh-Hant',
      },
      {
        type: 'LINE',
        image: 'https://watchout.tw/asset/goodcitizen.png',
        message: '阿草督督好',
        link: 'https://store.line.me/stickershop/product/1224270/zh-Hant',
      },
    ],
    other: [
      {
        title: '2014 透明報告',
        image: '',
        link: 'http://watchout.tw/transparency/2014',
      },
      {
        title: '歷年影響力報告',
        image: '',
        link: 'http://watchout.tw/impact/',
      },
      {
        title: '聯絡我們',
        image: '',
        link: 'http://watchout.tw/contact',
      },
      {
        title: '授權條款',
        image: '',
        link: 'http://watchout.tw/license',
      }
    ],
  },
};
var mxCommon = {
  props: {
    common: {
      type: Object,
      validator: function(data) {
        return true;
      }
    }
  },
};
Vue.component('w-footer', {
  mixins: [mxCommon],
  computed: {
    cptSocialGroups: function() {
      var arr = this.common.footer.social;
      var result = [];
      var types = {};
      for(var i = 0; i < arr.length; i++) {
        var cur = arr[i];
        if(!(cur.type in types)) {
          types[cur.type] = {type: cur.type, items: []};
          result.push(types[cur.type]);
        }
        types[cur.type].items.push(cur);
      }
      return result;
    },
  },
  template: `

{{ item.message }}

{{ item.title }}

  `
});
Vue.component('w-support', {
  mixins: [mxCommon],
  template: `

    ×

  `,
  methods: {
    close: function(event) {
      $(this.$el).hide();
    }
  }
});
<|endoftext|>


<commit_before>'use strict';

module.exports = {
    'parserOptions': {
        'sourceType': 'module',
    },
    'plugins': [
        'prefer-import',
    ],
    'rules': {
        // Rule to control usage of strict mode directives
        // comment: code inside es6 modules is under strict mode by default, so it's not necessary
        'strict': [2, 'never'],
        // Enforce require() on top-level module scope
        // comment: since we are using es6 modules, one will be using import instead of require but we still allow using inline require
        'global-require': 0,
        // Rule to warn the use of import when the es6-modules addon is activated.
        // comment: This should be a warning instead of an error because there are still cases when we must use require.
        'prefer-import/prefer-import-over-require': [1, 'warning'],
    },
};
<commit_msg>fix: fix invalid `prefer-import/prefer-import-over-require`<commit_after>'use strict';

module.exports = {
    'parserOptions': {
        'sourceType': 'module',
    },
    'plugins': [
        'prefer-import',
    ],
    'rules': {
        // Rule to control usage of strict mode directives
        // comment: code inside es6 modules is under strict mode by default, so it's not necessary
        'strict': [2, 'never'],
        // Enforce require() on top-level module scope
        // comment: since we are using es6 modules, one will be using import instead of require but we still allow using inline require
        'global-require': 0,
        // Rule to warn the use of import when the es6-modules addon is activated.
        // comment: This should be a warning instead of an error because there are still cases when we must use require.
        'prefer-import/prefer-import-over-require': 1,
    },
};
<|endoftext|>


<commit_before>'use strict';

var mixin = require('./util/mixin');

var defaults = module.exports = {

    name: 'PPMiniCart',

    parent: (typeof document !== 'undefined') ? document.body : null,

    action: 'https://www.paypal.com/cgi-bin/webscr',

    target: '',

    duration: 30,

    template: '$TEMPLATE$',

    styles: '$STYLES$',

    strings: {
        button: 'Check Out with ',
        subtotal: 'Subtotal:',
        discount: 'Discount:',
        processing: 'Processing...'
    }

};

/**
 * Mixes in the user config with the default config.
 *
 * @param {object} userConfig Configuration overrides
 * @return {object}
 */
module.exports.load = function load(userConfig) {
    return mixin(defaults, userConfig);
};
<commit_msg>Using cdnjs button image for HTTP/S support; #149<commit_after>'use strict';

var mixin = require('./util/mixin');

var defaults = module.exports = {

    name: 'PPMiniCart',

    parent: (typeof document !== 'undefined') ? document.body : null,

    action: 'https://www.paypal.com/cgi-bin/webscr',

    target: '',

    duration: 30,

    template: '$TEMPLATE$',

    styles: '$STYLES$',

    strings: {
        button: 'Check Out with ',
        subtotal: 'Subtotal:',
        discount: 'Discount:',
        processing: 'Processing...'
    }

};

/**
 * Mixes in the user config with the default config.
 *
 * @param {object} userConfig Configuration overrides
 * @return {object}
 */
module.exports.load = function load(userConfig) {
    return mixin(defaults, userConfig);
};
<|endoftext|>


<commit_before>var path = require('path')

module.exports = {
  host: process.env.HOST || 'localhost',
  port: process.env.PORT || (process.env.NODE_ENV === 'production' ? 8080 : 3000),
  rootPath: path.resolve(__dirname, '..'),
  assetsPath: path.resolve(__dirname, '../static/dist'),

  app: {
    title: 'Universal React Redux Starter Kit',
    description: 'A starter kit for building universal React/Redux apps',
    head: {
      titleTemplate: '%s | Universal React Redux Starter Kit',
      meta: [
        {
          name: 'description',
          content: 'A starter kit for building universal React/Redux apps'
        },
        { charset: 'utf-8' }
      ]
    }
  },

  devServer: {
    port: 1000
  },

  proxyTable: {}
}
<commit_msg>refactor: config<commit_after>var path = require('path')

module.exports = {
  host: process.env.HOST || 'localhost',
  port: process.env.PORT || 8080,

  rootPath: path.resolve(__dirname, '..'),
  assetsPath: path.resolve(__dirname, '../static/dist'),

  app: {
    title: 'Universal React Redux Starter Kit',
    description: 'A starter kit for building universal React/Redux apps',
    head: {
      titleTemplate: '%s | Universal React Redux Starter Kit',
      meta: [
        {
          name: 'description',
          content: 'A starter kit for building universal React/Redux apps'
        },
        { charset: 'utf-8' }
      ]
    }
  },

  devServer: {
    host: 'localhost',
    port: 3000
  },

  proxyTable: {}
}
<|endoftext|>