<commit_before>describe('WXYZ - Course Authoring System', function() {
	var path = require('path');
	var absolutePath = '';
	var fileToUpload = '';
	var last_element;
	var clickButton = function(toolbar_count, button_count) {
		element.all(by.css('.cke_toolbar')).then(function(toolbar) {
			//toolbar.row(0).count().then(function(ret) { console.log(ret) });
			//toolbar.row(toolbar_count).element.all(by.css('.cke_button')).then(function(button) {
			//	button.row(button_count).click();
			//});
		});
	};

	it('Start the browser', function(){
		browser.get('http://localhost:3000/');
        browser.driver.manage().window().maximize();
        browser.sleep(500);
	});

	it('Sign In', function(){
		element(by.name('signInButton')).click();
		element(by.name('username')).sendKeys('igor');
		element(by.name('password')).sendKeys('tiramisu');

		expect(element(by.name('username')).getAttribute('value')).toEqual('igor');
		expect(element(by.name('password')).getAttribute('value')).toEqual('tiramisu');

		element(by.name('signIn')).click();
	});

	/*it('Create Project', function(){
		element.all(by.repeater('item in menu.items').row(0)).click();
		element.all(by.repeater('subitem in item.items').row(0)).click();
		element(by.name('title')).sendKeys('Project 1');
		element(by.name('content')).sendKeys('This is an automated test of project creation.');

		expect(element(by.name('title')).getAttribute('value')).toEqual('Project 1');
		expect(element(by.name('content')).getAttribute('value')).toEqual('This is an automated test of project creation.');
		element(by.name('submitButton')).click();

		expect(element.all(by.repeater('project in projects.slice().reverse()')).last().getAttribute('heading')).toEqual('Project 1');
	});*/
	/*
	it('Create Course', function(){
		element.all(by.repeater('project in projects.slice().reverse()')).last().click();
		element.all(by.repeater('item in menu.items').row(2)).click();
		element.all(by.repeater('subitem in item.items').row(11)).click();
		element(by.name('title')).sendKeys('Course 1');

		expect(element(by.name('title')).getAttribute('value')).toEqual('Course 1');
		element(by.name('submitButton')).click();
	});*/

	it('Add Elements', function(){
		element.all(by.repeater('project in projects.slice().reverse()')).last().click();

		// text
		element(by.repeater('item in menu.items').row(3)).click();
		element.all(by.repeater('subitem in item.items').row(19)).click();
		browser.sleep(2000);

		clickButton(3, 6);

		browser.switchTo().frame(0);
		browser.driver.findElement(by.tagName('body')).click();
		browser.driver.findElement(by.tagName('body')).sendKeys('Text 1');
		browser.driver.switchTo().defaultContent();
		browser.waitForAngular();
		/*
		element(by.name('submit')).click();
		browser.sleep(2000);

		last_element = element.all(by.repeater('element in project.elements')).last();
		expect(last_element.element(by.css('.ng-binding')).getInnerHtml()).toBe('
Text 1
\n');

		//audio
		element(by.repeater('item in menu.items').row(3)).click();
		element.all(by.repeater('subitem in item.items').row(23)).click();
		fileToUpload = '../test_elements/audio_test.mp3';
		absolutePath = path.resolve(__dirname, fileToUpload);
		$('input[type="file"]').sendKeys(absolutePath);
		browser.sleep(500);
		last_element = element.all(by.repeater('element in project.elements')).last();
		expect(last_element.element(by.tagName('audio')).isPresent()).toBe(true);

		//video
		element(by.repeater('item in menu.items').row(3)).click();
		element.all(by.repeater('subitem in item.items').row(20)).click();
		fileToUpload = '../test_elements/video_test.mp4';
		absolutePath = path.resolve(__dirname, fileToUpload);
		element(by.model('showMedia')).click();
		$('input[type="file"]').sendKeys(absolutePath);
		browser.sleep(500);
		last_element = element.all(by.repeater('element in project.elements')).last();
		expect(last_element.element(by.tagName('video')).isPresent()).toBe(true);

		//static image (jpg)
		element(by.repeater('item in menu.items').row(3)).click();
		element.all(by.repeater('subitem in item.items').row(25)).click();
		fileToUpload = '../test_elements/dog_pic.jpg';
		absolutePath = path.resolve(__dirname, fileToUpload);
		$('input[type="file"]').sendKeys(absolutePath);
		browser.sleep(500);

		last_element = element.all(by.repeater('element in project.elements')).last();
		expect(last_element.element(by.tagName('img')).isPresent()).toBe(true);

		//static image (gif)
		element(by.repeater('item in menu.items').row(3)).click();
		element.all(by.repeater('subitem in item.items').row(25)).click();
		fileToUpload = '../test_elements/gif_test.gif';
		absolutePath = path.resolve(__dirname, fileToUpload);
		$('input[type="file"]').sendKeys(absolutePath);
		browser.sleep(500);

		last_element = element.all(by.repeater('element in project.elements')).last();
		expect(last_element.element(by.tagName('img')).isPresent()).toBe(true);
		*/
	});

	/*
	it('Remove an Element', function(){
		// delete the last element
		// open the last project
		element.all(by.repeater('project in projects.slice().reverse()')).last().click();
		// get number of elements and subtract one
		element.all(by.repeater('element in project.elements')).count().then(function(oldCount) {
			// find the last element
			last_element = element.all(by.repeater('element in project.elements')).last();
			// click delete and confirm
			last_element.element(by.name('elementDeleteButton')).click();
			element(by.name('confirmDeleteButton')).click();
			browser.sleep(300);
			// get the new number of elements
			element.all(by.repeater('element in project.elements')).count().then(function(newCount) {
				expect(newCount).toBe(oldCount-1);
			});
		});
	});
	*/
});<commit_msg>Finalized testing<commit_after>describe('WXYZ - Course Authoring System', function() {
	var path = require('path');
	var absolutePath = '';
	var fileToUpload = '';
	var last_element;
	var ckEditorButton = function(button) {
		element.all(by.css('.cke_button')).then(function(btn) {
			btn[button].click();
		});
	};

	it('Start the browser', function(){
		browser.get('http://localhost:3000/');
        browser.driver.manage().window().maximize();
        browser.sleep(500);
	});

	it('Create a user account using the default signin', function() {
		element(by.name('signUpButton')).click(); // click the signup button
		element(by.id('initialusername')).sendKeys('Admin'); // type the default username
		element(by.id('initialpassword')).sendKeys('12341234'); // type the default password
		element(by.name('firstName')).sendKeys('Igor11'); // type the first name
		element(by.name('lastName')).sendKeys('Igor11'); // type the last name
		element(by.name('email')).sendKeys('igor11@email.com'); // type the email
		element(by.id('username')).sendKeys('igor11'); // type the username
		element(by.id('password')).sendKeys('tiramisu'); // type the password
		browser.sleep(500);
		element(by.name('signUpSubmit')).click(); // click signup button
	});

	/*
	it('Sign In', function(){
		var user = '';
		var password = '';
		element(by.name('signInButton')).click(); // click the signin button
		element(by.name('username')).sendKeys(user); // type the username
		element(by.name('password')).sendKeys(password); // type the password
		expect(element(by.name('username')).getAttribute('value')).toEqual(user); // check the username
		expect(element(by.name('password')).getAttribute('value')).toEqual(password); // check the password
		element(by.name('signIn')).click(); // click signin button
	});*/

	it('Create Project', function(){
		element.all(by.repeater('item in menu.items').row(0)).click();
		element.all(by.repeater('subitem in item.items').row(0)).click();
		element(by.name('title')).sendKeys('Project 1');
		element(by.name('content')).sendKeys('This is an automated test of project creation.');

		expect(element(by.name('title')).getAttribute('value')).toEqual('Project 1');
		expect(element(by.name('content')).getAttribute('value')).toEqual('This is an automated test of project creation.');
		element(by.name('submitButton')).click();

		expect(element.all(by.repeater('project in projects.slice().reverse()')).last().getAttribute('heading')).toEqual('Project 1');
	});

	it('Create Course', function(){
		element.all(by.repeater('project in projects.slice().reverse()')).last().click();
		element.all(by.repeater('item in menu.items').row(2)).click();
		element.all(by.repeater('subitem in item.items').row(11)).click();
		element(by.name('title')).sendKeys('Course 1');

		expect(element(by.name('title')).getAttribute('value')).toEqual('Course 1');
		element(by.name('submitButton')).click();
	});

	it('Add Elements', function(){
		element.all(by.repeater('project in projects.slice().reverse()')).last().click();

		// text
		element(by.repeater('item in menu.items').row(3)).click();
		element.all(by.repeater('subitem in item.items').row(19)).click();
		browser.sleep(2000);

		ckEditorButton(20); // center

		browser.switchTo().frame(0);
		browser.driver.findElement(by.tagName('body')).click();
		browser.driver.findElement(by.tagName('body')).sendKeys('Text 1');
		browser.driver.switchTo().defaultContent();
		browser.waitForAngular();

		element(by.name('submit')).click();
		browser.sleep(2000);

		last_element = element.all(by.repeater('element in project.elements')).last();
		expect(last_element.element(by.css('.ng-binding')).getInnerHtml()).
			toBe('
Text 1
\n');

		//audio
		element(by.repeater('item in menu.items').row(3)).click();
		element.all(by.repeater('subitem in item.items').row(23)).click();
		fileToUpload = '../test_elements/audio_test.mp3';
		absolutePath = path.resolve(__dirname, fileToUpload);
		$('input[type="file"]').sendKeys(absolutePath);
		browser.sleep(500);
		last_element = element.all(by.repeater('element in project.elements')).last();
		expect(last_element.element(by.tagName('audio')).isPresent()).toBe(true);

		//video
		element(by.repeater('item in menu.items').row(3)).click();
		element.all(by.repeater('subitem in item.items').row(20)).click();
		fileToUpload = '../test_elements/video_test.mp4';
		absolutePath = path.resolve(__dirname, fileToUpload);
		element(by.model('showMedia')).click();
		$('input[type="file"]').sendKeys(absolutePath);
		browser.sleep(500);
		last_element = element.all(by.repeater('element in project.elements')).last();
		expect(last_element.element(by.tagName('video')).isPresent()).toBe(true);

		// youtube
		element(by.repeater('item in menu.items').row(3)).click();
		element.all(by.repeater('subitem in item.items').row(20)).click();
		element(by.model('showMedia')).click();
		element(by.model('videoEmbed')).sendKeys('https://www.youtube.com/watch?v=rlVwJvE3C5A');
		expect(element(by.model('videoEmbed'))).toBe('https://www.youtube.com/watch?v=rlVwJvE3C5A');
		element(by.name('submit')).click();
		browser.sleep(500);
		last_element = element.all(by.repeater('element in project.elements')).last();
		expect(last_element.element(by.tagName('iframe')).isPresent()).toBe(true);

		//static image (jpg)
		element(by.repeater('item in menu.items').row(3)).click();
		element.all(by.repeater('subitem in item.items').row(25)).click();
		fileToUpload = '../test_elements/dog_pic.jpg';
		absolutePath = path.resolve(__dirname, fileToUpload);
		$('input[type="file"]').sendKeys(absolutePath);
		browser.sleep(500);

		last_element = element.all(by.repeater('element in project.elements')).last();
		expect(last_element.element(by.tagName('img')).isPresent()).toBe(true);

		//static image (gif)
		element(by.repeater('item in menu.items').row(3)).click();
		element.all(by.repeater('subitem in item.items').row(25)).click();
		fileToUpload = '../test_elements/gif_test.gif';
		absolutePath = path.resolve(__dirname, fileToUpload);
		$('input[type="file"]').sendKeys(absolutePath);
		browser.sleep(500);

		last_element = element.all(by.repeater('element in project.elements')).last();
		expect(last_element.element(by.tagName('img')).isPresent()).toBe(true);

	});

	it('Remove an Element', function(){
		// delete the last element
		// open the last project
		element.all(by.repeater('project in projects.slice().reverse()')).last().click();
		// get number of elements and subtract one
		element.all(by.repeater('element in project.elements')).count().then(function(oldCount) {
			// find the last element
			last_element = element.all(by.repeater('element in project.elements')).last();
			// click delete and confirm
			last_element.element(by.name('elementDeleteButton')).click();
			element(by.name('confirmDeleteButton')).click();
			browser.sleep(300);
			// get the new number of elements
			element.all(by.repeater('element in project.elements')).count().then(function(newCount) {
				expect(newCount).toBe(oldCount-1);
			});
		});
	});
});<|endoftext|>


<commit_before>
var ForgePlugins = ForgePlugins || {};

ForgePlugins.EditorUI = function(editor)
{
    this._editor = editor;

    this._container = null;

    this._buttonPanel = null;

    this._hierarchyPanel = null;

    this._helperPanel = null;

    this._inspectorPanel = null;

    this._boot();
};

ForgePlugins.EditorUI.prototype =
{
    _boot: function()
    {
        this._container = document.createElement("div");
        this._container.id = "editor-main-container";
        this._editor.plugin.container.dom.appendChild(this._container);

        this._buttonPanel = new ForgePlugins.EditorButtonPanel(this._editor);
        this._container.appendChild(this._buttonPanel.container);

        this._hierarchyPanel = new ForgePlugins.EditorHierarchyPanel(this._editor);
        this._container.appendChild(this._hierarchyPanel.container);

        // this._helperPanel = new ForgePlugins.EditorHelperPanel(this._editor);
        // this._container.appendChild(this._helperPanel.container);

        this._inspectorPanel = new ForgePlugins.EditorInspectorPanel(this._editor);
        this._container.appendChild(this._inspectorPanel.container);
    },

    update: function()
    {

    },

    destroy: function()
    {
        this._container.removeChild(this._buttonPanel.container);
        this._buttonPanel.destroy();
        this._buttonPanel = null;

        this._container.removeChild(this._hierarchyPanel.container);
        this._hierarchyPanel.destroy();
        this._hierarchyPanel = null;

        this._container.removeChild(this._helperPanel.container);
        this._helperPanel.destroy();
        this._helperPanel = null;

        this._container.removeChild(this._inspectorPanel.container);
        this._inspectorPanel.destroy();
        this._inspectorPanel = null;

        this._container = null;
    }
};

Object.defineProperty(ForgePlugins.EditorUI.prototype, "helper",
{
    get: function()
    {
        return this._helperPanel;
    }
});<commit_msg>Restore helper panel<commit_after>
var ForgePlugins = ForgePlugins || {};

ForgePlugins.EditorUI = function(editor)
{
    this._editor = editor;

    this._container = null;

    this._buttonPanel = null;

    this._hierarchyPanel = null;

    this._helperPanel = null;

    this._inspectorPanel = null;

    this._boot();
};

ForgePlugins.EditorUI.prototype =
{
    _boot: function()
    {
        this._container = document.createElement("div");
        this._container.id = "editor-main-container";
        this._editor.plugin.container.dom.appendChild(this._container);

        this._buttonPanel = new ForgePlugins.EditorButtonPanel(this._editor);
        this._container.appendChild(this._buttonPanel.container);

        this._hierarchyPanel = new ForgePlugins.EditorHierarchyPanel(this._editor);
        this._container.appendChild(this._hierarchyPanel.container);

        this._helperPanel = new ForgePlugins.EditorHelperPanel(this._editor);
        this._container.appendChild(this._helperPanel.container);

        this._inspectorPanel = new ForgePlugins.EditorInspectorPanel(this._editor);
        this._container.appendChild(this._inspectorPanel.container);
    },

    update: function()
    {

    },

    destroy: function()
    {
        this._container.removeChild(this._buttonPanel.container);
        this._buttonPanel.destroy();
        this._buttonPanel = null;

        this._container.removeChild(this._hierarchyPanel.container);
        this._hierarchyPanel.destroy();
        this._hierarchyPanel = null;

        this._container.removeChild(this._helperPanel.container);
        this._helperPanel.destroy();
        this._helperPanel = null;

        this._container.removeChild(this._inspectorPanel.container);
        this._inspectorPanel.destroy();
        this._inspectorPanel = null;

        this._container = null;
    }
};

Object.defineProperty(ForgePlugins.EditorUI.prototype, "helper",
{
    get: function()
    {
        return this._helperPanel;
    }
});<|endoftext|>


<commit_before>import deepEqual from 'fast-deep-equal';
import { actionAddVertex } from '../actions/add_vertex';
import { actionChangeTags } from '../actions/change_tags';
import { actionMergeNodes } from '../actions/merge_nodes';
import { actionExtract } from '../actions/extract';
import { modeSelect } from '../modes/select';
import { osmJoinWays } from '../osm/multipolygon';
import { osmNodeGeometriesForTags, osmTagSuggestingArea } from '../osm/tags';
import { presetManager } from '../presets';
import { geoHasSelfIntersections, geoSphericalDistance } from '../geo';
import { t } from '../core/localizer';
import { utilDisplayLabel, utilTagText } from '../util';
import { validationIssue, validationIssueFix } from '../core/validation';

export function validationMismatchedGeometry() {
    var type = 'mismatched_geometry';

    function tagSuggestingLineIsArea(entity) {
        if (entity.type !== 'way' || entity.isClosed()) return null;

        var tagSuggestingArea = entity.tagSuggestingArea();
        if (!tagSuggestingArea) {
            return null;
        }

        var asLine = presetManager.matchTags(tagSuggestingArea, 'line');
        var asArea = presetManager.matchTags(tagSuggestingArea, 'area');
        if (asLine && asArea && (asLine === asArea)) {
            // these tags also allow lines and making this an area wouldn't matter
            return null;
        }

        return tagSuggestingArea;
    }

    function makeConnectEndpointsFixOnClick(way, graph) {
        // must have at least three nodes to close this automatically
        if (way.nodes.length < 3) return null;

        var nodes = graph.childNodes(way), testNodes;
        var firstToLastDistanceMeters = geoSphericalDistance(nodes[0].loc, nodes[nodes.length-1].loc);

        // if the distance is very small, attempt to merge the endpoints
        if (firstToLastDistanceMeters < 0.75) {
            testNodes = nodes.slice();   // shallow copy
            testNodes.pop();
            testNodes.push(testNodes[0]);
            // make sure this will not create a self-intersection
            if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
                return function(context) {
                    var way = context.entity(this.issue.entityIds[0]);
                    context.perform(
                        actionMergeNodes([way.nodes[0], way.nodes[way.nodes.length-1]], nodes[0].loc),
                        t('issues.fix.connect_endpoints.annotation')
                    );
                };
            }
        }

        // if the points were not merged, attempt to close the way
        testNodes = nodes.slice();   // shallow copy
        testNodes.push(testNodes[0]);
        // make sure this will not create a self-intersection
        if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
            return function(context) {
                var wayId = this.issue.entityIds[0];
                var way = context.entity(wayId);
                var nodeId = way.nodes[0];
                var index = way.nodes.length;
                context.perform(
                    actionAddVertex(wayId, nodeId, index),
                    t('issues.fix.connect_endpoints.annotation')
                );
            };
        }
    }

    function lineTaggedAsAreaIssue(entity) {

        var tagSuggestingArea = tagSuggestingLineIsArea(entity);
        if (!tagSuggestingArea) return null;

        return new validationIssue({
            type: type,
            subtype: 'area_as_line',
            severity: 'warning',
            message: function(context) {
                var entity = context.hasEntity(this.entityIds[0]);
                return entity ? t.html('issues.tag_suggests_area.message', {
                    feature: utilDisplayLabel(entity, 'area'),
                    tag: utilTagText({ tags: tagSuggestingArea })
                }) : '';
            },
            reference: showReference,
            entityIds: [entity.id],
            hash: JSON.stringify(tagSuggestingArea),
            dynamicFixes: function(context) {

                var fixes = [];

                var entity = context.entity(this.entityIds[0]);
                var connectEndsOnClick = makeConnectEndpointsFixOnClick(entity, context.graph());

                fixes.push(new validationIssueFix({
                    title: t.html('issues.fix.connect_endpoints.title'),
                    onClick: connectEndsOnClick
                }));

                fixes.push(new validationIssueFix({
                    icon: 'iD-operation-delete',
                    title: t.html('issues.fix.remove_tag.title'),
                    onClick: function(context) {
                        var entityId = this.issue.entityIds[0];
                        var entity = context.entity(entityId);
                        var tags = Object.assign({}, entity.tags);  // shallow copy
                        for (var key in tagSuggestingArea) {
                            delete tags[key];
                        }
                        context.perform(
                            actionChangeTags(entityId, tags),
                            t('issues.fix.remove_tag.annotation')
                        );
                    }
                }));

                return fixes;
            }
        });

        function showReference(selection) {
            selection.selectAll('.issue-reference')
                .data([0])
                .enter()
                .append('div')
                .attr('class', 'issue-reference')
                .html(t.html('issues.tag_suggests_area.reference'));
        }
    }

    function vertexPointIssue(entity, graph) {
        // we only care about nodes
        if (entity.type !== 'node') return null;

        // ignore tagless points
        if (Object.keys(entity.tags).length === 0) return null;

        // address lines are special so just ignore them
        if (entity.isOnAddressLine(graph)) return null;

        var geometry = entity.geometry(graph);
        var allowedGeometries = osmNodeGeometriesForTags(entity.tags);

        if (geometry === 'point' && !allowedGeometries.point && allowedGeometries.vertex) {

            return new validationIssue({
                type: type,
                subtype: 'vertex_as_point',
                severity: 'warning',
                message: function(context) {
                    var entity = context.hasEntity(this.entityIds[0]);
                    return entity ? t.html('issues.vertex_as_point.message', {
                        feature: utilDisplayLabel(entity, 'vertex')
                    }) : '';
                },
                reference: function showReference(selection) {
                    selection.selectAll('.issue-reference')
                        .data([0])
                        .enter()
                        .append('div')
                        .attr('class', 'issue-reference')
                        .html(t.html('issues.vertex_as_point.reference'));
                },
                entityIds: [entity.id]
            });

        } else if (geometry === 'vertex' && !allowedGeometries.vertex && allowedGeometries.point) {

            return new validationIssue({
                type: type,
                subtype: 'point_as_vertex',
                severity: 'warning',
                message: function(context) {
                    var entity = context.hasEntity(this.entityIds[0]);
                    return entity ? t.html('issues.point_as_vertex.message', {
                        feature: utilDisplayLabel(entity, 'point')
                    }) : '';
                },
                reference: function showReference(selection) {
                    selection.selectAll('.issue-reference')
                        .data([0])
                        .enter()
                        .append('div')
                        .attr('class', 'issue-reference')
                        .html(t.html('issues.point_as_vertex.reference'));
                },
                entityIds: [entity.id],
                dynamicFixes: extractPointDynamicFixes
            });
        }

        return null;
    }

    function otherMismatchIssue(entity, graph) {
        // ignore boring features
        if (!entity.hasInterestingTags()) return null;

        if (entity.type !== 'node' && entity.type !== 'way') return null;

        // address lines are special so just ignore them
        if (entity.type === 'node' && entity.isOnAddressLine(graph)) return null;

        var sourceGeom = entity.geometry(graph);

        var targetGeoms = entity.type === 'way' ? ['point', 'vertex'] : ['line', 'area'];

        if (sourceGeom === 'area') targetGeoms.unshift('line');

        var targetGeom = targetGeoms.find(nodeGeom => {
            var asSource = presetManager.matchTags(entity.tags, sourceGeom);
            var asTarget = presetManager.matchTags(entity.tags, nodeGeom);
            if (!asSource || !asTarget ||
                asSource === asTarget ||
                // sometimes there are two presets with the same tags for different geometries
                deepEqual(asSource.tags, asTarget.tags)) return false;

            if (asTarget.isFallback()) return false;

            var primaryKey = Object.keys(asTarget.tags)[0];

            // special case: buildings-as-points are discouraged by iD, but common in OSM, so ignore them
            if (primaryKey === 'building') return false;

            if (asTarget.tags[primaryKey] === '*') return false;

            return asSource.isFallback() || asSource.tags[primaryKey] === '*';
        });

        if (!targetGeom) return null;

        var subtype = targetGeom + '_as_' + sourceGeom;

        if (targetGeom === 'vertex') targetGeom = 'point';
        if (sourceGeom === 'vertex') sourceGeom = 'point';

        var referenceId = targetGeom + '_as_' + sourceGeom;

        var dynamicFixes;
        if (targetGeom === 'point') {
            dynamicFixes = extractPointDynamicFixes;

        } else if (sourceGeom === 'area' && targetGeom === 'line') {
            dynamicFixes = lintToAreaDynamicFixes;
        }

        return new validationIssue({
            type: type,
            subtype: subtype,
            severity: 'warning',
            message: function(context) {
                var entity = context.hasEntity(this.entityIds[0]);
                return entity ? t.html('issues.' + referenceId + '.message', {
                    feature: utilDisplayLabel(entity, targetGeom)
                }) : '';
            },
            reference: function showReference(selection) {
                selection.selectAll('.issue-reference')
                    .data([0])
                    .enter()
                    .append('div')
                    .attr('class', 'issue-reference')
                    .html(t.html('issues.mismatched_geometry.reference'));
            },
            entityIds: [entity.id],
            dynamicFixes: dynamicFixes
        });
    }

    function lintToAreaDynamicFixes(context) {

        var convertOnClick;

        var entityId = this.entityIds[0];
        var entity = context.entity(entityId);
        var tags = Object.assign({}, entity.tags);  // shallow copy
        delete tags.area;
        if (!osmTagSuggestingArea(tags)) {
            // if removing the area tag would make this a line, offer that as a quick fix
            convertOnClick = function(context) {
                var entityId = this.issue.entityIds[0];
                var entity = context.entity(entityId);
                var tags = Object.assign({}, entity.tags);  // shallow copy
                if (tags.area) {
                    delete tags.area;
                }
                context.perform(
                    actionChangeTags(entityId, tags),
                    t('issues.fix.convert_to_line.annotation')
                );
            };
        }

        return [
            new validationIssueFix({
                icon: 'iD-icon-line',
                title: t.html('issues.fix.convert_to_line.title'),
                onClick: convertOnClick
            })
        ];
    }

    function extractPointDynamicFixes(context) {

        var entityId = this.entityIds[0];

        var extractOnClick = null;
        if (!context.hasHiddenConnections(entityId)) {

            extractOnClick = function(context) {
                var entityId = this.issue.entityIds[0];
                var action = actionExtract(entityId);
                context.perform(
                    action,
                    t('operations.extract.annotation', { n: 1 })
                );
                // re-enter mode to trigger updates
                context.enter(modeSelect(context, [action.getExtractedNodeID()]));
            };
        }

        return [
            new validationIssueFix({
                icon: 'iD-operation-extract',
                title: t.html('issues.fix.extract_point.title'),
                onClick: extractOnClick
            })
        ];
    }

    function unclosedMultipolygonPartIssues(entity, graph) {

        if (entity.type !== 'relation' ||
            !entity.isMultipolygon() ||
            entity.isDegenerate() ||
            // cannot determine issues for incompletely-downloaded relations
            !entity.isComplete(graph)) return [];

        var sequences = osmJoinWays(entity.members, graph);

        var issues = [];

        for (var i in sequences) {
            var sequence = sequences[i];

            if (!sequence.nodes) continue;

            var firstNode = sequence.nodes[0];
            var lastNode = sequence.nodes[sequence.nodes.length - 1];

            // part is closed if the first and last nodes are the same
            if (firstNode === lastNode) continue;

            var issue = new validationIssue({
                type: type,
                subtype: 'unclosed_multipolygon_part',
                severity: 'warning',
                message: function(context) {
                    var entity = context.hasEntity(this.entityIds[0]);
                    return entity ? t.html('issues.unclosed_multipolygon_part.message', {
                        feature: utilDisplayLabel(entity, context.graph())
                    }) : '';
                },
                reference: showReference,
                loc: sequence.nodes[0].loc,
                entityIds: [entity.id],
                hash: sequence.map(function(way) {
                    return way.id;
                }).join()
            });
            issues.push(issue);
        }

        return issues;

        function showReference(selection) {
            selection.selectAll('.issue-reference')
                .data([0])
                .enter()
                .append('div')
                .attr('class', 'issue-reference')
                .html(t.html('issues.unclosed_multipolygon_part.reference'));
        }
    }

    var validation = function checkMismatchedGeometry(entity, graph) {
        var vertexPoint = vertexPointIssue(entity, graph);
        if (vertexPoint) return [vertexPoint];

        var lineAsArea = lineTaggedAsAreaIssue(entity);
        if (lineAsArea) return [lineAsArea];

        var mismatch = otherMismatchIssue(entity, graph);
        if (mismatch) return [mismatch];

        return unclosedMultipolygonPartIssues(entity, graph);
    };

    validation.type = type;

    return validation;
}
<commit_msg>Fix spelling<commit_after>import deepEqual from 'fast-deep-equal';
import { actionAddVertex } from '../actions/add_vertex';
import { actionChangeTags } from '../actions/change_tags';
import { actionMergeNodes } from '../actions/merge_nodes';
import { actionExtract } from '../actions/extract';
import { modeSelect } from '../modes/select';
import { osmJoinWays } from '../osm/multipolygon';
import { osmNodeGeometriesForTags, osmTagSuggestingArea } from '../osm/tags';
import { presetManager } from '../presets';
import { geoHasSelfIntersections, geoSphericalDistance } from '../geo';
import { t } from '../core/localizer';
import { utilDisplayLabel, utilTagText } from '../util';
import { validationIssue, validationIssueFix } from '../core/validation';

export function validationMismatchedGeometry() {
    var type = 'mismatched_geometry';

    function tagSuggestingLineIsArea(entity) {
        if (entity.type !== 'way' || entity.isClosed()) return null;

        var tagSuggestingArea = entity.tagSuggestingArea();
        if (!tagSuggestingArea) {
            return null;
        }

        var asLine = presetManager.matchTags(tagSuggestingArea, 'line');
        var asArea = presetManager.matchTags(tagSuggestingArea, 'area');
        if (asLine && asArea && (asLine === asArea)) {
            // these tags also allow lines and making this an area wouldn't matter
            return null;
        }

        return tagSuggestingArea;
    }

    function makeConnectEndpointsFixOnClick(way, graph) {
        // must have at least three nodes to close this automatically
        if (way.nodes.length < 3) return null;

        var nodes = graph.childNodes(way), testNodes;
        var firstToLastDistanceMeters = geoSphericalDistance(nodes[0].loc, nodes[nodes.length-1].loc);

        // if the distance is very small, attempt to merge the endpoints
        if (firstToLastDistanceMeters < 0.75) {
            testNodes = nodes.slice();   // shallow copy
            testNodes.pop();
            testNodes.push(testNodes[0]);
            // make sure this will not create a self-intersection
            if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
                return function(context) {
                    var way = context.entity(this.issue.entityIds[0]);
                    context.perform(
                        actionMergeNodes([way.nodes[0], way.nodes[way.nodes.length-1]], nodes[0].loc),
                        t('issues.fix.connect_endpoints.annotation')
                    );
                };
            }
        }

        // if the points were not merged, attempt to close the way
        testNodes = nodes.slice();   // shallow copy
        testNodes.push(testNodes[0]);
        // make sure this will not create a self-intersection
        if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
            return function(context) {
                var wayId = this.issue.entityIds[0];
                var way = context.entity(wayId);
                var nodeId = way.nodes[0];
                var index = way.nodes.length;
                context.perform(
                    actionAddVertex(wayId, nodeId, index),
                    t('issues.fix.connect_endpoints.annotation')
                );
            };
        }
    }

    function lineTaggedAsAreaIssue(entity) {

        var tagSuggestingArea = tagSuggestingLineIsArea(entity);
        if (!tagSuggestingArea) return null;

        return new validationIssue({
            type: type,
            subtype: 'area_as_line',
            severity: 'warning',
            message: function(context) {
                var entity = context.hasEntity(this.entityIds[0]);
                return entity ? t.html('issues.tag_suggests_area.message', {
                    feature: utilDisplayLabel(entity, 'area'),
                    tag: utilTagText({ tags: tagSuggestingArea })
                }) : '';
            },
            reference: showReference,
            entityIds: [entity.id],
            hash: JSON.stringify(tagSuggestingArea),
            dynamicFixes: function(context) {

                var fixes = [];

                var entity = context.entity(this.entityIds[0]);
                var connectEndsOnClick = makeConnectEndpointsFixOnClick(entity, context.graph());

                fixes.push(new validationIssueFix({
                    title: t.html('issues.fix.connect_endpoints.title'),
                    onClick: connectEndsOnClick
                }));

                fixes.push(new validationIssueFix({
                    icon: 'iD-operation-delete',
                    title: t.html('issues.fix.remove_tag.title'),
                    onClick: function(context) {
                        var entityId = this.issue.entityIds[0];
                        var entity = context.entity(entityId);
                        var tags = Object.assign({}, entity.tags);  // shallow copy
                        for (var key in tagSuggestingArea) {
                            delete tags[key];
                        }
                        context.perform(
                            actionChangeTags(entityId, tags),
                            t('issues.fix.remove_tag.annotation')
                        );
                    }
                }));

                return fixes;
            }
        });

        function showReference(selection) {
            selection.selectAll('.issue-reference')
                .data([0])
                .enter()
                .append('div')
                .attr('class', 'issue-reference')
                .html(t.html('issues.tag_suggests_area.reference'));
        }
    }

    function vertexPointIssue(entity, graph) {
        // we only care about nodes
        if (entity.type !== 'node') return null;

        // ignore tagless points
        if (Object.keys(entity.tags).length === 0) return null;

        // address lines are special so just ignore them
        if (entity.isOnAddressLine(graph)) return null;

        var geometry = entity.geometry(graph);
        var allowedGeometries = osmNodeGeometriesForTags(entity.tags);

        if (geometry === 'point' && !allowedGeometries.point && allowedGeometries.vertex) {

            return new validationIssue({
                type: type,
                subtype: 'vertex_as_point',
                severity: 'warning',
                message: function(context) {
                    var entity = context.hasEntity(this.entityIds[0]);
                    return entity ? t.html('issues.vertex_as_point.message', {
                        feature: utilDisplayLabel(entity, 'vertex')
                    }) : '';
                },
                reference: function showReference(selection) {
                    selection.selectAll('.issue-reference')
                        .data([0])
                        .enter()
                        .append('div')
                        .attr('class', 'issue-reference')
                        .html(t.html('issues.vertex_as_point.reference'));
                },
                entityIds: [entity.id]
            });

        } else if (geometry === 'vertex' && !allowedGeometries.vertex && allowedGeometries.point) {

            return new validationIssue({
                type: type,
                subtype: 'point_as_vertex',
                severity: 'warning',
                message: function(context) {
                    var entity = context.hasEntity(this.entityIds[0]);
                    return entity ? t.html('issues.point_as_vertex.message', {
                        feature: utilDisplayLabel(entity, 'point')
                    }) : '';
                },
                reference: function showReference(selection) {
                    selection.selectAll('.issue-reference')
                        .data([0])
                        .enter()
                        .append('div')
                        .attr('class', 'issue-reference')
                        .html(t.html('issues.point_as_vertex.reference'));
                },
                entityIds: [entity.id],
                dynamicFixes: extractPointDynamicFixes
            });
        }

        return null;
    }

    function otherMismatchIssue(entity, graph) {
        // ignore boring features
        if (!entity.hasInterestingTags()) return null;

        if (entity.type !== 'node' && entity.type !== 'way') return null;

        // address lines are special so just ignore them
        if (entity.type === 'node' && entity.isOnAddressLine(graph)) return null;

        var sourceGeom = entity.geometry(graph);

        var targetGeoms = entity.type === 'way' ? ['point', 'vertex'] : ['line', 'area'];

        if (sourceGeom === 'area') targetGeoms.unshift('line');

        var targetGeom = targetGeoms.find(nodeGeom => {
            var asSource = presetManager.matchTags(entity.tags, sourceGeom);
            var asTarget = presetManager.matchTags(entity.tags, nodeGeom);
            if (!asSource || !asTarget ||
                asSource === asTarget ||
                // sometimes there are two presets with the same tags for different geometries
                deepEqual(asSource.tags, asTarget.tags)) return false;

            if (asTarget.isFallback()) return false;

            var primaryKey = Object.keys(asTarget.tags)[0];

            // special case: buildings-as-points are discouraged by iD, but common in OSM, so ignore them
            if (primaryKey === 'building') return false;

            if (asTarget.tags[primaryKey] === '*') return false;

            return asSource.isFallback() || asSource.tags[primaryKey] === '*';
        });

        if (!targetGeom) return null;

        var subtype = targetGeom + '_as_' + sourceGeom;

        if (targetGeom === 'vertex') targetGeom = 'point';
        if (sourceGeom === 'vertex') sourceGeom = 'point';

        var referenceId = targetGeom + '_as_' + sourceGeom;

        var dynamicFixes;
        if (targetGeom === 'point') {
            dynamicFixes = extractPointDynamicFixes;

        } else if (sourceGeom === 'area' && targetGeom === 'line') {
            dynamicFixes = lineToAreaDynamicFixes;
        }

        return new validationIssue({
            type: type,
            subtype: subtype,
            severity: 'warning',
            message: function(context) {
                var entity = context.hasEntity(this.entityIds[0]);
                return entity ? t.html('issues.' + referenceId + '.message', {
                    feature: utilDisplayLabel(entity, targetGeom)
                }) : '';
            },
            reference: function showReference(selection) {
                selection.selectAll('.issue-reference')
                    .data([0])
                    .enter()
                    .append('div')
                    .attr('class', 'issue-reference')
                    .html(t.html('issues.mismatched_geometry.reference'));
            },
            entityIds: [entity.id],
            dynamicFixes: dynamicFixes
        });
    }

    function lineToAreaDynamicFixes(context) {

        var convertOnClick;

        var entityId = this.entityIds[0];
        var entity = context.entity(entityId);
        var tags = Object.assign({}, entity.tags);  // shallow copy
        delete tags.area;
        if (!osmTagSuggestingArea(tags)) {
            // if removing the area tag would make this a line, offer that as a quick fix
            convertOnClick = function(context) {
                var entityId = this.issue.entityIds[0];
                var entity = context.entity(entityId);
                var tags = Object.assign({}, entity.tags);  // shallow copy
                if (tags.area) {
                    delete tags.area;
                }
                context.perform(
                    actionChangeTags(entityId, tags),
                    t('issues.fix.convert_to_line.annotation')
                );
            };
        }

        return [
            new validationIssueFix({
                icon: 'iD-icon-line',
                title: t.html('issues.fix.convert_to_line.title'),
                onClick: convertOnClick
            })
        ];
    }

    function extractPointDynamicFixes(context) {

        var entityId = this.entityIds[0];

        var extractOnClick = null;
        if (!context.hasHiddenConnections(entityId)) {

            extractOnClick = function(context) {
                var entityId = this.issue.entityIds[0];
                var action = actionExtract(entityId);
                context.perform(
                    action,
                    t('operations.extract.annotation', { n: 1 })
                );
                // re-enter mode to trigger updates
                context.enter(modeSelect(context, [action.getExtractedNodeID()]));
            };
        }

        return [
            new validationIssueFix({
                icon: 'iD-operation-extract',
                title: t.html('issues.fix.extract_point.title'),
                onClick: extractOnClick
            })
        ];
    }

    function unclosedMultipolygonPartIssues(entity, graph) {

        if (entity.type !== 'relation' ||
            !entity.isMultipolygon() ||
            entity.isDegenerate() ||
            // cannot determine issues for incompletely-downloaded relations
            !entity.isComplete(graph)) return [];

        var sequences = osmJoinWays(entity.members, graph);

        var issues = [];

        for (var i in sequences) {
            var sequence = sequences[i];

            if (!sequence.nodes) continue;

            var firstNode = sequence.nodes[0];
            var lastNode = sequence.nodes[sequence.nodes.length - 1];

            // part is closed if the first and last nodes are the same
            if (firstNode === lastNode) continue;

            var issue = new validationIssue({
                type: type,
                subtype: 'unclosed_multipolygon_part',
                severity: 'warning',
                message: function(context) {
                    var entity = context.hasEntity(this.entityIds[0]);
                    return entity ? t.html('issues.unclosed_multipolygon_part.message', {
                        feature: utilDisplayLabel(entity, context.graph())
                    }) : '';
                },
                reference: showReference,
                loc: sequence.nodes[0].loc,
                entityIds: [entity.id],
                hash: sequence.map(function(way) {
                    return way.id;
                }).join()
            });
            issues.push(issue);
        }

        return issues;

        function showReference(selection) {
            selection.selectAll('.issue-reference')
                .data([0])
                .enter()
                .append('div')
                .attr('class', 'issue-reference')
                .html(t.html('issues.unclosed_multipolygon_part.reference'));
        }
    }

    var validation = function checkMismatchedGeometry(entity, graph) {
        var vertexPoint = vertexPointIssue(entity, graph);
        if (vertexPoint) return [vertexPoint];

        var lineAsArea = lineTaggedAsAreaIssue(entity);
        if (lineAsArea) return [lineAsArea];

        var mismatch = otherMismatchIssue(entity, graph);
        if (mismatch) return [mismatch];

        return unclosedMultipolygonPartIssues(entity, graph);
    };

    validation.type = type;

    return validation;
}
<|endoftext|>


<commit_before>Posts = new Meteor.Collection('posts');

//Posts.allow({
//    insert: function (userId, doc) {
//        // only allow posting if you are logged in
//        return !!userId;
//    }
//});

Posts.allow({
  update: ownsDocument,
  remove: ownsDocument
});

Meteor.methods({
    post: function (postAttributes) {
        var user = Meteor.user(),
            postWithSameLink = Posts.findOne({url: postAttributes.url});
        // ensure the user is logged in
        if (!user)
            throw new Meteor.Error(401, "You need to login to post new stories");
        // ensure the post has a title
        if (!postAttributes.title)
            throw new Meteor.Error(422, 'Please fill in a headline');
        // check that there are no previous posts with the same link
        if (postAttributes.url && postWithSameLink) {
            throw new Meteor.Error(302, 'This link has already been posted', postWithSameLink._id);
        }
//        // pick out the whitelisted keys
//        var post = _.extend(_.pick(postAttributes, 'url', 'title', 'message'), {
//            userId: user._id,
//            author: user.username,
//            submitted: new Date().getTime()
//        });
//        var postId = Posts.insert(post);
//        return postId;

        var post = _.extend(_.pick(postAttributes, 'url', 'message'), {
            title: postAttributes.title + (this.isSimulation ? '(client)' : '(server)'),
            userId: user._id,
            author: user.username,
            submitted: new Date().getTime()
        });

        // wait for 5 seconds
        if (!this.isSimulation) {
            var Future = Npm.require('fibers/future');
            var future = new Future();
            Meteor.setTimeout(function () {
                future.return();
            }, 5 * 1000);
            future.wait();
        }
        var postId = Posts.insert(post);
        return postId;
    }
});<commit_msg>Only allow changing certain fields of posts.<commit_after>Posts = new Meteor.Collection('posts');

//Posts.allow({
//    insert: function (userId, doc) {
//        // only allow posting if you are logged in
//        return !!userId;
//    }
//});

Posts.allow({
    update: ownsDocument,
    remove: ownsDocument
});

Posts.deny({
    update: function (userId, post, fieldNames) {
        // may only edit the following two fields:
        return (_.without(fieldNames, 'url', 'title').length > 0);
    }
});

Meteor.methods({
    post: function (postAttributes) {
        var user = Meteor.user(),
            postWithSameLink = Posts.findOne({url: postAttributes.url});
        // ensure the user is logged in
        if (!user)
            throw new Meteor.Error(401, "You need to login to post new stories");
        // ensure the post has a title
        if (!postAttributes.title)
            throw new Meteor.Error(422, 'Please fill in a headline');
        // check that there are no previous posts with the same link
        if (postAttributes.url && postWithSameLink) {
            throw new Meteor.Error(302, 'This link has already been posted', postWithSameLink._id);
        }
//        // pick out the whitelisted keys
//        var post = _.extend(_.pick(postAttributes, 'url', 'title', 'message'), {
//            userId: user._id,
//            author: user.username,
//            submitted: new Date().getTime()
//        });
//        var postId = Posts.insert(post);
//        return postId;

        var post = _.extend(_.pick(postAttributes, 'url', 'message'), {
            title: postAttributes.title + (this.isSimulation ? '(client)' : '(server)'),
            userId: user._id,
            author: user.username,
            submitted: new Date().getTime()
        });

        // wait for 5 seconds
        if (!this.isSimulation) {
            var Future = Npm.require('fibers/future');
            var future = new Future();
            Meteor.setTimeout(function () {
                future.return();
            }, 5 * 1000);
            future.wait();
        }
        var postId = Posts.insert(post);
        return postId;
    }
});<|endoftext|>


<commit_before>(function($) {

/* General Utils
----------------------------------------------------------------------------------------------------------------------*/

$.simulateByPoint = function(type, options) {
	var docEl = $(document);
	var point = options.point;
	var clientX, clientY;
	var node;

	if (point) {
		clientX = point.left - docEl.scrollLeft();
		clientY = point.top - docEl.scrollTop();
		node = document.elementFromPoint(clientX, clientY);
		$(node).simulate(type, options);
	}
};

/* Touch
----------------------------------------------------------------------------------------------------------------------*/

var origSimulateEvent = $.simulate.prototype.simulateEvent;
var touchUID = Date.now();

$.simulate.prototype.simulateEvent = function(elem, type, options) {
	if (/^touch/.test(type)) {
		return this.simulateTouchEvent(elem, type, options);
	}
	else {
		return origSimulateEvent.apply(this, arguments);
	}
};

$.simulate.prototype.simulateTouchEvent = function(elem, type, options) {
	// http://stackoverflow.com/a/29019278/96342
	var event = document.createEvent('Event');
	event.initEvent(type, true, true); // cancelable, bubbleable
	event.touches = [{
		target: elem,
		identifier: touchUID++,
		pageX: options.clientX,
		pageY: options.clientY,
		screenX: options.clientX,
		screenY: options.clientY,
		clientX: options.clientX,
		clientY: options.clientY
	}];
	this.dispatchEvent(elem, type, event, options);
};

$.simulateTouchClick = function(elem) {
	var $elem = $(elem);
	var clientCoords = {
		clientX: $elem.offset().left,
		clientY: $elem.offset().top
	};
	$elem.simulate('mousemove', clientCoords);
	$elem.simulate('mousedown', clientCoords);
	$elem.simulate('mouseup', clientCoords);
	$elem.simulate('click', clientCoords);
};

/* Drag-n-drop
----------------------------------------------------------------------------------------------------------------------*/

var DEBUG_DELAY = 500;
var DEBUG_MIN_DURATION = 2000;
var DEBUG_MIN_MOVES = 100;
var DRAG_DEFAULTS = {
	point: null, // the start point
	localPoint: { left: '50%', top: '50%' },
	end: null, // can be a point or an el
	localEndPoint: { left: '50%', top: '50%' },
	dx: 0,
	dy: 0,
	moves: 5,
	duration: 100 // ms
};

var dragStackCnt = 0;

$.simulate.prototype.simulateDrag = function() {
	var targetNode = this.target;
	var targetEl = $(targetNode);
	var options = $.extend({}, DRAG_DEFAULTS, this.options);
	var dx = options.dx;
	var dy = options.dy;
	var duration = options.duration;
	var moves = options.moves;
	var startPoint;
	var endEl;
	var endPoint;
	var localPoint;
	var offset;

	// compute start point
	if (options.point) {
		startPoint = options.point;
	}
	else {
		localPoint = normalizeElPoint(options.localPoint, targetEl);
		offset = targetEl.offset();
		startPoint = {
			left: offset.left + localPoint.left,
			top: offset.top + localPoint.top
		};
	}

	// compute end point
	if (options.end) {
		if (isPoint(options.end)) {
			endPoint = options.end;
		}
		else { // assume options.end is an element
			endEl = $(options.end);
			localPoint = normalizeElPoint(options.localEndPoint, endEl);
			offset = endEl.offset();
			endPoint = {
				left: offset.left + localPoint.left,
				top: offset.top + localPoint.top
			};
		}
	}

	if (endPoint) {
		dx = endPoint.left - startPoint.left;
		dy = endPoint.top - startPoint.top;
	}

	moves = Math.max(moves, options.debug ? DEBUG_MIN_MOVES : 1);
	duration = Math.max(duration, options.debug ? DEBUG_MIN_DURATION : 10);

	simulateDrag(
		this,
		targetNode,
		startPoint,
		dx,
		dy,
		moves,
		duration,
		options
	);
};

function simulateDrag(self, targetNode, startPoint, dx, dy, moveCnt, duration, options) {
	var debug = options.debug;
	var isTouch = options.isTouch;
	var docNode = targetNode.ownerDocument;
	var docEl = $(docNode);
	var waitTime = duration / moveCnt;
	var moveIndex = 0;
	var clientCoords;
	var intervalId;
	var dotEl;
	var dragId;

	if (debug) {
		dotEl = $('<div>')
			.css({
				position: 'absolute',
				zIndex: 99999,
				border: '5px solid red',
				borderRadius: '5px',
				margin: '-5px 0 0 -5px'
			})
			.appendTo('body');
	}

	function updateCoords() {
		var progress = moveIndex / moveCnt;
		var left = startPoint.left + dx * progress;
		var top = startPoint.top + dy * progress;

		clientCoords = {
			clientX: left - docEl.scrollLeft(),
			clientY: top - docEl.scrollTop()
		};

		if (debug) {
			dotEl.css({ left: left, top: top });
		}
	}

	function startDrag() {
		updateCoords();
		dragId = ++dragStackCnt;

		// simulate a drag-start only if another drag isn't already happening
		if (dragStackCnt === 1) {
			self.simulateEvent(targetNode, isTouch ? 'touchstart' : 'mousedown', clientCoords);
		}

		var delay = options.delay || 0;
		if (debug) {
			delay = Math.max(delay, DEBUG_DELAY);
		}

		if (delay) {
			setTimeout(function() {
				startMoving();
			}, delay);
		}
		else {
			startMoving();
		}
	}

	function startMoving() {
		intervalId = setInterval(tick, waitTime);
	}

	function tick() { // called one interval after start
		moveIndex++;
		updateCoords(); // update clientCoords before mousemove

		self.simulateEvent(docNode, isTouch ? 'touchmove' : 'mousemove', clientCoords);

		if (moveIndex >= moveCnt) {
			stopMoving();
		}
	}

	function stopMoving() {
		clearInterval(intervalId);
		if (debug) {
			setTimeout(function() {
				dotEl.remove(); // do this before calling stopDrag/callback. don't want dot picked up by elementFromPoint
				stopDrag();
			}, DEBUG_DELAY);
		}
		else {
			stopDrag();
		}
	}

	function stopDrag() { // progress at 1, coords already up to date at this point

		(options.onBeforeRelease || function() {})();

		// only simulate a drop if the current drag is still the active one.
		// otherwise, this means another drag has begun via onBeforeRelease.
		if (dragId === dragStackCnt) {
			if ($.contains(docNode, targetNode)) {
				self.simulateEvent(targetNode, isTouch ? 'touchend' : 'mouseup', clientCoords);
				self.simulateEvent(targetNode, 'click', clientCoords);
			}
			else {
				self.simulateEvent(docNode, isTouch ? 'touchend' : 'mouseup', clientCoords);
			}
		}

		dragStackCnt--;
		(options.onRelease || options.callback || function() {})(); // TODO: deprecate "callback" ?
	}

	startDrag();
}

function normalizeElPoint(point, el) {
	var left = point.left;
	var top = point.top;

	if (/%$/.test(left)) {
		left = parseInt(left) / 100 * el.outerWidth();
	}
	if (/%$/.test(top)) {
		top = parseInt(top) / 100 * el.outerHeight();
	}

	return { left: left, top: top };
}

function isPoint(input) {
	return typeof input === 'object' && // `in` operator only works on objects
		'left' in input && 'top' in input;
}

})(jQuery);<commit_msg>improve simulateTouchClick<commit_after>(function($) {

/* General Utils
----------------------------------------------------------------------------------------------------------------------*/

$.simulateByPoint = function(type, options) {
	var docEl = $(document);
	var point = options.point;
	var clientX, clientY;
	var node;

	if (point) {
		clientX = point.left - docEl.scrollLeft();
		clientY = point.top - docEl.scrollTop();
		node = document.elementFromPoint(clientX, clientY);
		$(node).simulate(type, options);
	}
};

/* Touch
----------------------------------------------------------------------------------------------------------------------*/

var origSimulateEvent = $.simulate.prototype.simulateEvent;
var touchUID = Date.now();

$.simulate.prototype.simulateEvent = function(elem, type, options) {
	if (/^touch/.test(type)) {
		return this.simulateTouchEvent(elem, type, options);
	}
	else {
		return origSimulateEvent.apply(this, arguments);
	}
};

$.simulate.prototype.simulateTouchEvent = function(elem, type, options) {
	// http://stackoverflow.com/a/29019278/96342
	var event = document.createEvent('Event');
	event.initEvent(type, true, true); // cancelable, bubbleable
	event.touches = [{
		target: elem,
		identifier: touchUID++,
		pageX: options.clientX,
		pageY: options.clientY,
		screenX: options.clientX,
		screenY: options.clientY,
		clientX: options.clientX,
		clientY: options.clientY
	}];
	this.dispatchEvent(elem, type, event, options);
};

$.simulateTouchClick = function(elem) {
	var $elem = $(elem);
	var clientCoords = {
		clientX: $elem.offset().left + $elem.outerWidth() / 2,
		clientY: $elem.offset().top + $elem.outerHeight() / 2
	};
	$elem.simulate('touchstart', clientCoords);
	$elem.simulate('touchend', clientCoords);
	$elem.simulate('mousemove', clientCoords);
	$elem.simulate('mousedown', clientCoords);
	$elem.simulate('mouseup', clientCoords);
	$elem.simulate('click', clientCoords);
};

/* Drag-n-drop
----------------------------------------------------------------------------------------------------------------------*/

var DEBUG_DELAY = 500;
var DEBUG_MIN_DURATION = 2000;
var DEBUG_MIN_MOVES = 100;
var DRAG_DEFAULTS = {
	point: null, // the start point
	localPoint: { left: '50%', top: '50%' },
	end: null, // can be a point or an el
	localEndPoint: { left: '50%', top: '50%' },
	dx: 0,
	dy: 0,
	moves: 5,
	duration: 100 // ms
};

var dragStackCnt = 0;

$.simulate.prototype.simulateDrag = function() {
	var targetNode = this.target;
	var targetEl = $(targetNode);
	var options = $.extend({}, DRAG_DEFAULTS, this.options);
	var dx = options.dx;
	var dy = options.dy;
	var duration = options.duration;
	var moves = options.moves;
	var startPoint;
	var endEl;
	var endPoint;
	var localPoint;
	var offset;

	// compute start point
	if (options.point) {
		startPoint = options.point;
	}
	else {
		localPoint = normalizeElPoint(options.localPoint, targetEl);
		offset = targetEl.offset();
		startPoint = {
			left: offset.left + localPoint.left,
			top: offset.top + localPoint.top
		};
	}

	// compute end point
	if (options.end) {
		if (isPoint(options.end)) {
			endPoint = options.end;
		}
		else { // assume options.end is an element
			endEl = $(options.end);
			localPoint = normalizeElPoint(options.localEndPoint, endEl);
			offset = endEl.offset();
			endPoint = {
				left: offset.left + localPoint.left,
				top: offset.top + localPoint.top
			};
		}
	}

	if (endPoint) {
		dx = endPoint.left - startPoint.left;
		dy = endPoint.top - startPoint.top;
	}

	moves = Math.max(moves, options.debug ? DEBUG_MIN_MOVES : 1);
	duration = Math.max(duration, options.debug ? DEBUG_MIN_DURATION : 10);

	simulateDrag(
		this,
		targetNode,
		startPoint,
		dx,
		dy,
		moves,
		duration,
		options
	);
};

function simulateDrag(self, targetNode, startPoint, dx, dy, moveCnt, duration, options) {
	var debug = options.debug;
	var isTouch = options.isTouch;
	var docNode = targetNode.ownerDocument;
	var docEl = $(docNode);
	var waitTime = duration / moveCnt;
	var moveIndex = 0;
	var clientCoords;
	var intervalId;
	var dotEl;
	var dragId;

	if (debug) {
		dotEl = $('<div>')
			.css({
				position: 'absolute',
				zIndex: 99999,
				border: '5px solid red',
				borderRadius: '5px',
				margin: '-5px 0 0 -5px'
			})
			.appendTo('body');
	}

	function updateCoords() {
		var progress = moveIndex / moveCnt;
		var left = startPoint.left + dx * progress;
		var top = startPoint.top + dy * progress;

		clientCoords = {
			clientX: left - docEl.scrollLeft(),
			clientY: top - docEl.scrollTop()
		};

		if (debug) {
			dotEl.css({ left: left, top: top });
		}
	}

	function startDrag() {
		updateCoords();
		dragId = ++dragStackCnt;

		// simulate a drag-start only if another drag isn't already happening
		if (dragStackCnt === 1) {
			self.simulateEvent(targetNode, isTouch ? 'touchstart' : 'mousedown', clientCoords);
		}

		var delay = options.delay || 0;
		if (debug) {
			delay = Math.max(delay, DEBUG_DELAY);
		}

		if (delay) {
			setTimeout(function() {
				startMoving();
			}, delay);
		}
		else {
			startMoving();
		}
	}

	function startMoving() {
		intervalId = setInterval(tick, waitTime);
	}

	function tick() { // called one interval after start
		moveIndex++;
		updateCoords(); // update clientCoords before mousemove

		self.simulateEvent(docNode, isTouch ? 'touchmove' : 'mousemove', clientCoords);

		if (moveIndex >= moveCnt) {
			stopMoving();
		}
	}

	function stopMoving() {
		clearInterval(intervalId);
		if (debug) {
			setTimeout(function() {
				dotEl.remove(); // do this before calling stopDrag/callback. don't want dot picked up by elementFromPoint
				stopDrag();
			}, DEBUG_DELAY);
		}
		else {
			stopDrag();
		}
	}

	function stopDrag() { // progress at 1, coords already up to date at this point

		(options.onBeforeRelease || function() {})();

		// only simulate a drop if the current drag is still the active one.
		// otherwise, this means another drag has begun via onBeforeRelease.
		if (dragId === dragStackCnt) {
			if ($.contains(docNode, targetNode)) {
				self.simulateEvent(targetNode, isTouch ? 'touchend' : 'mouseup', clientCoords);
				self.simulateEvent(targetNode, 'click', clientCoords);
			}
			else {
				self.simulateEvent(docNode, isTouch ? 'touchend' : 'mouseup', clientCoords);
			}
		}

		dragStackCnt--;
		(options.onRelease || options.callback || function() {})(); // TODO: deprecate "callback" ?
	}

	startDrag();
}

function normalizeElPoint(point, el) {
	var left = point.left;
	var top = point.top;

	if (/%$/.test(left)) {
		left = parseInt(left) / 100 * el.outerWidth();
	}
	if (/%$/.test(top)) {
		top = parseInt(top) / 100 * el.outerHeight();
	}

	return { left: left, top: top };
}

function isPoint(input) {
	return typeof input === 'object' && // `in` operator only works on objects
		'left' in input && 'top' in input;
}

})(jQuery);<|endoftext|>


<commit_before>"use strict";

const registry = new Map();

// by default, act like a spy that populates registry
const mockNpmPublish = jest.fn((pkg, tag) => {
  registry.set(pkg.name, tag);

  return Promise.resolve();
});

// keep test data isolated
afterEach(() => {
  registry.clear();
});

module.exports = mockNpmPublish;
module.exports.registry = registry;
<commit_msg>chore(mocks): Add npmPublish.order() helper<commit_after>"use strict";

const registry = new Map();

// by default, act like a spy that populates registry
const mockNpmPublish = jest.fn((pkg, tag) => {
  registry.set(pkg.name, tag);

  return Promise.resolve();
});

// a convenient format for assertions
function order() {
  return Array.from(registry.keys());
}

// keep test data isolated
afterEach(() => {
  registry.clear();
});

module.exports = mockNpmPublish;
module.exports.order = order;
module.exports.registry = registry;
<|endoftext|>


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

    var program = require('./../src/customcommander');
    var fs = require('fs');
    var cordovacontroller = require('./../src/cordovacontroller');
    var helper = require('./../src/clihelper');
    var request = require('request');

    var archive, archivePath, widgetName, phonegapAccessToken, widgetPhonegapId;

    var checkBuildStatus = function (url, platforms) {
        var ind = 0;
        var TIMEOUT = 60;
        var waitSymbols = [
            '-',
            '\\',
            '|',
            '/'
        ];
        var indicatorId = setInterval(function () {
            process.stdout.write('\b\r' + waitSymbols[ind++ % 4]);

            if (ind % TIMEOUT === 0) {
                request(url, function (error, response) {
                    var body, isEveryPlatformBuilded;

                    if (error) {
                        console.error(error.toString());
                        process.exit(1);
                    }

                    body = JSON.parse(response.body);
                    isEveryPlatformBuilded = platforms.every(function (platform) {
                        return body.status[platform] !== 'pending';
                    });
                    if (isEveryPlatformBuilded) {
                        clearInterval(indicatorId);
                        console.log('\rBuild finished.');
                    }
                });
                ind = 1;
            }
        }, 100);
    };
    var startBuild = function () {
        var platforms;
        var checkUrl = 'https://build.phonegap.com/api/v1/apps/' + widgetPhonegapId;
        var url = checkUrl + '/build';

        if (program.platform) {
            if (cordovacontroller.REMOTE_SUPPORTED_PLATFORMS.indexOf(program.platform) !== -1) {
                platforms = [program.platform];
                url += '/' + program.platform;
            } else {
                console.error('Unsupported platform: ' + program.platform);
                process.exit(1);
            }
        }
        checkUrl += '?access_token=' + phonegapAccessToken;
        url += '?access_token=' + phonegapAccessToken;

        request.post(url, function (error, resp) {
            if (error) {
                console.error(error.toString());
                process.exit(1);
            }

            if (resp.statusCode === 202) {
                console.log('Build successfully started.');
                checkBuildStatus(checkUrl, platforms || cordovacontroller.REMOTE_SUPPORTED_PLATFORMS);
            } else {
                console.log('Unable to start build.');
                process.exit(1);
            }
        });
    };
    var updateCallback = function (error, resp) {
        var body;

        fs.unlink(cordovacontroller.getArchivePath(process.cwd()));
        if (error) {
            console.error(error.toString());
            process.exit(1);
        }

        body = JSON.parse(resp.body);
        switch (resp.statusCode) {
            case 200:
                startBuild();
                break;
            case 401:
                console.log('Invalid PhoneGap Id');
                process.exit(1);
                break;
            case 404:
                console.log(body.error);
                process.exit(1);
                break;
            default:
                console.log('Unknown error from build.phonegap.com');
                process.exit(1);
                break;
        }
    };

    program
        .arguments('[name]')
        .option('--platform <platform>', 'Start build for specific platform')
        .option('--noupload', 'Start build without upload')
        .action(function (name) {
            widgetName = name;
        })
        .parse(process.argv);

    helper.workByWidgetName(widgetName);
    helper.checkPhonegapAuthorization(); //will terminate process if not authorized

    phonegapAccessToken = helper.getPhonegapCredentials().access_token;

    if (typeof widgetName === 'undefined') {
        widgetName = helper.getWidgetNameByPath(process.cwd());
    }
    widgetPhonegapId = helper.getWidgetPhonegapId(widgetName); //will terminate process if doesn't have id
    archivePath = cordovacontroller.getArchivePath(process.cwd());

    if (!program.noupload) {
        archive = fs.createWriteStream(archivePath);
        archive.on('close', function () {
            request.put('https://build.phonegap.com/api/v1/apps/' + widgetPhonegapId +
                    '?access_token=' + phonegapAccessToken, updateCallback)
                .form()
                .append('file', fs.createReadStream(archivePath));
        });
        cordovacontroller.createArchive(archive);
    } else {
        startBuild();
    }
})();<commit_msg>Add link for status check<commit_after>(function () {
    'use strict';

    var program = require('./../src/customcommander');
    var fs = require('fs');
    var cordovacontroller = require('./../src/cordovacontroller');
    var helper = require('./../src/clihelper');
    var request = require('request');

    var archive, archivePath, widgetName, phonegapAccessToken, widgetPhonegapId;

    var checkBuildStatus = function (url, statusUrl, platforms) {
        var ind = 0;
        var TIMEOUT = 60;
        var waitSymbols = [
            '-',
            '\\',
            '|',
            '/'
        ];
        var indicatorId = setInterval(function () {
            process.stdout.write('\b\r' + waitSymbols[ind++ % 4]);

            if (ind % TIMEOUT === 0) {
                request(url, function (error, response) {
                    var body, isEveryPlatformBuilded;

                    if (error) {
                        console.error(error.toString());
                        process.exit(1);
                    }

                    body = JSON.parse(response.body);
                    isEveryPlatformBuilded = platforms.every(function (platform) {
                        return body.status[platform] !== 'pending';
                    });
                    if (isEveryPlatformBuilded) {
                        clearInterval(indicatorId);
                        console.log('\rBuild finished.');
                        console.log('Check status at: ' + statusUrl);
                    }
                });
                ind = 1;
            }
        }, 100);
    };
    var startBuild = function () {
        var platforms;
        var checkUrl = 'https://build.phonegap.com/api/v1/apps/' + widgetPhonegapId;
        var statusUrl = 'https://build.phonegap.com/apps/' + widgetPhonegapId;
        var url = checkUrl + '/build';

        if (program.platform) {
            if (cordovacontroller.REMOTE_SUPPORTED_PLATFORMS.indexOf(program.platform) !== -1) {
                platforms = [program.platform];
                url += '/' + program.platform;
            } else {
                console.error('Unsupported platform: ' + program.platform);
                process.exit(1);
            }
        }
        checkUrl += '?access_token=' + phonegapAccessToken;
        url += '?access_token=' + phonegapAccessToken;

        request.post(url, function (error, resp) {
            if (error) {
                console.error(error.toString());
                process.exit(1);
            }

            if (resp.statusCode === 202) {
                console.log('Build successfully started.');
                checkBuildStatus(checkUrl, statusUrl, platforms || cordovacontroller.REMOTE_SUPPORTED_PLATFORMS);
            } else {
                console.log('Unable to start build.');
                process.exit(1);
            }
        });
    };
    var updateCallback = function (error, resp) {
        var body;

        fs.unlink(cordovacontroller.getArchivePath(process.cwd()));
        if (error) {
            console.error(error.toString());
            process.exit(1);
        }

        body = JSON.parse(resp.body);
        switch (resp.statusCode) {
            case 200:
                startBuild();
                break;
            case 401:
                console.log('Invalid PhoneGap Id');
                process.exit(1);
                break;
            case 404:
                console.log(body.error);
                process.exit(1);
                break;
            default:
                console.log('Unknown error from build.phonegap.com');
                process.exit(1);
                break;
        }
    };

    program
        .arguments('[name]')
        .option('--platform <platform>', 'Start build for specific platform')
        .option('--noupload', 'Start build without upload')
        .action(function (name) {
            widgetName = name;
        })
        .parse(process.argv);

    helper.workByWidgetName(widgetName);
    helper.checkPhonegapAuthorization(); //will terminate process if not authorized

    phonegapAccessToken = helper.getPhonegapCredentials().access_token;

    if (typeof widgetName === 'undefined') {
        widgetName = helper.getWidgetNameByPath(process.cwd());
    }
    widgetPhonegapId = helper.getWidgetPhonegapId(widgetName); //will terminate process if doesn't have id
    archivePath = cordovacontroller.getArchivePath(process.cwd());

    if (!program.noupload) {
        archive = fs.createWriteStream(archivePath);
        archive.on('close', function () {
            request.put('https://build.phonegap.com/api/v1/apps/' + widgetPhonegapId +
                    '?access_token=' + phonegapAccessToken, updateCallback)
                .form()
                .append('file', fs.createReadStream(archivePath));
        });
        cordovacontroller.createArchive(archive);
    } else {
        startBuild();
    }
})();
<|endoftext|>


<commit_before>/**
 * @fileOverview Search Folder Manager for Thunderbird
 * @author       ClearCode Inc.
 * @version      4
 *
 * @description
 *   Usage:
 *     Components.utills.import('resource://.../SerchFolderManager.jsm');
 *     new SerchFolderManager([
 *       { label:         'Important',
 *         subjectPrefix: '[Important]' },
 *       { label:         'FYI',
 *         conditions:    'OR (subject,begins with,FYI) OR (subject,begins with,[FYI])' },
 *       { label:         'DenyHosts Reports',
 *         subject:       'DenyHosts Report from www.example.com' },
 *       ...
 *     ]);
 *
 * @license
 *   The MIT License, Copyright (c) 2014-2018 ClearCode Inc.
 * @url https://github.com/clear-code/js-codemodule-search-folder-manager
 */

var EXPORTED_SYMBOLS = ['SearchFolderManager'];

var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;

Cu.import('resource://gre/modules/XPCOMUtils.jsm');

XPCOMUtils.defineLazyServiceGetter(this,
                                   'AccountManager',
                                   '@mozilla.org/messenger/account-manager;1',
                                   'nsIMsgAccountManager');
XPCOMUtils.defineLazyServiceGetter(this,
                                   'MailSession',
                                   '@mozilla.org/messenger/services/session;1',
                                   'nsIMsgMailSession');

XPCOMUtils.defineLazyModuleGetter(this,
                                  'Services',
                                  'resource://gre/modules/Services.jsm');
XPCOMUtils.defineLazyModuleGetter(this,
                                  'MailServices',
                                  'resource:///modules/mailServices.js');
XPCOMUtils.defineLazyModuleGetter(this,
                                  'VirtualFolderHelper',
                                  'resource:///modules/virtualFolderWrapper.js');

/**
 * Initializes a search folder manager for given folder
 * definitions (search conditions).
 *
 * @param Array of Definition
 *        An array of search folder definitions (search conditions).
 *
 * Definition:
 *   @param label         String: the name of the folder
 *   @param conditions    String: the search conditions (optional)
 *   @param subject       String: the subject to be matched (optional)
 *   @param subjectPrefix String: the prefix of the subject to be matched (optional)
 *   @param searchTargets String: "all" or comma-separated URIs of mail folders (optional, default=all)
 */
function SearchFolderManager(aDefinitions) {
  this.definitions = aDefinitions;

  Services.obs.addObserver(this, 'mail-tabs-session-restored', false);
}
SearchFolderManager.prototype = {
  get allAccounts() {
    return this.toArray(AccountManager.accounts, Ci.nsIMsgAccount);
  },
  get allAccountKeys() {
    return this.allAccounts.map(function(aAccount) {
      return this.getAccountKey(aAccount);
    }, this).filter(function(aKey) {
      return aKey != '';
    });
  },
  toArray: function (aEnumerator, aInterface) {
    aInterface = aInterface || Ci.nsISupports;
    var array = [];
    if (Ci.nsISupportsArray && aEnumerator instanceof Ci.nsISupportsArray) {
      let count = aEnumerator.Count();
      for (let i = 0; i < count; i++) {
        array.push(aEnumerator.QueryElementAt(i, aInterface));
      }
    } else if (aEnumerator instanceof Ci.nsIArray) {
      let count = aEnumerator.length;
      for (let i = 0; i < count; i++) {
        array.push(aEnumerator.queryElementAt(i, aInterface));
      }
    } else if (aEnumerator instanceof Ci.nsISimpleEnumerator) {
      while (aEnumerator.hasMoreElements()) {
        array.push(aEnumerator.getNext().QueryInterface(aInterface));
      }
    }
    return array;
  },
  getDescendants: function(aRoot) {
    var folders = [];
    if ('descendants' in aRoot) { // Thunderbird 24
      let descendants = aRoot.descendants;
      for (let i = 0, maxi = descendants.length; i < maxi; i++) {
        let folder = descendants.queryElementAt(i, Ci.nsIMsgFolder);
        folders.push(folder);
      }
    } else { // Thunderbird 17 or olders
      let descendants = Cc['@mozilla.org/supports-array;1']
                          .createInstance(Ci.nsISupportsArray);
      aRoot.ListDescendents(descendants);
      for (let i = 0, maxi = descendants.Count(); i < maxi; i++) {
        let folder = descendants.GetElementAt(i).QueryInterface(Ci.nsIMsgFolder);
        folders.push(folder);
      }
    }
    return folders;
  },

  observe: function(aSubject, aTopic, aData) {
    Services.obs.removeObserver(this, 'mail-tabs-session-restored');
    // This must be done after mail-tabs-session-restored.
    // Otherwise, non-ASCII search conditions are not saved correctly.
    this.buildSearchFolders();

    this.startListenFolderChanges();
  },
  startListenFolderChanges: function() {
    var notifyFlags = Ci.nsIFolderListener.added |
                        Ci.nsIFolderListener.removed;
    MailSession.AddFolderListener(this, notifyFlags);
  },
  // nsIFolderListener
  OnItemAdded: function(aParent, aItem) {
    try {
      aItem = aItem.QueryInterface(Ci.nsIMsgFolder);
      if (aItem.flags & Ci.nsMsgFolderFlags.Virtual)
        return;
    } catch(e) {
      return;
    }
    this.buildSearchFoldersIn(aItem.rootFolder, {
      addedFolder: aItem.URI
    });
  },
  OnItemRemoved: function(aParent, aItem) {
    try {
      aItem = aItem.QueryInterface(Ci.nsIMsgFolder);
      if (aItem.flags & Ci.nsMsgFolderFlags.Virtual)
        return;
    } catch(e) {
      return;
    }
    this.buildSearchFoldersIn(aItem.rootFolder, {
      removedFolder: aItem.URI
    });
  },
  OnItemPropertyChanged: function() {},
  OnItemIntPropertyChanged: function() {},
  OnItemBoolPropertyChanged: function() {},
  OnItemUnicharPropertyChanged: function() {},
  OnItemEvent: function() {},

  buildSearchFolders: function() {
    this.allAccounts.forEach(function(aAccount) {
      if (!aAccount.incomingServer)
        return;
      this.buildSearchFoldersIn(aAccount.incomingServer.rootMsgFolder);
    }, this);
  },
  buildSearchFoldersIn: function(aRoot, aModification) {
    if (!aRoot)
      return;
    this.definitions.forEach(function(aDefinition) {
      try {
        this.buildSearchFolderIn(aDefinition, aRoot, aModification);
      }
      catch(e) {
        Cu.reportError(e);
      }
    }, this);
  },
  buildSearchFolderIn: function(aDefinition, aRoot, aModification) {
    if (!aDefinition.conditions &&
        !aDefinition.subjectPrefix &&
        !aDefinition.subject ||
        !aRoot)
      return;

    var searchTargets = aDefinition.searchTargets || 'all';
    var searchFolders;
    if (!aModification) {
      searchFolders = this.getSearchFolders(aRoot, searchTargets.split(/[,\s]+/));
      if (!searchFolders)
        return;
    }

    var name = 'autogen-' + aDefinition.label;

    var isCreation = false;
    var isModified = false;
    var virtualFolder;
    try {
      virtualFolder = aRoot.getChildNamed(name);
    } catch(e) {
      try {
        virtualFolder = aRoot.getChildNamed(aDefinition.label);
        if (!(virtualFolder.flags & Ci.nsMsgFolderFlags.Virtual))
          virtualFolder = null;
      } catch(e) {
        // folder not found!
      }
    }
    if (!virtualFolder) {
      isCreation = true;
      isModified = true;
      virtualFolder = aRoot.addSubfolder(name);
      virtualFolder.setFlag(Ci.nsMsgFolderFlags.Virtual);
    }

    // We always have to set prettyName because it is not saved.
    virtualFolder.prettyName = aDefinition.label;

    var wrapper = VirtualFolderHelper.wrapVirtualFolder(virtualFolder);

    var conditions = this.buildSearchCondition(aDefinition);
    if (wrapper.searchString != conditions) {
      wrapper.searchString = conditions;
      isModified = true;
    }
    var currentSearchFolders = wrapper.searchFolders.map(function(aFolder) {
      return aFolder.URI
    }).join('|');
    if (aModification) {
      searchFolders = currentSearchFolders.split('|');
      if (aModification.addedFolder) {
        let index = searchFolders.indexOf(aModification.addedFolder);
        if (index < 0) {
          searchFolders.push(aModification.addedFolder);
        }
      }
      if (aModification.removedFolder) {
        let index = searchFolders.indexOf(aModification.removedFolder);
        if (index > -1) {
          searchFolders.splice(index, 1);
        }
      }
      searchFolders = searchFolders.join('|');
    }
    if (currentSearchFolders != searchFolders) {
      wrapper.searchFolders = searchFolders;
      isModified = true;
    }
    if (isCreation) {
      wrapper.onlineSearch = false;
    }

    if (!isModified)
      return;

    wrapper.cleanUpMessageDatabase();
    if (isCreation) {
      virtualFolder.msgDatabase.Close(true);
      aRoot.NotifyItemAdded(virtualFolder);
    }
    MailServices.accounts.saveVirtualFolders();
  },
  getSearchFolders: function(aRoot, aKeys) {
    var flags = {};
    Object.keys(Ci.nsMsgFolderFlags).forEach(function(aKey) {
      flags[aKey.toLowerCase()] = Ci.nsMsgFolderFlags[aKey];
    });
    var folders = [];
    aKeys.some(function(aKey) {
      aKey = aKey.toLowerCase();
      if (aKey == 'all') {
        folders = this.getDescendants(aRoot).map(function(aFolder) {
          return aFolder.URI;
        });
        return true;
      }
      if (typeof flags[aKey] == 'number') {
        let folder = aRoot.getFolderWithFlags(flags[aKey]);
        if (folder)
          folders.push(folder.URI);
      }
      return false;
    }, this);
    try {
      if (folders.length > 0)
        folders.unshift(aRoot.URI);
    } catch(e) {
    }
    return folders.join('|');
  },
  buildSearchCondition: function(aDefinition) {
    if (aDefinition.conditions)
      return this.UnicodeToUTF8(aDefinition.conditions);

    if (aDefinition.subjectPrefix)
      return 'AND (subject,begins with,' + this.UnicodeToUTF8(aDefinition.subjectPrefix) + ')';

    return 'AND (subject,is,' + this.UnicodeToUTF8(aDefinition.subject) + ')';
  },
  UnicodeToUTF8: function(aString) {
    return unescape(encodeURIComponent(aString));
  }
};
<commit_msg>Suppress unfixable error<commit_after>/**
 * @fileOverview Search Folder Manager for Thunderbird
 * @author       ClearCode Inc.
 * @version      4
 *
 * @description
 *   Usage:
 *     Components.utills.import('resource://.../SerchFolderManager.jsm');
 *     new SerchFolderManager([
 *       { label:         'Important',
 *         subjectPrefix: '[Important]' },
 *       { label:         'FYI',
 *         conditions:    'OR (subject,begins with,FYI) OR (subject,begins with,[FYI])' },
 *       { label:         'DenyHosts Reports',
 *         subject:       'DenyHosts Report from www.example.com' },
 *       ...
 *     ]);
 *
 * @license
 *   The MIT License, Copyright (c) 2014-2018 ClearCode Inc.
 * @url https://github.com/clear-code/js-codemodule-search-folder-manager
 */

var EXPORTED_SYMBOLS = ['SearchFolderManager'];

var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;

Cu.import('resource://gre/modules/XPCOMUtils.jsm');

XPCOMUtils.defineLazyServiceGetter(this,
                                   'AccountManager',
                                   '@mozilla.org/messenger/account-manager;1',
                                   'nsIMsgAccountManager');
XPCOMUtils.defineLazyServiceGetter(this,
                                   'MailSession',
                                   '@mozilla.org/messenger/services/session;1',
                                   'nsIMsgMailSession');

XPCOMUtils.defineLazyModuleGetter(this,
                                  'Services',
                                  'resource://gre/modules/Services.jsm');
XPCOMUtils.defineLazyModuleGetter(this,
                                  'MailServices',
                                  'resource:///modules/mailServices.js');
XPCOMUtils.defineLazyModuleGetter(this,
                                  'VirtualFolderHelper',
                                  'resource:///modules/virtualFolderWrapper.js');

/**
 * Initializes a search folder manager for given folder
 * definitions (search conditions).
 *
 * @param Array of Definition
 *        An array of search folder definitions (search conditions).
 *
 * Definition:
 *   @param label         String: the name of the folder
 *   @param conditions    String: the search conditions (optional)
 *   @param subject       String: the subject to be matched (optional)
 *   @param subjectPrefix String: the prefix of the subject to be matched (optional)
 *   @param searchTargets String: "all" or comma-separated URIs of mail folders (optional, default=all)
 */
function SearchFolderManager(aDefinitions) {
  this.definitions = aDefinitions;

  Services.obs.addObserver(this, 'mail-tabs-session-restored', false);
}
SearchFolderManager.prototype = {
  get allAccounts() {
    return this.toArray(AccountManager.accounts, Ci.nsIMsgAccount);
  },
  get allAccountKeys() {
    return this.allAccounts.map(function(aAccount) {
      return this.getAccountKey(aAccount);
    }, this).filter(function(aKey) {
      return aKey != '';
    });
  },
  toArray: function (aEnumerator, aInterface) {
    aInterface = aInterface || Ci.nsISupports;
    var array = [];
    if (Ci.nsISupportsArray && aEnumerator instanceof Ci.nsISupportsArray) {
      let count = aEnumerator.Count();
      for (let i = 0; i < count; i++) {
        array.push(aEnumerator.QueryElementAt(i, aInterface));
      }
    } else if (aEnumerator instanceof Ci.nsIArray) {
      let count = aEnumerator.length;
      for (let i = 0; i < count; i++) {
        array.push(aEnumerator.queryElementAt(i, aInterface));
      }
    } else if (aEnumerator instanceof Ci.nsISimpleEnumerator) {
      while (aEnumerator.hasMoreElements()) {
        array.push(aEnumerator.getNext().QueryInterface(aInterface));
      }
    }
    return array;
  },
  getDescendants: function(aRoot) {
    var folders = [];
    if ('descendants' in aRoot) { // Thunderbird 24
      let descendants = aRoot.descendants;
      for (let i = 0, maxi = descendants.length; i < maxi; i++) {
        let folder = descendants.queryElementAt(i, Ci.nsIMsgFolder);
        folders.push(folder);
      }
    } else { // Thunderbird 17 or olders
      let descendants = Cc['@mozilla.org/supports-array;1']
                          .createInstance(Ci.nsISupportsArray);
      aRoot.ListDescendents(descendants);
      for (let i = 0, maxi = descendants.Count(); i < maxi; i++) {
        let folder = descendants.GetElementAt(i).QueryInterface(Ci.nsIMsgFolder);
        folders.push(folder);
      }
    }
    return folders;
  },

  observe: function(aSubject, aTopic, aData) {
    Services.obs.removeObserver(this, 'mail-tabs-session-restored');
    // This must be done after mail-tabs-session-restored.
    // Otherwise, non-ASCII search conditions are not saved correctly.
    this.buildSearchFolders();

    this.startListenFolderChanges();
  },
  startListenFolderChanges: function() {
    var notifyFlags = Ci.nsIFolderListener.added |
                        Ci.nsIFolderListener.removed;
    MailSession.AddFolderListener(this, notifyFlags);
  },
  // nsIFolderListener
  OnItemAdded: function(aParent, aItem) {
    try {
      aItem = aItem.QueryInterface(Ci.nsIMsgFolder);
      if (aItem.flags & Ci.nsMsgFolderFlags.Virtual)
        return;
    } catch(e) {
      return;
    }
    this.buildSearchFoldersIn(aItem.rootFolder, {
      addedFolder: aItem.URI
    });
  },
  OnItemRemoved: function(aParent, aItem) {
    try {
      aItem = aItem.QueryInterface(Ci.nsIMsgFolder);
      if (aItem.flags & Ci.nsMsgFolderFlags.Virtual)
        return;
    } catch(e) {
      return;
    }
    this.buildSearchFoldersIn(aItem.rootFolder, {
      removedFolder: aItem.URI
    });
  },
  OnItemPropertyChanged: function() {},
  OnItemIntPropertyChanged: function() {},
  OnItemBoolPropertyChanged: function() {},
  OnItemUnicharPropertyChanged: function() {},
  OnItemEvent: function() {},

  buildSearchFolders: function() {
    this.allAccounts.forEach(function(aAccount) {
      if (!aAccount.incomingServer)
        return;
      this.buildSearchFoldersIn(aAccount.incomingServer.rootMsgFolder);
    }, this);
  },
  buildSearchFoldersIn: function(aRoot, aModification) {
    if (!aRoot)
      return;
    this.definitions.forEach(function(aDefinition) {
      try {
        this.buildSearchFolderIn(aDefinition, aRoot, aModification);
      }
      catch(e) {
        Cu.reportError(e);
      }
    }, this);
  },
  buildSearchFolderIn: function(aDefinition, aRoot, aModification) {
    if (!aDefinition.conditions &&
        !aDefinition.subjectPrefix &&
        !aDefinition.subject ||
        !aRoot)
      return;

    var searchTargets = aDefinition.searchTargets || 'all';
    var searchFolders;
    if (!aModification) {
      searchFolders = this.getSearchFolders(aRoot, searchTargets.split(/[,\s]+/));
      if (!searchFolders)
        return;
    }

    var name = 'autogen-' + aDefinition.label;

    var isCreation = false;
    var isModified = false;
    var virtualFolder;
    try {
      virtualFolder = aRoot.getChildNamed(name);
    } catch(e) {
      try {
        virtualFolder = aRoot.getChildNamed(aDefinition.label);
        if (!(virtualFolder.flags & Ci.nsMsgFolderFlags.Virtual))
          virtualFolder = null;
      } catch(e) {
        // folder not found!
      }
    }
    if (!virtualFolder) {
      isCreation = true;
      isModified = true;
      virtualFolder = aRoot.addSubfolder(name);
      virtualFolder.setFlag(Ci.nsMsgFolderFlags.Virtual);
    }

    // We always have to set prettyName because it is not saved.
    virtualFolder.prettyName = aDefinition.label;

    var wrapper = VirtualFolderHelper.wrapVirtualFolder(virtualFolder);

    var conditions = this.buildSearchCondition(aDefinition);
    if (wrapper.searchString != conditions) {
      wrapper.searchString = conditions;
      isModified = true;
    }
    var currentSearchFolders = wrapper.searchFolders.map(function(aFolder) {
      return aFolder.URI
    }).join('|');
    if (aModification) {
      searchFolders = currentSearchFolders.split('|');
      if (aModification.addedFolder) {
        let index = searchFolders.indexOf(aModification.addedFolder);
        if (index < 0) {
          searchFolders.push(aModification.addedFolder);
        }
      }
      if (aModification.removedFolder) {
        let index = searchFolders.indexOf(aModification.removedFolder);
        if (index > -1) {
          searchFolders.splice(index, 1);
        }
      }
      searchFolders = searchFolders.join('|');
    }
    if (currentSearchFolders != searchFolders) {
      wrapper.searchFolders = searchFolders;
      isModified = true;
    }
    if (isCreation) {
      wrapper.onlineSearch = false;
    }

    if (!isModified)
      return;

    wrapper.cleanUpMessageDatabase();
    if (isCreation) {
      virtualFolder.msgDatabase.Close(true);
      aRoot.NotifyItemAdded(virtualFolder);
    }
    try {
      MailServices.accounts.saveVirtualFolders();
    }
    catch(e) {
      // it raises the error but works as expected, so we simply ignore.
      if (!String(e).includes('File error: Not found'))
        throw e;
    }
  },
  getSearchFolders: function(aRoot, aKeys) {
    var flags = {};
    Object.keys(Ci.nsMsgFolderFlags).forEach(function(aKey) {
      flags[aKey.toLowerCase()] = Ci.nsMsgFolderFlags[aKey];
    });
    var folders = [];
    aKeys.some(function(aKey) {
      aKey = aKey.toLowerCase();
      if (aKey == 'all') {
        folders = this.getDescendants(aRoot).map(function(aFolder) {
          return aFolder.URI;
        });
        return true;
      }
      if (typeof flags[aKey] == 'number') {
        let folder = aRoot.getFolderWithFlags(flags[aKey]);
        if (folder)
          folders.push(folder.URI);
      }
      return false;
    }, this);
    try {
      if (folders.length > 0)
        folders.unshift(aRoot.URI);
    } catch(e) {
    }
    return folders.join('|');
  },
  buildSearchCondition: function(aDefinition) {
    if (aDefinition.conditions)
      return this.UnicodeToUTF8(aDefinition.conditions);

    if (aDefinition.subjectPrefix)
      return 'AND (subject,begins with,' + this.UnicodeToUTF8(aDefinition.subjectPrefix) + ')';

    return 'AND (subject,is,' + this.UnicodeToUTF8(aDefinition.subject) + ')';
  },
  UnicodeToUTF8: function(aString) {
    return unescape(encodeURIComponent(aString));
  }
};
<|endoftext|>


<commit_before>var fieldRegistry = require('kwf/frontend-form/field-registry');
var Field = require('kwf/frontend-form/field/field');
var kwfExtend = require('kwf/extend');

var Checkbox = kwfExtend(Field, {
    initField: function() {
        this.el.select('input').each(function(input) {
            input.on('click', function() {
                this.el.trigger('kwf-form-change', this.getValue());
            }, this);
        }, this);
    },
    clearValue: function() {
        var inp = this.el.find('input');
        inp.get(0).checked = false;
    },
    setValue: function(value) {
        var inp = this.el.find('input');
        inp.get(0).checked = !!value;
    },
    getValue: function(value) {
        var inp = this.el.find('input');
        return inp.get(0).checked;
    }
});

fieldRegistry.register('kwfFormFieldCheckbox', Checkbox);
module.exports = Checkbox;
<commit_msg>Fix javascript problem<commit_after>var $ = require('jQuery');
var fieldRegistry = require('kwf/frontend-form/field-registry');
var Field = require('kwf/frontend-form/field/field');
var kwfExtend = require('kwf/extend');

var Checkbox = kwfExtend(Field, {
    initField: function() {
        this.el.select('input').each(function(input) {
            $(input).on('click', function() {
                this.el.trigger('kwf-form-change', this.getValue());
            }, this);
        }, this);
    },
    clearValue: function() {
        var inp = this.el.find('input');
        inp.get(0).checked = false;
    },
    setValue: function(value) {
        var inp = this.el.find('input');
        inp.get(0).checked = !!value;
    },
    getValue: function(value) {
        var inp = this.el.find('input');
        return inp.get(0).checked;
    }
});

fieldRegistry.register('kwfFormFieldCheckbox', Checkbox);
module.exports = Checkbox;
<|endoftext|>