<commit_before>// ==UserScript==
// @name         ProgrammersForum Save Threads
// @namespace    programmersforum.ru
// @version      1.5.0
// @description  adds exportThreads function to export the specified threads, and loadThreadsList to get IDs of all threads in the specified category
// @author       Alex P
// @include      *programmersforum.ru/*
// @require      https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js
// @require      https://unpkg.com/axios/dist/axios.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/jszip/3.2.2/jszip.min.js
// @require      https://cdn.jsdelivr.net/npm/file-saver@2.0.2/dist/FileSaver.min.js
// @grant        none
// @downloadURL  https://github.com/AlexP11223/ProgForumRuUserscripts/raw/master/pf_save_threads.user.js
// ==/UserScript==

(function () {
    'use strict';

    function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    function parseHtml(html) {
        return new DOMParser().parseFromString(html, 'text/html')
    }

    function parseThread(html) {
        const htmlDoc = parseHtml(html);

        const pageCountBar = htmlDoc.querySelector('.pagenav .vbmenu_control');

        return {
            head: htmlDoc.head.outerHTML,
            content: htmlDoc.querySelector('#posts').innerHTML,
            title: htmlDoc.querySelector('.navbar strong').textContent.trim(),
            categories: Array.from(htmlDoc.querySelectorAll('.navbar a[href^=forumdisplay]')).map(el => el.textContent.trim()),
            pageCount: pageCountBar ? parseInt(pageCountBar.textContent.split(' ')[3]) : 1,
        };
    }

    function threadUrl(threadId) {
        return `/showthread.php?t=${threadId}`;
    }

    function generateThreadPagesUrls(threadId, pageCount) {
        const firstPageUrl = threadUrl(threadId);
        return [firstPageUrl, ..._.range(2, pageCount + 1, 1).map(n => `${firstPageUrl}&page=${n}`)];
    }

    function loadImageBase64(url) {
        console.log(`Loading ${url}`);
        return axios.get(url, {responseType: 'arraybuffer'})
            .then(response => `data:${response.headers['content-type']};base64,${btoa(String.fromCharCode(...new Uint8Array(response.data)))}`);
    }

    function loadFile(url) {
        console.log(`Loading ${url}`);
        return axios.get(url, {responseType: 'blob'})
            .then(response => response.data);
    }

    const imgCache = new Map();

    async function replaceRemoteImages(html) {
        const htmlDoc = parseHtml(html);

        const imgs = [
            ...htmlDoc.querySelectorAll('a[href^="member.php"] img[src*="u="]'),
            ...htmlDoc.querySelectorAll('img[src*="attachmentid="]'),
            ...htmlDoc.querySelectorAll('img[src^="images/smilies/"]'),
        ];
        const imgUrls = imgs.map(img => img.src);

        for (const imgUrl of imgUrls) {
            if (!imgCache.has(imgUrl)) {
                try {
                    imgCache.set(imgUrl, await loadImageBase64(imgUrl));
                } catch (e) {
                    console.error(e);
                }
            }
        }

        for (const img of imgs) {
            const localUrl = imgCache.get(img.src);
            if (localUrl) {
                img.src = localUrl;
            }
        }

        return htmlDoc.body.innerHTML;
    }

    function getLocalAttachmentPath(attachment) {
        return `attachments/${attachment.id} ${attachment.name}`;
    }

    async function loadAttachments(html) {
        const htmlDoc = parseHtml(html);

        const attachmentLinks = [
            ...htmlDoc.body.querySelectorAll('a[href*="attachmentid"]:not([rel])'),
        ];

        let attachments = [];
        for (const link of attachmentLinks) {
            await sleep(_.random(1000, 3000));

            try {
                const id = new URL(link.href, location.origin).searchParams.get('attachmentid');
                const attachment = {
                    id,
                    name: sanitizeFileName(link.textContent),
                    blob: await loadFile(link.href),
                };
                attachments.push(attachment);

                link.href = getLocalAttachmentPath(attachment);
            } catch (e) {
                console.error(e);
            }
        }

        return [htmlDoc.body.innerHTML, attachments];
    }

    let cssCache = null;

    async function getCss() {
        if (!cssCache) {
            cssCache = '';
            for (const url of ['/clientscript/vbulletin_important.css?v=3811', '/highlight/styles/programmersforum.css']) {
                console.log(`Loading ${url}`);
                try {
                    cssCache += await $.get(url) + '\n';
                } catch (e) {
                    console.error(e);
                }
            }
        }
        return cssCache;
    }

    function minifyJs(js) {
        return js
            .replace(/\/\*[\s\S]+\*\//g, '')
            .replace(/(\r\n|\n)+/g, '\n')
            .split('\n').map(s => s.trim()).join('\n');
    }

    let jsCache = null;

    async function getJs() {
        if (!jsCache) {
            jsCache = '';
            for (const url of ['/alex/video_embed.js', '/alex/code_highlighter.js']) {
                console.log(`Loading ${url}`);
                try {
                    jsCache += minifyJs(await $.get(url, null, null, 'text')) + '\n';
                } catch (e) {
                    console.error(e);
                }
            }
        }
        return jsCache;
    }

    async function loadThread(id) {
        console.log(`Loading ${threadUrl(id)}`);
        const firstPage = parseThread(await $.get(threadUrl(id)));

        console.log(`${firstPage.title} (${firstPage.pageCount} pages)`);

        await sleep(_.random(1000, 3000));

        let pages = [firstPage];
        for (const url of generateThreadPagesUrls(id, firstPage.pageCount).slice(1)) {
            await sleep(_.random(1000, 3000));

            console.log(`Loading ${url}`);
            pages.push(parseThread(await $.get(url)));
        }

        const head = pages[0].head.replace('windows-1251', 'utf-8');

        const postsHtml = `<div id="posts">${pages.map(p => p.content).join('')}</div>`;
        const postsHtmlWithImages = await replaceRemoteImages(postsHtml);
        const [postsHtmlWithImagesAndAttachments, attachments] = await loadAttachments(postsHtmlWithImages);

        return {
            id,
            title: firstPage.title,
            categories: firstPage.categories,
            pageCount: firstPage.pageCount,
            attachments,
            html: `<!DOCTYPE html>

${head}

${firstPage.categories.join(' - ')}

${firstPage.title}

${postsHtmlWithImagesAndAttachments}

`,
        };
    }

    function sanitizeFileName(input, replacement = ' ') {
        return _.truncate(input, {length: 100, omission: ''})
            .replace(/[\/\?<>\\:\*\|"]/g, replacement);
    }

    window.exportThreads = async function (...ids) {
        const zip = new JSZip();

        const ZIP_ROOT = 'programmersforum_export/';

        for (const id of ids) {
            const thread = await loadThread(id);

            const fileName = `${thread.id} ${sanitizeFileName(_.truncate(thread.title, {length: 100}))}.html`;
            const path = thread.categories.map(sanitizeFileName).join('/');
            zip.file(`${ZIP_ROOT}${path}/${fileName}`, thread.html);

            for (const attachment of thread.attachments) {
                zip.file(`${ZIP_ROOT}${path}/${getLocalAttachmentPath(attachment)}`, attachment.blob);
            }
        }

        const zipBlob = await zip.generateAsync({
            type: "blob",
            compression: "DEFLATE",
            compressionOptions: {
                level: 6
            }
        });
        saveAs(zipBlob, 'threads.zip');
    };

    window.loadThreadsList = async function (forumId) {
        await $.get('/archive/index.php?pda=1');

        const startUrl = `/archive/index.php/f-${forumId}.html`;

        console.log(`Loading ${startUrl}`);
        const firstPageHtmlDoc = parseHtml(await $.get(startUrl));

        const nextUrls = Array.from(firstPageHtmlDoc.body.querySelectorAll('a[href*="-p-"]')).map(a => a.href);

        let htmlDocs = [firstPageHtmlDoc];
        for (const url of nextUrls) {
            await sleep(_.random(1000, 3000));

            console.log(`Loading ${url}`);
            htmlDocs.push(parseHtml(await $.get(url)));
        }

        return _.flatten(htmlDocs.map(d => Array.from(d.body.querySelectorAll('li a')).map(a => a.href)))
            .map(url => url.split('-').slice(-1)[0].split('.')[0]);
    };
})();
<commit_msg>use custom hljs (delphi, asm)<commit_after>// ==UserScript==
// @name         ProgrammersForum Save Threads
// @namespace    programmersforum.ru
// @version      1.5.1
// @description  adds exportThreads function to export the specified threads, and loadThreadsList to get IDs of all threads in the specified category
// @author       Alex P
// @include      *programmersforum.ru/*
// @require      https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js
// @require      https://unpkg.com/axios/dist/axios.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/jszip/3.2.2/jszip.min.js
// @require      https://cdn.jsdelivr.net/npm/file-saver@2.0.2/dist/FileSaver.min.js
// @grant        none
// @downloadURL  https://github.com/AlexP11223/ProgForumRuUserscripts/raw/master/pf_save_threads.user.js
// ==/UserScript==

(function () {
    'use strict';

    function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    function parseHtml(html) {
        return new DOMParser().parseFromString(html, 'text/html')
    }

    function parseThread(html) {
        const htmlDoc = parseHtml(html);

        const pageCountBar = htmlDoc.querySelector('.pagenav .vbmenu_control');

        return {
            head: htmlDoc.head.outerHTML,
            content: htmlDoc.querySelector('#posts').innerHTML,
            title: htmlDoc.querySelector('.navbar strong').textContent.trim(),
            categories: Array.from(htmlDoc.querySelectorAll('.navbar a[href^=forumdisplay]')).map(el => el.textContent.trim()),
            pageCount: pageCountBar ? parseInt(pageCountBar.textContent.split(' ')[3]) : 1,
        };
    }

    function threadUrl(threadId) {
        return `/showthread.php?t=${threadId}`;
    }

    function generateThreadPagesUrls(threadId, pageCount) {
        const firstPageUrl = threadUrl(threadId);
        return [firstPageUrl, ..._.range(2, pageCount + 1, 1).map(n => `${firstPageUrl}&page=${n}`)];
    }

    function loadImageBase64(url) {
        console.log(`Loading ${url}`);
        return axios.get(url, {responseType: 'arraybuffer'})
            .then(response => `data:${response.headers['content-type']};base64,${btoa(String.fromCharCode(...new Uint8Array(response.data)))}`);
    }

    function loadFile(url) {
        console.log(`Loading ${url}`);
        return axios.get(url, {responseType: 'blob'})
            .then(response => response.data);
    }

    const imgCache = new Map();

    async function replaceRemoteImages(html) {
        const htmlDoc = parseHtml(html);

        const imgs = [
            ...htmlDoc.querySelectorAll('a[href^="member.php"] img[src*="u="]'),
            ...htmlDoc.querySelectorAll('img[src*="attachmentid="]'),
            ...htmlDoc.querySelectorAll('img[src^="images/smilies/"]'),
        ];
        const imgUrls = imgs.map(img => img.src);

        for (const imgUrl of imgUrls) {
            if (!imgCache.has(imgUrl)) {
                try {
                    imgCache.set(imgUrl, await loadImageBase64(imgUrl));
                } catch (e) {
                    console.error(e);
                }
            }
        }

        for (const img of imgs) {
            const localUrl = imgCache.get(img.src);
            if (localUrl) {
                img.src = localUrl;
            }
        }

        return htmlDoc.body.innerHTML;
    }

    function getLocalAttachmentPath(attachment) {
        return `attachments/${attachment.id} ${attachment.name}`;
    }

    async function loadAttachments(html) {
        const htmlDoc = parseHtml(html);

        const attachmentLinks = [
            ...htmlDoc.body.querySelectorAll('a[href*="attachmentid"]:not([rel])'),
        ];

        let attachments = [];
        for (const link of attachmentLinks) {
            await sleep(_.random(1000, 3000));

            try {
                const id = new URL(link.href, location.origin).searchParams.get('attachmentid');
                const attachment = {
                    id,
                    name: sanitizeFileName(link.textContent),
                    blob: await loadFile(link.href),
                };
                attachments.push(attachment);

                link.href = getLocalAttachmentPath(attachment);
            } catch (e) {
                console.error(e);
            }
        }

        return [htmlDoc.body.innerHTML, attachments];
    }

    let cssCache = null;

    async function getCss() {
        if (!cssCache) {
            cssCache = '';
            for (const url of ['/clientscript/vbulletin_important.css?v=3811', '/highlight/styles/programmersforum.css']) {
                console.log(`Loading ${url}`);
                try {
                    cssCache += await $.get(url) + '\n';
                } catch (e) {
                    console.error(e);
                }
            }
        }
        return cssCache;
    }

    function minifyJs(js) {
        return js
            .replace(/\/\*[\s\S]+\*\//g, '')
            .replace(/(\r\n|\n)+/g, '\n')
            .split('\n').map(s => s.trim()).join('\n');
    }

    let jsCache = null;

    async function getJs() {
        if (!jsCache) {
            jsCache = '';
            for (const url of ['/alex/video_embed.js', '/alex/code_highlighter.js']) {
                console.log(`Loading ${url}`);
                try {
                    jsCache += minifyJs(await $.get(url, null, null, 'text')) + '\n';
                } catch (e) {
                    console.error(e);
                }
            }
        }
        return jsCache;
    }

    async function loadThread(id) {
        console.log(`Loading ${threadUrl(id)}`);
        const firstPage = parseThread(await $.get(threadUrl(id)));

        console.log(`${firstPage.title} (${firstPage.pageCount} pages)`);

        await sleep(_.random(1000, 3000));

        let pages = [firstPage];
        for (const url of generateThreadPagesUrls(id, firstPage.pageCount).slice(1)) {
            await sleep(_.random(1000, 3000));

            console.log(`Loading ${url}`);
            pages.push(parseThread(await $.get(url)));
        }

        const head = pages[0].head.replace('windows-1251', 'utf-8');

        const postsHtml = `<div id="posts">${pages.map(p => p.content).join('')}</div>`;
        const postsHtmlWithImages = await replaceRemoteImages(postsHtml);
        const [postsHtmlWithImagesAndAttachments, attachments] = await loadAttachments(postsHtmlWithImages);

        return {
            id,
            title: firstPage.title,
            categories: firstPage.categories,
            pageCount: firstPage.pageCount,
            attachments,
            html: `<!DOCTYPE html>

${head}

${firstPage.categories.join(' - ')}

${firstPage.title}

${postsHtmlWithImagesAndAttachments}

`,
        };
    }

    function sanitizeFileName(input, replacement = ' ') {
        return _.truncate(input, {length: 100, omission: ''})
            .replace(/[\/\?<>\\:\*\|"]/g, replacement);
    }

    window.exportThreads = async function (...ids) {
        const zip = new JSZip();

        const ZIP_ROOT = 'programmersforum_export/';

        for (const id of ids) {
            const thread = await loadThread(id);

            const fileName = `${thread.id} ${sanitizeFileName(_.truncate(thread.title, {length: 100}))}.html`;
            const path = thread.categories.map(sanitizeFileName).join('/');
            zip.file(`${ZIP_ROOT}${path}/${fileName}`, thread.html);

            for (const attachment of thread.attachments) {
                zip.file(`${ZIP_ROOT}${path}/${getLocalAttachmentPath(attachment)}`, attachment.blob);
            }
        }

        const zipBlob = await zip.generateAsync({
            type: "blob",
            compression: "DEFLATE",
            compressionOptions: {
                level: 6
            }
        });
        saveAs(zipBlob, 'threads.zip');
    };

    window.loadThreadsList = async function (forumId) {
        await $.get('/archive/index.php?pda=1');

        const startUrl = `/archive/index.php/f-${forumId}.html`;

        console.log(`Loading ${startUrl}`);
        const firstPageHtmlDoc = parseHtml(await $.get(startUrl));

        const nextUrls = Array.from(firstPageHtmlDoc.body.querySelectorAll('a[href*="-p-"]')).map(a => a.href);

        let htmlDocs = [firstPageHtmlDoc];
        for (const url of nextUrls) {
            await sleep(_.random(1000, 3000));

            console.log(`Loading ${url}`);
            htmlDocs.push(parseHtml(await $.get(url)));
        }

        return _.flatten(htmlDocs.map(d => Array.from(d.body.querySelectorAll('li a')).map(a => a.href)))
            .map(url => url.split('-').slice(-1)[0].split('.')[0]);
    };
})();
<|endoftext|>


<commit_before>function action(worker, on, action, selector, original_color, highlight_color, params, on_success) {
    var url = '';
    if(on) {
        url = '/cmpp/personnes/gestion/' + worker + '/holidays/' + on + '/' + action + '/';
        $(selector).attr('style', 'background: ' + highlight_color);
    } else {
        url = '/cmpp/personnes/gestion/' + worker + '/holidays/' + action;
    }
    $("#holiday-dlg").load(url,
                           function() {
                               $(this).dialog({title: params.title,
                                               width: params.width,
                                               buttons: [{text: params.button_close,
                                                          click: function() {
                                                              $(this).dialog('close');
                                                              $(selector).attr('style', original_color);
                                                          }},{text: params.button_confirm,
                                                              click: function(){
                                                                  $.ajax({url: url,
                                                                          type: 'post',
                                                                          data: $('#holiday-dlg form').serialize()
                                                                         }).done(on_success)
                                                              }}]});
                           })
};

function add_holiday(worker) {
    params = {'title': 'Ajouter une absence', 'button_close': 'Fermer',
              'button_confirm': 'Ajouter', 'width': '500px'};

    on_success = function(response, status, xhr) {
        var content_type = xhr.getResponseHeader('content-type');
      if(content_type.indexOf('html') > -1) {
            $('#holiday-dlg form').html(response);
        } else {
            if(!response.err) {
                var result = response.content;
                var id = response.id;
                var li = $("

");
                var ul = $('

');
                $.each(result, function(i, e) {
                    console.log(e);
                    var item = $('
' + e[1] + '
');
                    ul.append(item);
                });
                var button_edit = $('<button class="icon-edit" data-action="edit" data-id="' + id + '"></button>');
                var button_delete = $('<button class="icon-remove" data-action="delete" data-id="' + id + '"></button>');
                var actions = $('

');
                actions.append(button_edit, button_delete);
                ul.append(actions);
                li.append(ul);
                $("#holidays").append(li);
                $("#holiday-dlg").dialog("close");
            }
        }
    };
    action(worker, null, 'ajouter', null, null, null, params, on_success);
};

function delete_holiday(worker, holiday) {
    var selector = '#' + holiday + ' ul';
    var initial_color = $(selector).attr('style');
    var params = {'title': 'Supprimer une absence',
                  'button_close': 'Non', 'button_confirm': 'Oui',
                  'width': '450px'}

    on_success = function(response) {
        if(!response.err) {
            $('#' +  holiday).remove();
            $(selector).attr('style', initial_color);
            $("#holiday-dlg").dialog("close");
        }
    };
    action(worker, holiday, 'supprimer', selector, initial_color, '#f8b0b0', params, on_success);
};

function edit_holiday(worker, holiday) {
    var selector = '#' + holiday + ' ul';
    var initial_color = $(selector).attr('style');
    params = {'title': 'Éditer une absence',
              'button_close': 'Fermer', 'button_confirm': 'Mettre à jour',
              'width': '500px'}

    on_success = function(response, status, xhr) {
        var content_type = xhr.getResponseHeader('content-type');
        if(content_type.indexOf('html') > -1) {
            $('#holiday-dlg form').html(response);
        } else {
            if(!response.err) {
                $.each(response.content, function(i, e) {
                    $('#holidays #' + holiday + ' li.' + e[0]).html(e[1]);
                });
                $(selector).attr('style', initial_color);
                $("#holiday-dlg").dialog("close");
            }
        }}
    action(worker, holiday, 'editer', selector, initial_color,  '#af7', params, on_success);
};<commit_msg>Handling more properly json responses<commit_after>function action(worker, on, action, selector, original_color, highlight_color, params, on_success) {
    var url = '';
    if(on) {
        url = '/cmpp/personnes/gestion/' + worker + '/holidays/' + on + '/' + action + '/';
        $(selector).attr('style', 'background: ' + highlight_color);
    } else {
        url = '/cmpp/personnes/gestion/' + worker + '/holidays/' + action;
    }
    $("#holiday-dlg").load(url,
                           function() {
                               $(this).dialog({title: params.title,
                                               width: params.width,
                                               buttons: [{text: params.button_close,
                                                          click: function() {
                                                              $(this).dialog('close');
                                                              $(selector).attr('style', original_color);
                                                          }},{text: params.button_confirm,
                                                              click: function(){
                                                                  $.ajax({url: url,
                                                                          type: 'post',
                                                                          data: $('#holiday-dlg form').serialize()
                                                                         }).done(on_success)
                                                              }}]});
                           })
};

function add_holiday(worker) {
    params = {'title': 'Ajouter une absence', 'button_close': 'Fermer',
              'button_confirm': 'Ajouter', 'width': '500px'};

    on_success = function(response) {
        try {
            $.parseJSON(response);
            if(!response.err) {
                var result = response.content;
                var id = response.id;
                var li = $("

");
                var ul = $('

');
                $.each(result, function(i, e) {
                    var item = $('
' + e[1] + '
');
                    ul.append(item);
                });
                var button_edit = $('<button class="icon-edit" data-action="edit" data-id="' + id + '"></button>');
                var button_delete = $('<button class="icon-remove" data-action="delete" data-id="' + id + '"></button>');
                var actions = $('

');
                actions.append(button_edit, button_delete);
                ul.append(actions);
                li.append(ul);
                $("#holidays").append(li);
                $("#holiday-dlg").dialog("close");
            }
        } catch(e) {
            $('#holiday-dlg form').html(response);
        }

    };
    action(worker, null, 'ajouter', null, null, null, params, on_success);
};

function delete_holiday(worker, holiday) {
    var selector = '#' + holiday + ' ul';
    var initial_color = $(selector).attr('style');
    var params = {'title': 'Supprimer une absence',
                  'button_close': 'Non', 'button_confirm': 'Oui',
                  'width': '450px'}

    on_success = function(response) {
        if(!response.err) {
            $('#' +  holiday).remove();
            $(selector).attr('style', initial_color);
            $("#holiday-dlg").dialog("close");
        }
    };
    action(worker, holiday, 'supprimer', selector, initial_color, '#f8b0b0', params, on_success);
};

function edit_holiday(worker, holiday) {
    var selector = '#' + holiday + ' ul';
    var initial_color = $(selector).attr('style');
    params = {'title': 'Éditer une absence',
              'button_close': 'Fermer', 'button_confirm': 'Mettre à jour',
              'width': '500px'}

    on_success = function(response) {
        try {
            $.parseJSON(response);
            if(!response.err) {
                $.each(response.content, function(i, e) {
                    $('#holidays #' + holiday + ' li.' + e[0]).html(e[1]);
                });
                $(selector).attr('style', initial_color);
                $("#holiday-dlg").dialog("close");
            }
        } catch(e) {
            console.log(e);
            $('#holiday-dlg form').html(response);
        }

    }
    action(worker, holiday, 'editer', selector, initial_color,  '#af7', params, on_success);
};<|endoftext|>


<commit_before>enyo.kind({
	name: "Phobos",
	classes: "enyo-unselectable",
	components: [
		{kind: "Analyzer", onIndexReady: "indexReady"},
		//{name: "db", kind: "PackageDb", onFinish: "dbReady"},
		//{name: "db", kind: "PackageDb", onFinish: "dbReady"},
		{kind: "DragAvatar", components: [
			{tag: "img", src: "images/icon.png"}
		]},
		{kind: "FittableRows", classes: "enyo-fit", Xstyle: "padding: 10px;", components: [
			{kind: "onyx.Toolbar", layoutKind: "FittableColumnsLayout", Xstyle: "margin: 10px;", components: [
				{kind: "onyx.Button", content: "Close", ontap: "closeDocAction"},
				{name: "documentLabel", content: "Document"},
				{kind: "onyx.Button", content: "Save", ontap: "saveDocAction"},
				{fit: true},
				{kind: "onyx.Button", content: "Designer", ontap: "designerAction"}
			]},
			{name: "body", fit: true, kind: "FittableColumns", Xstyle: "padding-bottom: 10px;", components: [
				{name: "left", classes: "panel", showing: false, components: [
				]},
				{name: "middle", fit: true, classes: "panel", components: [
					{classes: "border panel enyo-fit", style: "margin: 8px;", components: [
						{kind: "Ace", classes: "enyo-fit", style: "margin: 4px;"}
					]}
				]},
				{name: "right", classes: "panel", components: [
					// neccesary nesting here for 'margin: 8px;"
					{kind: "enyo.Scroller", classes: "border panel enyo-fit", style: "margin: 8px;", components: [
						{kind: "onyx.Button", content: "Reparse", ontap: "reparseAction"},
						{name: "dump", style: "padding: 10px;", allowHtml: true}
					]}
				]}
			]}
		]},
		{name: "waitPopup", kind: "onyx.Popup", centered: true, floating: true, autoDismiss: false, modal: true, style: "text-align: center; padding: 20px;", components: [
			{kind: "Image", src: "$phobos/images/save-spinner.gif", style: "width: 54px; height: 55px;"},
			{name: "waitPopupMessage", content: "Saving document...", style: "padding-top: 10px;"}
		]}
	],
	handlers: {
	},
	create: function() {
		this.inherited(arguments);
		this.buildDb();
	},
	//
	//
	saveDocAction: function() {
		this.showWaitPopup("Saving document...");
		this.bubble("onSaveDocument", {content: this.$.ace.getValue()});
	},
	saveComplete: function() {
		this.hideWaitPopup();
	},
	beginOpenDoc: function() {
		this.showWaitPopup("Opening document...");
	},
	openDoc: function(inCode, inExt) {
		this.hideWaitPopup();
		var mode = {json: "json", js: "javascript", html: "html", css: "css"}[inExt] || "text";
		this.$.ace.setEditingMode(mode);
		this.$.ace.setValue(inCode);
		this.reparseAction();
	},
	showWaitPopup: function(inMessage) {
		this.$.waitPopupMessage.setContent(inMessage);
		this.$.waitPopup.show();
	},
	hideWaitPopup: function() {
		this.$.waitPopup.hide();
	},
	//
	//
	buildDb: function() {
		this.$.analyzer.analyze(["$enyo/source", "$lib/layout", "$lib/onyx"]);
	},
	indexReady: function() {
		this.testDb();
	},
	testDb: function() {
		//var c = this.$.db.findByName("enyo.Control");
		//var c = this.$.db.findByName("onyx.Button");
		var c = this.$.analyzer.index.findByName("enyo.FittableRows");
		this.dumpInfo(c);
	},
	dumpInfo: function(inObject) {
		var c = inObject;
		if (!c) {
			this.$.dump.setContent("(no info)");
			return;
		}
		//
		var h$ = "
" + c.name + "
";
		//
		var h = [];
		for (var i=0, p; p=c.superkinds[i]; i++) {
			h.push(p);
		}
		h$ += "
Extends
" + "

" + h.join("

") + "

";
		//
		var h = [];
		for (var i=0, p; p=c.components[i]; i++) {
			h.push(p.name);
		}
		if (h.length) {
			h$ += "
Components
" + "

" + h.join("

") + "

";
		}
		//
		h = [];
		for (var i=0, p; p=c.properties[i]; i++) {
			h.push(p.name);
		}
		h$ += "
Properties
" + "

" + h.join("

") + "

";
		//
		h = [];
		for (var i=0, p; p=c.allProperties[i]; i++) {
			h.push(p.name);
		}
		h$ += "
All Properties
" + "

" + h.join("

") + "

";
		//
		this.$.dump.setContent(h$);
	},
	reparseAction: function() {
		var module = {
			name: "Document",
			code: this.$.ace.getValue()
		};
		this.$.analyzer.index.indexModule(module);
		// ad hoc: dump the first object, if it exists
		this.dumpInfo(module.objects && module.objects[0]);
	},
	designerAction: function() {
		// TODO: Crib more of this from Ares2v1
		var c = this.$.ace.getValue();
		var module = {
			name: "Document",
			code: c
		};
		this.$.analyzer.index.indexModule(module);
		var o = module.objects && module.objects[0];
		var comps = o.components;
		var start = o.components[0].start;
		var end=c.lastIndexOf("]")+1;
		var js = eval("([\n"+c.substring(start, end)+")");
		this.bubble("onDesignDocument", {content: js});
	},
	closeDocAction: function(inSender, inEvent) {
		this.bubble("onCloseDocument", {});
	}
});

<commit_msg>Warn on close, if the document is modified.<commit_after>enyo.kind({
	name: "Phobos",
	classes: "enyo-unselectable",
	components: [
		{kind: "Analyzer", onIndexReady: "indexReady"},
		//{name: "db", kind: "PackageDb", onFinish: "dbReady"},
		//{name: "db", kind: "PackageDb", onFinish: "dbReady"},
		{kind: "DragAvatar", components: [
			{tag: "img", src: "images/icon.png"}
		]},
		{kind: "FittableRows", classes: "enyo-fit", Xstyle: "padding: 10px;", components: [
			{kind: "onyx.Toolbar", layoutKind: "FittableColumnsLayout", Xstyle: "margin: 10px;", components: [
				{kind: "onyx.Button", content: "Close", ontap: "closeDocAction"},
				{name: "documentLabel", content: "Document"},
				{kind: "onyx.Button", content: "Save", ontap: "saveDocAction"},
				{fit: true},
				{kind: "onyx.Button", content: "Designer", ontap: "designerAction"}
			]},
			{name: "body", fit: true, kind: "FittableColumns", Xstyle: "padding-bottom: 10px;", components: [
				{name: "left", classes: "panel", showing: false, components: [
				]},
				{name: "middle", fit: true, classes: "panel", components: [
					{classes: "border panel enyo-fit", style: "margin: 8px;", components: [
						{kind: "Ace", classes: "enyo-fit", style: "margin: 4px;", onChange: "changed"}
					]}
				]},
				{name: "right", classes: "panel", components: [
					// neccesary nesting here for 'margin: 8px;"
					{kind: "enyo.Scroller", classes: "border panel enyo-fit", style: "margin: 8px;", components: [
						{kind: "onyx.Button", content: "Reparse", ontap: "reparseAction"},
						{name: "dump", style: "padding: 10px;", allowHtml: true}
					]}
				]}
			]}
		]},
		{name: "waitPopup", kind: "onyx.Popup", centered: true, floating: true, autoDismiss: false, modal: true, style: "text-align: center; padding: 20px;", components: [
			{kind: "Image", src: "$phobos/images/save-spinner.gif", style: "width: 54px; height: 55px;"},
			{name: "waitPopupMessage", content: "Saving document...", style: "padding-top: 10px;"}
		]},
		{name: "savePopup", kind: "onyx.Popup", centered: true, floating: true, autoDismiss: false, modal: true, style: "text-align: center; padding: 20px;", components: [
			{name: "message", content: "Document was modified! Save it before closing?", style: "padding: 10px;"},
			{kind: "FittableColumns", components: [
				{kind: "onyx.Button", content: "Cancel", ontap: "cancelCloseAction"},
				{kind: "onyx.Button", content: "Don't Save", ontap: "abandonDocAction"},
			]}
		]}
	],
	handlers: {
	},
	docChanged: false,
	create: function() {
		this.inherited(arguments);
		this.buildDb();
	},
	//
	//
	saveDocAction: function() {
		this.showWaitPopup("Saving document...");
		this.bubble("onSaveDocument", {content: this.$.ace.getValue()});
	},
	saveComplete: function() {
		this.hideWaitPopup();
		this.docChanged=false;
	},
	beginOpenDoc: function() {
		this.showWaitPopup("Opening document...");
	},
	openDoc: function(inCode, inExt) {
		this.hideWaitPopup();
		var mode = {json: "json", js: "javascript", html: "html", css: "css"}[inExt] || "text";
		this.$.ace.setEditingMode(mode);
		this.$.ace.setValue(inCode);
		this.reparseAction();
		this.docChanged=false;
	},
	showWaitPopup: function(inMessage) {
		this.$.waitPopupMessage.setContent(inMessage);
		this.$.waitPopup.show();
	},
	hideWaitPopup: function() {
		this.$.waitPopup.hide();
	},
	//
	//
	buildDb: function() {
		this.$.analyzer.analyze(["$enyo/source", "$lib/layout", "$lib/onyx"]);
	},
	indexReady: function() {
		this.testDb();
	},
	testDb: function() {
		//var c = this.$.db.findByName("enyo.Control");
		//var c = this.$.db.findByName("onyx.Button");
		var c = this.$.analyzer.index.findByName("enyo.FittableRows");
		this.dumpInfo(c);
	},
	dumpInfo: function(inObject) {
		var c = inObject;
		if (!c) {
			this.$.dump.setContent("(no info)");
			return;
		}
		//
		var h$ = "
" + c.name + "
";
		//
		var h = [];
		for (var i=0, p; p=c.superkinds[i]; i++) {
			h.push(p);
		}
		h$ += "
Extends
" + "

" + h.join("

") + "

";
		//
		var h = [];
		for (var i=0, p; p=c.components[i]; i++) {
			h.push(p.name);
		}
		if (h.length) {
			h$ += "
Components
" + "

" + h.join("

") + "

";
		}
		//
		h = [];
		for (var i=0, p; p=c.properties[i]; i++) {
			h.push(p.name);
		}
		h$ += "
Properties
" + "

" + h.join("

") + "

";
		//
		h = [];
		for (var i=0, p; p=c.allProperties[i]; i++) {
			h.push(p.name);
		}
		h$ += "
All Properties
" + "

" + h.join("

") + "

";
		//
		this.$.dump.setContent(h$);
	},
	reparseAction: function() {
		var module = {
			name: "Document",
			code: this.$.ace.getValue()
		};
		this.$.analyzer.index.indexModule(module);
		// ad hoc: dump the first object, if it exists
		this.dumpInfo(module.objects && module.objects[0]);
	},
	designerAction: function() {
		// TODO: Crib more of this from Ares2v1
		var c = this.$.ace.getValue();
		var module = {
			name: "Document",
			code: c
		};
		this.$.analyzer.index.indexModule(module);
		var o = module.objects && module.objects[0];
		var comps = o.components;
		var start = o.components[0].start;
		var end=c.lastIndexOf("]")+1;
		var js = eval("([\n"+c.substring(start, end)+")");
		this.bubble("onDesignDocument", {content: js});
	},
	closeDocAction: function(inSender, inEvent) {
		if (this.docChanged) {
			this.$.savePopup.show();
		} else {
			this.bubble("onCloseDocument", {});
		}
	},
	// called when "Don't Save" is selected in save popup
	abandonDocAction: function(inSender, inEvent) {
		this.$.savePopup.hide();
		this.bubble("onCloseDocument", {});
	},
	// called when the "Cancel" is selected in save popup
	cancelCloseAction: function(inSender, inEvent) {
		this.$.savePopup.hide();
	},
	changed: function(inSender, inEvent) {
		this.docChanged=true;
	}
});

<|endoftext|>


<commit_before>'use strict';

require('codemirror/mode/javascript/javascript');
require('codemirror/addon/search/searchcursor');
require('codemirror/addon/dialog/dialog');
require('codemirror/addon/dialog/dialog.css');
require('codemirror/addon/search/search');
require('codemirror/addon/selection/mark-selection');
require('codemirror/lib/codemirror.css');
require('../../assets/theme/parallax.css');

const CodeMirror = require('codemirror');
require('./pbasic')(CodeMirror);

const keyExtension = require('./key-extension');
const consoleStore = require('../../src/stores/console');
const editorStore = require('../../src/stores/editor');
const fileStore = require('../../src/stores/file');
const { handleInput } = require('../../src/actions/editor');
const DocumentsStore = require('../../src/stores/documents');

const React = require('react');
const TransmissionBar = require('./transmission-bar');

function editor(app, opts, done){

  var codeEditor;
  var outputConsole;
  var transmission;

  function refreshConsole(){
    const { text } = consoleStore.getState();

    if(outputConsole){
      outputConsole.innerHTML = text;
      outputConsole.scrollTop = outputConsole.scrollHeight;
    }
  }

  consoleStore.listen(refreshConsole);

  var space = app.workspace;

  app.view('editor', function(el, cb){
    console.log('editor render');

    if(!codeEditor){
      let editorContainer = document.createElement('div');
      editorContainer.style.display = 'flex';
      editorContainer.style.flex = '1';
      editorContainer.style.flexDirection = 'column';
      editorContainer.setAttribute('id', 'editorContainer');
      el.appendChild(editorContainer);

      codeEditor = CodeMirror(editorContainer, {
        value: space.current.deref() || '',
        mode: 'pbasic',
        theme: 'parallax',
        lineNumbers: true
      });

      codeEditor.on('inputRead', handleInput);
      codeEditor.on('keyHandled', handleInput);

      codeEditor.setOption('styleSelectedText', true);
      codeEditor.setOption('tabSize', 2);
      codeEditor.setOption('extraKeys', {
        'Ctrl-Up': false,
        'Ctrl-Down': false,
        'Tab': false,
        'Shift-Tab': false
      });
      keyExtension.setup(app);
      editorStore.cm = codeEditor;
      fileStore.documents = new DocumentsStore(codeEditor);
    }

    if(!outputConsole){
      outputConsole = document.createElement('pre');
      outputConsole.style.height = '200px';
      outputConsole.style.boxShadow = 'inset 0 5px 10px -5px rgba(0, 0, 0, 0.26)';
      outputConsole.style.backgroundColor = 'white';
      outputConsole.style.padding = '10px';
      outputConsole.style.margin = '0';
      outputConsole.style.overflow = 'auto';
      outputConsole.style.whiteSpace = 'pre-wrap';
      el.appendChild(outputConsole);
    }
    if(!transmission) {
      transmission = document.createElement('div');
      el.appendChild(transmission);
      React.render(<TransmissionBar />, transmission);
    }

    cb();
  });

  space.updateContent(opts.initial, done);
}

module.exports = editor;
<commit_msg>never going to hit this<commit_after>'use strict';

require('codemirror/mode/javascript/javascript');
require('codemirror/addon/search/searchcursor');
require('codemirror/addon/dialog/dialog');
require('codemirror/addon/dialog/dialog.css');
require('codemirror/addon/search/search');
require('codemirror/addon/selection/mark-selection');
require('codemirror/lib/codemirror.css');
require('../../assets/theme/parallax.css');

const CodeMirror = require('codemirror');
require('./pbasic')(CodeMirror);

const keyExtension = require('./key-extension');
const consoleStore = require('../../src/stores/console');
const editorStore = require('../../src/stores/editor');
const fileStore = require('../../src/stores/file');
const { handleInput } = require('../../src/actions/editor');
const DocumentsStore = require('../../src/stores/documents');

const React = require('react');
const TransmissionBar = require('./transmission-bar');

function editor(app, opts, done){

  var codeEditor;
  var outputConsole;
  var transmission;

  function refreshConsole(){
    const { text } = consoleStore.getState();

    if(outputConsole){
      outputConsole.innerHTML = text;
      outputConsole.scrollTop = outputConsole.scrollHeight;
    }
  }

  consoleStore.listen(refreshConsole);

  var space = app.workspace;

  app.view('editor', function(el, cb){
    console.log('editor render');

    if(!codeEditor){
      let editorContainer = document.createElement('div');
      editorContainer.style.display = 'flex';
      editorContainer.style.flex = '1';
      editorContainer.style.flexDirection = 'column';
      editorContainer.setAttribute('id', 'editorContainer');
      el.appendChild(editorContainer);

      codeEditor = CodeMirror(editorContainer, {
        mode: 'pbasic',
        theme: 'parallax',
        lineNumbers: true
      });

      codeEditor.on('inputRead', handleInput);
      codeEditor.on('keyHandled', handleInput);

      codeEditor.setOption('styleSelectedText', true);
      codeEditor.setOption('tabSize', 2);
      codeEditor.setOption('extraKeys', {
        'Ctrl-Up': false,
        'Ctrl-Down': false,
        'Tab': false,
        'Shift-Tab': false
      });
      keyExtension.setup(app);
      editorStore.cm = codeEditor;
      fileStore.documents = new DocumentsStore(codeEditor);
    }

    if(!outputConsole){
      outputConsole = document.createElement('pre');
      outputConsole.style.height = '200px';
      outputConsole.style.boxShadow = 'inset 0 5px 10px -5px rgba(0, 0, 0, 0.26)';
      outputConsole.style.backgroundColor = 'white';
      outputConsole.style.padding = '10px';
      outputConsole.style.margin = '0';
      outputConsole.style.overflow = 'auto';
      outputConsole.style.whiteSpace = 'pre-wrap';
      el.appendChild(outputConsole);
    }
    if(!transmission) {
      transmission = document.createElement('div');
      el.appendChild(transmission);
      React.render(<TransmissionBar />, transmission);
    }

    cb();
  });

  space.updateContent(opts.initial, done);
}

module.exports = editor;
<|endoftext|>


<commit_before>// Call spamassassin via spamd

var sock = require('./line_socket');

var defaults = {
    spamd_socket: 'localhost:783',
    max_size:     500000,
    old_headers_action: "rename",
    subject_prefix: "*** SPAM ***",
};

exports.hook_data_post = function (next, connection) {
    var config = this.config.get('spamassassin.ini');
    var plugin = this;

    for (var key in defaults) {
        config.main[key] = config.main[key] || defaults[key];
    }

    ['reject_threshold', 'relay_reject_threshold',
     'munge_subject_threshold', 'max_size'].forEach(
        function (item) {
            if (config.main[item]) {
                config.main[item] = new Number(config.main[item]);
            }
        }
    );

    if (connection.transaction.data_bytes > config.main.max_size) {
        return next();
    }

    var socket = new sock.Socket();
    if (config.main.spamd_socket.match(/\//)) {
        // assume unix socket
        socket.connect(config.main.spamd_socket);
    }
    else {
        var hostport = config.main.spamd_socket.split(/:/);
        socket.connect(hostport[1], hostport[0]);
    }

    socket.setTimeout(300 * 1000);

    var username = config.main.spamd_user ||
                   connection.transaction.notes.spamd_user ||
                   'default';

    var data_marker = 0;
    var in_data = false;
    var end_pending = true;

    var send_data = function () {
        in_data = true;
        var wrote_all = true;
        while (wrote_all && (data_marker < connection.transaction.data_lines.length)) {
            var line = connection.transaction.data_lines[data_marker];
            data_marker++;
            // dot-stuffing not necessary for spamd
            wrote_all = socket.write(new Buffer(line, 'binary'));
            if (!wrote_all) return;
        }
        // we get here if wrote_all still true, and we got to end of data_lines
        if (end_pending) {
            end_pending = false;
            socket.end("\r\n");
        }
    };

    socket.on('drain', function () {
        connection.logdebug(plugin, 'drain');
        if (end_pending && in_data) {
            process.nextTick(function () { send_data() });
        }
    });

    socket.on('timeout', function () {
        connection.logerror(plugin, "spamd connection timed out");
        socket.end();
        next();
    });
    socket.on('error', function (err) {
        connection.logerror(plugin, "spamd connection failed: " + err);
        // we don't deny on error - maybe another plugin can deliver
        next();
    });
    socket.on('connect', function () {
        socket.write("SYMBOLS SPAMC/1.3\r\n", function () {
            socket.write("User: " + username + "\r\n\r\n", function () {
                socket.write("X-Envelope-From: " +
                    connection.transaction.mail_from.address()
                    + "\r\n", function () {
                        socket.write("X-Haraka-UUID: " +
                            connection.transaction.uuid + "\r\n", function () {
                                send_data();
                        });
                });
            });
        });
    });

    var spamd_response = {};
    var state = 'line0';

    socket.on('line', function (line) {
        connection.logprotocol(plugin, "Spamd C: " + line);
        line = line.replace(/\r?\n/, '');
        if (state === 'line0') {
            spamd_response.line0 = line;
            state = 'response';
        }
        else if (state === 'response') {
            if (line.match(/\S/)) {
                var matches;
                if (matches = line.match(/Spam: (True|False) ; (-?\d+\.\d) \/ (-?\d+\.\d)/)) {
                    spamd_response.flag = matches[1];
                    spamd_response.hits = matches[2];
                    spamd_response.reqd = matches[3];
                    spamd_response.flag = spamd_response.flag === 'True' ? 'Yes' : 'No'
                }
            }
            else {
                state = 'tests';
            }
        }
        else if (state === 'tests') {
            spamd_response.tests = line;
            socket.end();
        }
    });

    socket.on('end', function () {
        // Now we do stuff with the results...

        plugin.fixup_old_headers(config.main.old_headers_action, connection.transaction);

        if (spamd_response.flag === 'Yes') {
            connection.transaction.add_header('X-Spam-Flag', 'YES');
            connection.transaction.remove_header('precedence');
            connection.transaction.add_header('Precedence', 'junk');
        }
        connection.transaction.add_header('X-Spam-Status', spamd_response.flag +
            ', hits=' + spamd_response.hits + ' required=' + spamd_response.reqd +
            "\n\ttests=" + spamd_response.tests);

        var stars = Math.floor(spamd_response.hits);
        if (stars < 1) stars = 1;
        if (stars > 50) stars = 50;
        var stars_string = '';
        for (var i = 0; i < stars; i++) {
            stars_string += '*';
        }
        connection.transaction.add_header('X-Spam-Level', stars_string);

        connection.loginfo(plugin, "status=" + spamd_response.flag + ', hits=' +
            spamd_response.hits + ', required=' + spamd_response.reqd +
            ", reject=" + ((connection.relaying) ? (config.main.relay_reject_threshold || config.main.reject_threshold) :
                           config.main.reject_threshold) +
            ", tests=\"" + spamd_response.tests + "\"");

        if ((connection.relaying && config.main.relay_reject_threshold && (spamd_response.hits >= config.main.relay_reject_threshold))
           || (config.main.reject_threshold && (spamd_response.hits >= config.main.reject_threshold))) {
            return next(DENY, "spam score exceeded threshold");
        }
        else if (config.main.munge_subject_threshold && (spamd_response.hits >= config.main.munge_subject_threshold)) {
            var subj = connection.transaction.header.get('Subject');
            // Try and prevent any double subject modifications
            var subject_re = new RegExp('^' + config.main.subject_prefix);
            if (!subject_re.test(subj)) {
                connection.transaction.remove_header('Subject');
                connection.transaction.add_header('Subject', config.main.subject_prefix + " " + subj);
            }
        }
        next();
    });

};

exports.fixup_old_headers = function (action, transaction) {
    var headers = ['X-Spam-Flag', 'X-Spam-Status', 'X-Spam-Level'];

    switch (action) {
        case "keep": return;
        case "drop": for (var key in headers) { transaction.remove_header(key) }
                     break;
        case "rename":
        default:
                     for (var key in headers) {
                         var old_val = transaction.header.get(key);
                         if (old_val) {
                             transaction.header.remove_header(key);
                             transaction.header.add_header(key.replace(/X-/, 'X-Old-'), old_val);
                         }
                     }
                     break;
    }
}
<commit_msg>Improve spamassassin plugin<commit_after>// Call spamassassin via spamd

var sock = require('./line_socket');

var defaults = {
    spamd_socket: 'localhost:783',
    max_size:     500000,
    old_headers_action: "rename",
    subject_prefix: "*** SPAM ***",
};

exports.hook_data_post = function (next, connection) {
    var plugin = this;

    var config = this.config.get('spamassassin.ini');

    for (var key in defaults) {
        config.main[key] = config.main[key] || defaults[key];
    }

    ['reject_threshold', 'relay_reject_threshold',
     'munge_subject_threshold', 'max_size'].forEach(
        function (item) {
            if (config.main[item]) {
                config.main[item] = new Number(config.main[item]);
            }
        }
    );

    if (config.main.max_size > 0 &&
        connection.transaction.data_bytes > config.main.max_size)
    {
        connection.loginfo(this, 'skipping: message exceeds maximum size');
        return next();
    }

    var socket = new sock.Socket();
    if (config.main.spamd_socket.match(/\//)) {
        // assume unix socket
        socket.connect(config.main.spamd_socket);
    }
    else {
        var hostport = config.main.spamd_socket.split(/:/);
        socket.connect((hostport[1] || 783), hostport[0]);
    }

    socket.setTimeout(300 * 1000);

    var username = config.main.spamd_user ||
                   connection.transaction.notes.spamd_user ||
                   'default';

    var data_marker = 0;
    var in_data = false;
    var end_pending = true;

    // TODO: buffer writes into 64K chunks
    var send_data = function () {
        in_data = true;
        var wrote_all = true;
        while (wrote_all && (data_marker < connection.transaction.data_lines.length)) {
            var line = connection.transaction.data_lines[data_marker];
            data_marker++;
            // dot-stuffing not necessary for spamd
            wrote_all = socket.write(new Buffer(line, 'binary'));
            if (!wrote_all) return;
        }
        // we get here if wrote_all still true, and we got to end of data_lines
        if (end_pending) {
            end_pending = false;
            socket.end("\r\n");
        }
    };

    socket.on('drain', function () {
        connection.logdebug(plugin, 'drain');
        if (end_pending && in_data) {
            process.nextTick(function () { send_data() });
        }
    });

    socket.on('timeout', function () {
        connection.logerror(plugin, "spamd connection timed out");
        socket.end();
        next();
    });
    socket.on('error', function (err) {
        connection.logerror(plugin, "spamd connection failed: " + err);
        // we don't deny on error - maybe another plugin can deliver
        next();
    });
    socket.on('connect', function () {
        var headers = [
            'SYMBOLS SPAMC/1.3',
            'User: ' + username,
            '',
            'X-Envelope-From: ' + connection.transaction.mail_from.address(),
            'X-Haraka-UUID: ' + connection.transaction.uuid,
        ];
        if (connection.relaying) {
            headers.push('X-Haraka-Relay: true');
        }
        socket.write(headers.join("\r\n"), function () {
            send_data();
        });
    });

    var spamd_response = {};
    var state = 'line0';

    socket.on('line', function (line) {
        connection.logprotocol(plugin, "Spamd C: " + line);
        line = line.replace(/\r?\n/, '');
        if (state === 'line0') {
            spamd_response.line0 = line;
            state = 'response';
        }
        else if (state === 'response') {
            if (line.match(/\S/)) {
                var matches;
                if (matches = line.match(/Spam: (True|False) ; (-?\d+\.\d) \/ (-?\d+\.\d)/)) {
                    spamd_response.flag = matches[1];
                    spamd_response.hits = matches[2];
                    spamd_response.reqd = matches[3];
                    spamd_response.flag = spamd_response.flag === 'True' ? 'Yes' : 'No'
                }
            }
            else {
                state = 'tests';
            }
        }
        else if (state === 'tests') {
            spamd_response.tests = line;
            socket.end();
        }
    });

    socket.on('end', function () {
        // Now we do stuff with the results...

        plugin.fixup_old_headers(config.main.old_headers_action, connection.transaction);

        if (spamd_response.flag === 'Yes') {
            connection.transaction.add_header('X-Spam-Flag', 'YES');
            connection.transaction.remove_header('precedence');
            connection.transaction.add_header('Precedence', 'junk');
        }
        connection.transaction.add_header('X-Spam-Status', spamd_response.flag +
            ', hits=' + spamd_response.hits + ' required=' + spamd_response.reqd +
            "\n\ttests=" + spamd_response.tests);

        var stars = Math.floor(spamd_response.hits);
        if (stars < 1) stars = 1;
        if (stars > 50) stars = 50;
        var stars_string = '';
        for (var i = 0; i < stars; i++) {
            stars_string += '*';
        }
        connection.transaction.add_header('X-Spam-Level', stars_string);

        connection.loginfo(plugin, "status=" + spamd_response.flag + ', hits=' +
            spamd_response.hits + ', required=' + spamd_response.reqd +
            ", reject=" + ((connection.relaying) ? (config.main.relay_reject_threshold || config.main.reject_threshold) :
                           config.main.reject_threshold) +
            ", tests=\"" + spamd_response.tests + "\"");

        if ((connection.relaying && config.main.relay_reject_threshold && (spamd_response.hits >= config.main.relay_reject_threshold))
           || (config.main.reject_threshold && (spamd_response.hits >= config.main.reject_threshold))) {
            return next(DENY, "spam score exceeded threshold");
        }
        else if (config.main.munge_subject_threshold && (spamd_response.hits >= config.main.munge_subject_threshold)) {
            var subj = connection.transaction.header.get('Subject');
            // Try and prevent any double subject modifications
            var subject_re = new RegExp('^' + config.main.subject_prefix);
            if (!subject_re.test(subj)) {
                connection.transaction.remove_header('Subject');
                connection.transaction.add_header('Subject', config.main.subject_prefix + " " + subj);
            }
        }
        next();
    });

};

exports.fixup_old_headers = function (action, transaction) {
    var headers = ['X-Spam-Flag', 'X-Spam-Status', 'X-Spam-Level'];

    switch (action) {
        case "keep": return;
        case "drop": for (var key in headers) { transaction.remove_header(key) }
                     break;
        case "rename":
        default:
                     for (var key in headers) {
                         var old_val = transaction.header.get(key);
                         if (old_val) {
                             transaction.header.remove_header(key);
                             transaction.header.add_header(key.replace(/X-/, 'X-Old-'), old_val);
                         }
                     }
                     break;
    }
}
<|endoftext|>


<commit_before>$(document).ready(function(){
	$.getJSON("wpt2json.json?url={{ url|addslashes }}", function(geojson) {
		var map = new google.maps.Map($('#map').get(0), {
			disableDoubleClickZoom: true,
			mapTypeId: google.maps.MapTypeId.TERRAIN
		});
		var bounds = new google.maps.LatLngBounds(
			new google.maps.LatLng(geojson.bbox[1], geojson.bbox[0]),
			new google.maps.LatLng(geojson.bbox[4], geojson.bbox[3])
		);
		map.fitBounds(bounds);
		google.maps.event.addListener(map, 'dblclick', function () {
			map.fitBounds(bounds);
		});
		$.each(geojson.features, function(i, feature) {
			var color = 'ffff00';
			if (feature.properties.hasOwnProperty('color')) {
				color = feature.properties.color;
			}
			var elevation = 0;
			if (feature.geometry.coordinates.length > 2) {
				elevation = feature.geometry.coordinates[2];
			}
			var radius = 400;
			if (feature.properties.hasOwnProperty('radius')) {
				radius = feature.properties.radius;
			}
			var position = new google.maps.LatLng(
				feature.geometry.coordinates[1],
				feature.geometry.coordinates[0]
			);
			var richMarkerContent = $('#richMarker').clone().attr({id: null}).show();
			richMarkerContent.css('background-color', '#' + color);
			var label = feature.properties.id;
			var match = /^([A-Z][0-9]{2})([0-9]{3})$/.exec(feature.properties.id);
			if (match && 10 * (match[2] - 1) <= elevation && elevation <= 10 * (match[2] + 1)) {
				label = match[1];
			}
			$('#label', richMarkerContent).html(label);
			var marker = new RichMarker({
				anchor: RichMarkerPosition.MIDDLE,
				content: richMarkerContent.get(0),
				flat: true,
				map: map,
				position: position
			});
			var infoWindowContent = $('#infoWindow').clone().attr({id: null}).show();
			$('#id', infoWindowContent).html(feature.properties.id);
			$('#lat', infoWindowContent).html(feature.geometry.coordinates[1]);
			$('#lng', infoWindowContent).html(feature.geometry.coordinates[0]);
			$('#elevation', infoWindowContent).html(elevation);
			if (feature.properties.hasOwnProperty('description')) {
				$('#description', infoWindowContent).html(feature.properties.description);
			}
			$('#radius', infoWindowContent).html(radius);
			$('#color', infoWindowContent).html(color);
			var circle = new google.maps.Circle({
				center: position,
				clickable: false,
				fillColor: color,
				fillOpacity: 0.25,
				map: map,
				radius: radius,
				strokeColor: color,
				strokeOpacity: 1,
				strokeWeight: 1
			});
			var infoWindow = new google.maps.InfoWindow({content: infoWindowContent.get(0)});
			google.maps.event.addListener(marker, 'click', function () {
				infoWindow.open(map, marker);
			});
			google.maps.event.addListener(marker, 'dblclick', function () {
				map.panTo(position);
				map.setZoom(15);
			});
		});
	});
});
<commit_msg>Factor out Waypoint class and use MVCObjects<commit_after>function Waypoint(options) {

	this.set('id', options.id);
	this.set('position', options.position);
	this.set('elevation', options.elevation);
	this.set('description', options.description);
	this.set('radius', options.radius);
	this.set('color', options.color);
	this.set('map', options.map);

	var richMarkerContent = $('#richMarker').clone().attr({id: null}).show();
	richMarkerContent.css('background-color', '#' + options.color);
	var label = this.get('id');
	var match = /^([A-Z][0-9]{2})([0-9]{3})$/.exec(this.get('id'));
	if (match && 10 * (match[2] - 1) <= this.get('elevation') && this.get('elevation') <= 10 * (match[2] + 1)) {
		label = match[1];
	}
	$('#label', richMarkerContent).html(label);
	var marker = new RichMarker({
		anchor: RichMarkerPosition.MIDDLE,
		content: richMarkerContent.get(0),
		flat: true,
		map: this.get('map')
	});
	marker.bindTo('position', this);
	marker.bindTo('map', this);

	var circle = new google.maps.Circle({
		clickable: false,
		fillOpacity: 0.25,
		strokeOpacity: 1,
		strokeWeight: 1
	});
	circle.bindTo('center', this, 'position');
	circle.bindTo('fillColor', this, 'color');
	circle.bindTo('radius', this);
	circle.bindTo('strokeColor', this, 'color');
	circle.bindTo('map', this);

	var infoWindowContent = $('#infoWindow').clone().attr({id: null}).show();
	$('#id', infoWindowContent).html(this.get('id'));
	$('#lat', infoWindowContent).html(this.get('position').lat());
	$('#lng', infoWindowContent).html(this.get('position').lng());
	$('#elevation', infoWindowContent).html(this.get('elevation'));
	$('#description', infoWindowContent).html(this.get('description'));
	$('#radius', infoWindowContent).html(this.get('radius'));
	$('#color', infoWindowContent).html(this.get('color'));
	var infoWindow = new google.maps.InfoWindow({content: infoWindowContent.get(0)});

	google.maps.event.addListener(marker, 'click', function () {
		infoWindow.open(map, marker);
	});

	google.maps.event.addListener(marker, 'dblclick', function () {
		map.panTo(position);
		map.setZoom(15);
	});

}

Waypoint.prototype = new google.maps.MVCObject();

$(document).ready(function(){
	$.getJSON("wpt2json.json?url={{ url|addslashes }}", function(geojson) {
		var map = new google.maps.Map($('#map').get(0), {
			disableDoubleClickZoom: true,
			mapTypeId: google.maps.MapTypeId.TERRAIN
		});
		var bounds = new google.maps.LatLngBounds(
			new google.maps.LatLng(geojson.bbox[1], geojson.bbox[0]),
			new google.maps.LatLng(geojson.bbox[4], geojson.bbox[3])
		);
		map.fitBounds(bounds);
		google.maps.event.addListener(map, 'dblclick', function () {
			map.fitBounds(bounds);
		});
		$.each(geojson.features, function(i, feature) {
			var options = {
				color: 'ffff00',
				description: '',
				elevation: 0,
				id: feature.properties.id,
				map: map,
				position: new google.maps.LatLng(
					feature.geometry.coordinates[1],
					feature.geometry.coordinates[0]
				),
				radius: 400
			};
			if (feature.properties.hasOwnProperty('color')) {
				options.color = feature.properties.color;
			}
			if (feature.geometry.coordinates.length > 2) {
				options.elevation = feature.geometry.coordinates[2];
			}
			if (feature.properties.hasOwnProperty('radius')) {
				options.radius = feature.properties.radius;
			}
			var waypoint = new Waypoint(options);
		});
	});
});
<|endoftext|>


<commit_before>var AWS = require("aws-sdk");

var TABLE_NAME = "Computournament-Registry";
var ddb = new AWS.DynamoDB();

exports.handler = function(event, context) {

    var timestamp = Math.round((new Date()).getTime() / 1000);

    checkPendingChallenge(context, function(item) {

	item.Status.S = (event.solution === eval(item.Challenge.S)) ? "solved" : "failed";
	item.Enddtime.N = timestamp.toString();

	var params = {
	    Item: item,
	    TableName : TABLE_NAME
	};

	ddb.putItem(params, function(err, data) {
	    if (err) {
		context.fail(err);
	    }

	    var timeToSolve = timestamp - item.Starttime.N;
	    context.succeed( { timeToSolve: timeToSolve, result: item.Status.S  }  );
	});
    });
};

function checkPendingChallenge(context, cb) {

    var params = {
    	"TableName": TABLE_NAME,
    	"KeyConditionExpression": "CognitoId = :id",
    	"ExpressionAttributeValues": {
            ":id": {"S": context.identity.cognitoIdentityId }
    	},
	"Limit": 1,
	"ScanIndexForward": false
    };

    ddb.query(params, function(err, data) {
	if (err) {
	    context.fail(err);
	}

	if (data.Items[0].Status.S == "pending") {
	    cb(data.Items[0]);
	}
	else {
	    context.fail("No pending challenge available!");
	}
    });
}

<commit_msg>fixed item data access<commit_after>var AWS = require("aws-sdk");

var TABLE_NAME = "Computournament-Registry";
var ddb = new AWS.DynamoDB();

exports.handler = function(event, context) {

    var timestamp = Math.round((new Date()).getTime() / 1000);

    checkPendingChallenge(context, function(item) {

	item.Status.S = (event.solution === eval(item.Challenge.S)) ? "solved" : "failed";
	item.Enddtime = { N: timestamp.toString() };

	var params = {
	    Item: item,
	    TableName : TABLE_NAME
	};

	ddb.putItem(params, function(err, data) {
	    if (err) {
		context.fail(err);
	    }

	    var timeToSolve = timestamp - item.Starttime.N;
	    context.succeed( { timeToSolve: timeToSolve, result: item.Status.S  }  );
	});
    });
};

function checkPendingChallenge(context, cb) {

    var params = {
    	"TableName": TABLE_NAME,
    	"KeyConditionExpression": "CognitoId = :id",
    	"ExpressionAttributeValues": {
            ":id": {"S": context.identity.cognitoIdentityId }
    	},
	"Limit": 1,
	"ScanIndexForward": false
    };

    ddb.query(params, function(err, data) {
	if (err) {
	    context.fail(err);
	}

	if (data.Items[0].Status.S == "pending") {
	    cb(data.Items[0]);
	}
	else {
	    context.fail("No pending challenge available!");
	}
    });
}

<|endoftext|>


<commit_before>'use strict';

var homebridge;
var Characteristic;

module.exports = function(pHomebridge) {
	if (pHomebridge && !homebridge) {
		homebridge = pHomebridge;
		Characteristic = homebridge.hap.Characteristic;
    }

    var hexToBase64 = function(val) {
        return new Buffer((''+val).replace(/[^0-9A-F]/ig, ''), 'hex').toString('base64');
    }, base64ToHex = function(val) {
        if(!val) return val;
        return new Buffer(val, 'base64').toString('hex');
    }, swap16 = function (val) {
        return ((val & 0xFF) << 8)
            | ((val >>> 8) & 0xFF);
    }, swap32 = function (val) {
        return ((val & 0xFF) << 24)
           | ((val & 0xFF00) << 8)
           | ((val >>> 8) & 0xFF00)
           | ((val >>> 24) & 0xFF);
    },	hexToHPA = function(val) {
        return parseInt(swap16(val), 10);
    }, hPAtoHex = function(val) {
        return swap16(Math.round(val)).toString(16);
    }, numToHex = function(val, len) {
        var s = Number(val>>>0).toString(16);
        if(s.length % 2 != 0) {
            s = '0' + s;
        }
        if(len) {
            return ('0000000000000' + s).slice(-1 * len);
        }
    return s;
}

    class S2R1Characteristic extends Characteristic {
        constructor() {
            super('S2R1', 'E863F116-079E-48FF-8F27-9C2605A29F52');
            this.setProps({
                format: Characteristic.Formats.DATA,
                perms: [
                    Characteristic.Perms.READ, Characteristic.Perms.NOTIFY
                ]
            });
        }
    }

    class S2R2Characteristic extends Characteristic {
        constructor() {
            super('S2R2', 'E863F117-079E-48FF-8F27-9C2605A29F52');
            this.setProps({
                format: Characteristic.Formats.DATA,
                perms: [
                    Characteristic.Perms.READ, Characteristic.Perms.NOTIFY
                ]
            });
        }
    }

    class S2W1Characteristic extends Characteristic {
        constructor() {
            super('S2W1', 'E863F11C-079E-48FF-8F27-9C2605A29F52');
            this.setProps({
                format: Characteristic.Formats.DATA,
                perms: [
                    Characteristic.Perms.WRITE
                ]
            });
        }
    }

    class S2W2Characteristic extends Characteristic {
        constructor() {
            super('S2W2', 'E863F121-079E-48FF-8F27-9C2605A29F52');
            this.setProps({
                format: Characteristic.Formats.DATA,
                perms: [
                    Characteristic.Perms.WRITE
                ]
            });
        }
    }

    class FakeGatoHistoryService extends homebridge.hap.Service {
        constructor(accessoryType, accessory) {
            super(accessory.name + " History", 'E863F007-079E-48FF-8F27-9C2605A29F52');
            this.log = accessory.log;
            switch (accessoryType)
            {
                case "weather":
                    this.accessoryType116 = "03";
                    this.accessoryType117 = "07";
                    break;
                case "energy":
                    this.accessoryType116 = "07";
                    this.accessoryType117 = "1f";
                    break;
                case "room":
                    this.accessoryType116 = "04";
                    this.accessoryType117 = "0f";
                    break;
            }

            this.accessoryType=accessoryType;
            this.nextAvailableEntry = 1;
            this.history = [];
            this.maxHistory = 100;
            this.currentEntry = 1;
            this.transfer=false;
            this.setTime=true;
            this.refTime=0;

            this.addCharacteristic(S2R1Characteristic);

            this.addCharacteristic(S2R2Characteristic)
                .on('get', (callback) => {
                    if ((this.currentEntry<this.nextAvailableEntry) && (this.transfer==true))
                    {

                        if ((this.history[this.currentEntry].temp==0 &&
                            this.history[this.currentEntry].pressure==0 &&
                            this.history[this.currentEntry].humidity==0) || (this.history[this.currentEntry].power==0xFFFF) || (this.setTime==true))
                        {
                            this.log.debug("Data "+ this.accessoryType + ": 15" + numToHex(swap16(this.currentEntry),4) + "0000 0000 0000 81" + numToHex(swap32(this.refTime),8) +"0000 0000 00 0000");
                            callback(null,hexToBase64('15' + numToHex(swap16(this.currentEntry),4) +' 0000 0000 0000 81' + numToHex(swap32(this.refTime),8) + '0000 0000 00 0000'));
                            this.setTime=false;
                        }
                        else
                            switch (this.accessoryType)
                            {
                                case "weather":
                            	    this.log.debug("Data "+ this.accessoryType + ": 10 " + numToHex(swap16(this.currentEntry),4) + " 0000 "
                                            + numToHex(swap32(this.history[this.currentEntry].time-this.refTime-978307200),8)
                                            + this.accessoryType117
                                            + numToHex(swap16(this.history[this.currentEntry].temp*100),4)
                                            + numToHex(swap16(this.history[this.currentEntry].humidity*100),4)
                                            + numToHex(swap16(this.history[this.currentEntry].pressure*10),4));
                                    callback(null,hexToBase64('10' + numToHex(swap16(this.currentEntry),4)+ ' 0000 '
                                            + numToHex(swap32(this.history[this.currentEntry].time-this.refTime-978307200),8)
                                            + this.accessoryType117
                                            + numToHex(swap16(this.history[this.currentEntry].temp*100),4)
                                            + numToHex(swap16(this.history[this.currentEntry].humidity*100),4)
                                            + numToHex(swap16(this.history[this.currentEntry].pressure*10),4)));
                                    break;
                                case "energy":
                            	    this.log.debug("Data "+ this.accessoryType + ": 14 " + numToHex(swap16(this.currentEntry),4) + " 0000 "
                                            + numToHex(swap32(this.history[this.currentEntry].time-this.refTime-978307200),8)
                                            + this.accessoryType117
                                            + "0000 0000"
                                            + numToHex(swap16(this.history[this.currentEntry].power*10),4)
                                            + "0000 0000");
                                    callback(null,hexToBase64('14' + numToHex(swap16(this.currentEntry),4)+ ' 0000 '
                                            + numToHex(swap32(this.history[this.currentEntry].time-this.refTime-978307200),8)
                                            + this.accessoryType117
                                            + "0000 0000"
                                            + numToHex(swap16(this.history[this.currentEntry].power*10),4)
                                            + "0000 0000"));
                                    break;
                            }
                        this.currentEntry++;
                    }
                    else
                    {
                        this.transfer=false;
                        callback(null,hexToBase64('00'));
                    }

            });

            this.addCharacteristic(S2W1Characteristic)
                .on('set', this.setCurrentS2W1.bind(this));

            this.addCharacteristic(S2W2Characteristic)
                .on('set', this.setCurrentS2W2.bind(this));

        }

        sendHistory(address){
            var hexAddress= address.toString('16');
            if (address!=0)
                this.currentEntry = address;
            else
                this.currentEntry = 1;
            this.transfer=true;
        }

        //in order to be consistent with Eve, entry address start from 1
        addEntry(entry){
            if (this.nextAvailableEntry<this.maxHistory)
            {
                if (this.refTime==0)
                {
                    this.refTime=entry.time-978307200;
                    switch (this.accessoryType)
                        {
                            case "weather":
                                this.history[this.nextAvailableEntry]= {time: entry.time, temp:0, pressure:0, humidity:0};
                                break;
                            case "energy":
                                this.history[this.nextAvailableEntry]= {time: entry.time, power:0xFFFF};
                                break;
                        }
                    this.nextAvailableEntry++;
                }
                this.history[this.nextAvailableEntry] = (entry);
                this.nextAvailableEntry++;
            }
            else
            {
                this.history[1] = (entry);
                this.nextAvailableEntry = 2;
            }

            this.getCharacteristic(S2R1Characteristic)
                .setValue(hexToBase64(numToHex(swap32(entry.time-this.refTime-978307200),8) + '00000000' + numToHex(swap32(this.refTime),8) + '0401020202' + this.accessoryType116 +'020f03' + numToHex(swap16(this.nextAvailableEntry),4) +'ed0f00000000000000000101'));
            console.log("Next available entry " + this.accessoryType + ": " + this.nextAvailableEntry.toString(16));
            console.log("116 " + this.accessoryType + ": " + numToHex(swap32(entry.time-this.refTime-978307200),8) + '00000000' + numToHex(swap32(this.refTime),8) + '0401020202' + this.accessoryType116 +'020f03' + numToHex(swap16(this.nextAvailableEntry),4) +'ed0f00000000000000000101');

        }

        setCurrentS2W1(val, callback) {
            callback(null,val);
            this.log.debug("Data request " + this.accessoryType + ": "+ base64ToHex(val));
            var valHex = base64ToHex(val);
            var substring = valHex.substring(4,12);
            var valInt = parseInt(substring,16);
            var address = swap32(valInt);
            var hexAddress= address.toString('16');

            this.log.debug("Address requested " + this.accessoryType + ": "+ hexAddress);
            if (this.transfer==false)
            {
                //this.transfer=true;
                this.sendHistory(address);
            }
        }

        setCurrentS2W2(val, callback) {
            this.log.debug("Clock adjust: "+ base64ToHex(val));
            callback(null,val);
        }

    }

    return FakeGatoHistoryService;
}<commit_msg>working rolling memory using additional fields in 116 characteristics with memory size and first/last entry number<commit_after>'use strict';

var homebridge;
var Characteristic;

module.exports = function(pHomebridge) {
	if (pHomebridge && !homebridge) {
		homebridge = pHomebridge;
		Characteristic = homebridge.hap.Characteristic;
    }

    var hexToBase64 = function(val) {
        return new Buffer((''+val).replace(/[^0-9A-F]/ig, ''), 'hex').toString('base64');
    }, base64ToHex = function(val) {
        if(!val) return val;
        return new Buffer(val, 'base64').toString('hex');
    }, swap16 = function (val) {
        return ((val & 0xFF) << 8)
            | ((val >>> 8) & 0xFF);
    }, swap32 = function (val) {
        return ((val & 0xFF) << 24)
           | ((val & 0xFF00) << 8)
           | ((val >>> 8) & 0xFF00)
           | ((val >>> 24) & 0xFF);
    },	hexToHPA = function(val) {
        return parseInt(swap16(val), 10);
    }, hPAtoHex = function(val) {
        return swap16(Math.round(val)).toString(16);
    }, numToHex = function(val, len) {
        var s = Number(val>>>0).toString(16);
        if(s.length % 2 != 0) {
            s = '0' + s;
        }
        if(len) {
            return ('0000000000000' + s).slice(-1 * len);
        }
    return s;

}

    class S2R1Characteristic extends Characteristic {
        constructor() {
            super('S2R1', 'E863F116-079E-48FF-8F27-9C2605A29F52');
            this.setProps({
                format: Characteristic.Formats.DATA,
                perms: [
                    Characteristic.Perms.READ, Characteristic.Perms.NOTIFY
                ]
            });
        }
    }

    class S2R2Characteristic extends Characteristic {
        constructor() {
            super('S2R2', 'E863F117-079E-48FF-8F27-9C2605A29F52');
            this.setProps({
                format: Characteristic.Formats.DATA,
                perms: [
                    Characteristic.Perms.READ, Characteristic.Perms.NOTIFY
                ]
            });
        }
    }

    class S2W1Characteristic extends Characteristic {
        constructor() {
            super('S2W1', 'E863F11C-079E-48FF-8F27-9C2605A29F52');
            this.setProps({
                format: Characteristic.Formats.DATA,
                perms: [
                    Characteristic.Perms.WRITE
                ]
            });
        }
    }

    class S2W2Characteristic extends Characteristic {
        constructor() {
            super('S2W2', 'E863F121-079E-48FF-8F27-9C2605A29F52');
            this.setProps({
                format: Characteristic.Formats.DATA,
                perms: [
                    Characteristic.Perms.WRITE
                ]
            });
        }
    }

    class FakeGatoHistoryService extends homebridge.hap.Service {
        constructor(accessoryType, accessory) {
            super(accessory.name + " History", 'E863F007-079E-48FF-8F27-9C2605A29F52');

            var entry2address = function(val) {
                var temp = val % this.memorySize;
                return temp;
            }.bind(this);

            this.log = accessory.log;
            switch (accessoryType)
            {
                case "weather":
                    this.accessoryType116 = "03";
                    this.accessoryType117 = "07";
                    break;
                case "energy":
                    this.accessoryType116 = "07";
                    this.accessoryType117 = "1f";
                    break;
                case "room":
                    this.accessoryType116 = "04";
                    this.accessoryType117 = "0f";
                    break;
            }

            this.accessoryType=accessoryType;
            this.firstEntry = 0;
            this.lastEntry = 0;
            this.history = [];
            this.memorySize = 60;
            this.usedMemory=0;
            this.currentEntry = 1;
            this.transfer=false;
            this.setTime=true;
            this.refTime=0;
            this.memoryAddress=0;

            this.addCharacteristic(S2R1Characteristic);

            this.addCharacteristic(S2R2Characteristic)
                .on('get', (callback) => {
                    if ((this.currentEntry<this.lastEntry) && (this.transfer==true))
                    {
                        this.memoryAddress = entry2address (this.currentEntry);

                        if ((this.history[this.memoryAddress].temp==0 &&
                            this.history[this.memoryAddress].pressure==0 &&
                            this.history[this.memoryAddress].humidity==0) || (this.history[this.memoryAddress].power==0xFFFF) || (this.setTime==true))
                        {
                            this.log.debug("Data "+ this.accessoryType + ": 15" + numToHex(swap16(this.currentEntry),4) + "0000 0000 0000 81" + numToHex(swap32(this.refTime),8) +"0000 0000 00 0000");
                            callback(null,hexToBase64('15' + numToHex(swap16(this.currentEntry),4) +' 0000 0000 0000 81' + numToHex(swap32(this.refTime),8) + '0000 0000 00 0000'));
                            this.setTime=false;
                        }
                        else
                            switch (this.accessoryType)
                            {
                                case "weather":
                                    this.log.debug(this.accessoryType + " Entry: " + this.currentEntry + ", Address: " + this.memoryAddress);
                                    this.log.debug("Data "+ this.accessoryType + ": 10 " + numToHex(swap16(this.currentEntry),4) + " 0000 "
                                            + numToHex(swap32(this.history[this.memoryAddress].time-this.refTime-978307200),8)
                                            + this.accessoryType117
                                            + numToHex(swap16(this.history[this.memoryAddress].temp*100),4)
                                            + numToHex(swap16(this.history[this.memoryAddress].humidity*100),4)
                                            + numToHex(swap16(this.history[this.memoryAddress].pressure*10),4));
                                    callback(null,hexToBase64('10' + numToHex(swap16(this.currentEntry),4)+ ' 0000 '
                                            + numToHex(swap32(this.history[this.memoryAddress].time-this.refTime-978307200),8)
                                            + this.accessoryType117
                                            + numToHex(swap16(this.history[this.memoryAddress].temp*100),4)
                                            + numToHex(swap16(this.history[this.memoryAddress].humidity*100),4)
                                            + numToHex(swap16(this.history[this.memoryAddress].pressure*10),4)));
                                    break;
                                case "energy":
                                    this.log.debug(this.accessoryType + " Entry: " + this.currentEntry + ", Address: " + this.memoryAddress);
                                    this.log.debug("Data "+ this.accessoryType + ": 14 " + numToHex(swap16(this.currentEntry),4) + " 0000 "
                                            + numToHex(swap32(this.history[this.memoryAddress].time-this.refTime-978307200),8)
                                            + this.accessoryType117
                                            + "0000 0000"
                                            + numToHex(swap16(this.history[this.memoryAddress].power*10),4)
                                            + "0000 0000");
                                    callback(null,hexToBase64('14' + numToHex(swap16(this.currentEntry),4)+ ' 0000 '
                                            + numToHex(swap32(this.history[this.memoryAddress].time-this.refTime-978307200),8)
                                            + this.accessoryType117
                                            + "0000 0000"
                                            + numToHex(swap16(this.history[this.memoryAddress].power*10),4)
                                            + "0000 0000"));
                                    break;
                            }
                        this.currentEntry++;
                    }
                    else
                    {
                        this.transfer=false;
                        callback(null,hexToBase64('00'));
                    }

            });

            this.addCharacteristic(S2W1Characteristic)
                .on('set', this.setCurrentS2W1.bind(this));

            this.addCharacteristic(S2W2Characteristic)
                .on('set', this.setCurrentS2W2.bind(this));

        }

        sendHistory(address){
            var hexAddress= address.toString('16');
            if (address!=0)
                this.currentEntry = address;
            else
                this.currentEntry = 1;
            this.transfer=true;
        }

        //in order to be consistent with Eve, entry address start from 1
        addEntry(entry){

            var entry2address = function(val) {
                return val % this.memorySize;
            }.bind(this);

            if (this.usedMemory<this.memorySize)
            {
                this.usedMemory++;
                this.firstEntry=0;
                this.lastEntry=this.usedMemory;
            }
            else
            {
                this.firstEntry++;
                this.lastEntry = this.firstEntry+this.usedMemory;
            }

            if (this.refTime==0)
                {
                    this.refTime=entry.time-978307200;
                    switch (this.accessoryType)
                        {
                            case "weather":
                                this.history[this.lastEntry]= {time: entry.time, temp:0, pressure:0, humidity:0};
                                break;
                            case "energy":
                                this.history[this.lastEntry]= {time: entry.time, power:0xFFFF};
                                break;
                        }
                    this.lastEntry++;
                    this.usedMemory++;
                }

            this.history[entry2address(this.lastEntry)] = (entry);

            this.getCharacteristic(S2R1Characteristic)
                .setValue(hexToBase64(numToHex(swap32(entry.time-this.refTime-978307200),8) + '00000000' + numToHex(swap32(this.refTime),8) + '0401020202' + this.accessoryType116 +'020f03' + numToHex(swap16(this.usedMemory),4) + numToHex(swap16(this.memorySize),4) + numToHex(swap32(this.firstEntry),8) + '000000000101'));
            this.log.debug("First entry " + this.accessoryType + ": " + this.firstEntry.toString(16));
            this.log.debug("Last entry " + this.accessoryType + ": " + this.lastEntry.toString(16));
            this.log.debug("Used memory " + this.accessoryType + ": " + this.usedMemory.toString(16));
            this.log.debug("116 " + this.accessoryType + ": " + numToHex(swap32(entry.time-this.refTime-978307200),8) + '00000000' + numToHex(swap32(this.refTime),8) + '0401020202' + this.accessoryType116 +'020f03 ' + numToHex(swap16(this.usedMemory),4) + numToHex(swap16(this.memorySize),4) + numToHex(swap32(this.firstEntry),8) + '000000000101');

        }

        setCurrentS2W1(val, callback) {
            callback(null,val);
            this.log.debug("Data request " + this.accessoryType + ": "+ base64ToHex(val));
            var valHex = base64ToHex(val);
            var substring = valHex.substring(4,12);
            var valInt = parseInt(substring,16);
            var address = swap32(valInt);
            var hexAddress= address.toString('16');

            this.log.debug("Address requested " + this.accessoryType + ": "+ hexAddress);
            //if (this.transfer==false)
            {
                //this.transfer=true;
                this.sendHistory(address);
            }
        }

        setCurrentS2W2(val, callback) {
            this.log.debug("Clock adjust: "+ base64ToHex(val));
            callback(null,val);
        }

    }

    return FakeGatoHistoryService;
}<|endoftext|>


<commit_before>/**
 * Module dependencies
 */

var
	Match, Play, Team, Player,
	mongoose = require('mongoose'),
	Schema = mongoose.Schema,
	ObjectId = Schema.ObjectId;

/**
 * Connect to database
 */

mongoose.connect('mongodb://localhost/minute');

/**
 * Players
 */

Player = new Schema({
	number: Number,
	name: String
});

/**
 * Teams
 */

Team = new Schema({
	name: String,
	acronym: String,
	score: { type: Number, default: 0 },
	rooster: [ Player ],
	substitutes: [ Player ]
});

/**
 * Plays
 */

Play = new Schema({
	time: Number,
	type: String,
	text: String
});

/**
 * Matches
 */

Match = new Schema({
	time: { type: Number, default: 0 },
	active: { type: Boolean, default: false },
	plays: [ Play ],
	teams: [ Team ]
});

Match.virtual('home').get(function(){
	return this.teams[0];
});

Match.virtual('away').get(function(){
	return this.teams[0];
});

/**
 * Exports all models
 */

exports.Plays = mongoose.model('plays', Play);
exports.Players = mongoose.model('players', Player);
exports.Teams = mongoose.model('teams', Team);
exports.Matches = mongoose.model('matches', Match);
<commit_msg>Fix away virtual;<commit_after>/**
 * Module dependencies
 */

var
	Match, Play, Team, Player,
	mongoose = require('mongoose'),
	Schema = mongoose.Schema,
	ObjectId = Schema.ObjectId;

/**
 * Connect to database
 */

mongoose.connect('mongodb://localhost/minute');

/**
 * Players
 */

Player = new Schema({
	number: Number,
	name: String
});

/**
 * Teams
 */

Team = new Schema({
	name: String,
	acronym: String,
	score: { type: Number, default: 0 },
	rooster: [ Player ],
	substitutes: [ Player ]
});

/**
 * Plays
 */

Play = new Schema({
	time: Number,
	type: String,
	text: String
});

/**
 * Matches
 */

Match = new Schema({
	time: { type: Number, default: 0 },
	active: { type: Boolean, default: false },
	plays: [ Play ],
	teams: [ Team ]
});

Match.virtual('home').get(function(){
	return this.teams[0];
});

Match.virtual('away').get(function(){
	return this.teams[1];
});

/**
 * Exports all models
 */

exports.Plays = mongoose.model('plays', Play);
exports.Players = mongoose.model('players', Player);
exports.Teams = mongoose.model('teams', Team);
exports.Matches = mongoose.model('matches', Match);
<|endoftext|>


<commit_before>"use strict";
var urlCache = require("../lib/internal/urlCache");

var expect = require("chai").expect;

describe("INTERNAL -- urlCache", function()
{
	describe("startRetrieving()", function()
	{
		it("reports that it is retrieving", function()
		{
			var cache = new urlCache();
			var url = 'some url';

			cache.startRetrieving(url);

			expect(cache.isRetrieving(url)).to.be.true;
		});

		it("reports when it is not retrieving", function()
		{
			var cache = new urlCache();
			var url = 'some url';

			cache.startRetrieving(url);

			expect(cache.isRetrieving('another url')).to.be.false;
		});

		it("is finished after being stored", function()
		{
			var cache = new urlCache();
			var url = 'some url';

			cache.startRetrieving(url);
			cache.store(url, 'something');

			expect(cache.isRetrieving(url)).to.be.false;
		});

		it("can be cleared", function()
		{
			var cache = new urlCache();
			var url = 'some url';

			cache.startRetrieving(url);
			cache.clear();

			expect(cache.isRetrieving(url)).to.be.false;
		});
	});

	describe("store()", function()
	{
		it("can be retrieved", function()
		{
			var cache = new urlCache();
			var content = 'some content';
			var url = 'some url';

			cache.store(url, content);

			expect(cache.retrieve(url)).to.equal(content);
		});

		it("is then contained", function()
		{
			var cache = new urlCache();
			var url = 'some url';

			cache.store(url, 'anything');

			expect(cache.contains(url)).to.be.true;
		});

		it("can be cleared", function()
		{
			var cache = new urlCache();
			var url = 'some url';

			cache.store(url, 'anything');
			cache.clear();

			expect(cache.retrieve(url)).to.equal(undefined);
			expect(cache.contains(url)).to.be.false;
		});
	});

	describe("addCallback()", function()
	{
		it("is executed after cache is stored", function()
		{
			var cache = new urlCache();
			var callback = 'some item';
			var url = 'some url';
			var success = false;

			cache.addCallback(url, callback);
			expect(success).to.be.false;
			cache.store(url, function(item) {
				expect(item).to.equal(callback);
				success = true;
			});

			expect(success).to.be.true;
		});

		it("is not overwritten by starting retrieval", function()
		{
			var cache = new urlCache();
			var callback = 'some item';
			var url = 'some url';
			var success = false;

			cache.addCallback(url, callback);
			cache.startRetrieving(url);
			cache.store(url, function(item) {
				expect(item).to.equal(callback);
				success = true;
			});

			expect(success).to.be.true;
		});
	});
});
<commit_msg>Makes single quotes into double quotes<commit_after>"use strict";
var urlCache = require("../lib/internal/urlCache");

var expect = require("chai").expect;

describe("INTERNAL -- urlCache", function()
{
	describe("startRetrieving()", function()
	{
		it("reports that it is retrieving", function()
		{
			var cache = new urlCache();
			var url = "some url";

			cache.startRetrieving(url);

			expect(cache.isRetrieving(url)).to.be.true;
		});

		it("reports when it is not retrieving", function()
		{
			var cache = new urlCache();
			var url = "some url";

			cache.startRetrieving(url);

			expect(cache.isRetrieving("another url")).to.be.false;
		});

		it("is finished after being stored", function()
		{
			var cache = new urlCache();
			var url = "some url";

			cache.startRetrieving(url);
			cache.store(url, "something");

			expect(cache.isRetrieving(url)).to.be.false;
		});

		it("can be cleared", function()
		{
			var cache = new urlCache();
			var url = "some url";

			cache.startRetrieving(url);
			cache.clear();

			expect(cache.isRetrieving(url)).to.be.false;
		});
	});

	describe("store()", function()
	{
		it("can be retrieved", function()
		{
			var cache = new urlCache();
			var content = "some content";
			var url = "some url";

			cache.store(url, content);

			expect(cache.retrieve(url)).to.equal(content);
		});

		it("is then contained", function()
		{
			var cache = new urlCache();
			var url = "some url";

			cache.store(url, "anything");

			expect(cache.contains(url)).to.be.true;
		});

		it("can be cleared", function()
		{
			var cache = new urlCache();
			var url = "some url";

			cache.store(url, "anything");
			cache.clear();

			expect(cache.retrieve(url)).to.equal(undefined);
			expect(cache.contains(url)).to.be.false;
		});
	});

	describe("addCallback()", function()
	{
		it("is executed after cache is stored", function()
		{
			var cache = new urlCache();
			var callback = "some item";
			var url = "some url";
			var success = false;

			cache.addCallback(url, callback);
			expect(success).to.be.false;
			cache.store(url, function(item) {
				expect(item).to.equal(callback);
				success = true;
			});

			expect(success).to.be.true;
		});

		it("is not overwritten by starting retrieval", function()
		{
			var cache = new urlCache();
			var callback = "some item";
			var url = "some url";
			var success = false;

			cache.addCallback(url, callback);
			cache.startRetrieving(url);
			cache.store(url, function(item) {
				expect(item).to.equal(callback);
				success = true;
			});

			expect(success).to.be.true;
		});
	});
});
<|endoftext|>