<commit_before>idrinth.tier = {
    list: { },
    addTagged: function ( name ) {
        if ( !name || !this.list.hasOwnProperty ( name ) || typeof this.list[name] === 'function' || document.getElementById ( 'idrinth-tier-box-' + name ) ) {
            return;
        }
        var make = function ( x, name ) {
            var makeElement = function ( label, number, description ) {
                return {
                    content: label + ' ' + idrinth.ui.formatNumber ( number ),
                    attributes: [ {
                            name: 'title',
                            value: description
                        } ]
                };
            };
            var info = [
                makeElement ( 'FS', this.list[name].fs.nm, 'Fair share' ),
                makeElement ( 'AP', this.list[name].ap, 'Achievement point damage' )
            ];
            if ( this.list[name].os && this.list[name].os.nm ) {
                info.push ( makeElement ( 'OS', this.list[name].os.nm, 'Optimal share' ) );
                info.unshift ( makeElement ( 'MA', this.list[name].nm[this.list[name].nm.length - 1], 'Maximum/highest tier' ) );
                info.unshift ( makeElement ( 'MI', this.list[name].nm[0], 'Minimum/lowest tier' ) );
            }
            info.unshift (
                    {
                        type: 'strong',
                        content: this.list[name].name.replace ( /\(.*$/, '' )
                    } );
            idrinth.ui.body.appendChild ( idrinth.ui.buildElement (
                    {
                        id: 'idrinth-tier-box-' + name,
                        css: 'idrinth-hovering-box idrinth-tier-box',
                        children: [ {
                                children: info
                            } ],
                        attributes: [
                            {
                                name: 'title',
                                value: 'click to close'
                            },
                            {
                                name: 'onclick',
                                value: 'idrinth.ui.removeElement(this.id);idrinth.tier.taggedSlots[\'' + x + '\']=null;'
                            },
                            {
                                name: 'style',
                                value: 'left:' + x + 'px;background-image: url(https://dotd.idrinth.de/static/raid-image-service/' + this.list[name].url + '/);'
                            }
                        ]
                    }
            ) );
        };
        for (var key in this.taggedSlots) {
            if ( this.taggedSots.hasOwnProperty ( key ) && typeof key !== 'function' && this.taggedSlots[key] === null ) {
                return make ( key, name );
            }
        }
        idrinth.alert ( 'There is no space for another tier-box at the moment, please close one first.' );
    },
    taggedSlots: { },
    start: function () {
        'use strict';
        var pos = 1;
        while ( pos < window.innerWidth - 140 * ( Object.keys ().length + 2 ) ) {
            this.taggedSlots[( pos * 140 ).toString ()] = null;
            pos++;
        }
        idrinth.runAjax (
                'https://dotd.idrinth.de/' + idrinth.platform + '/tier-service/',
                function ( text ) {
                    idrinth.tier.import ( text );
                },
                function ( ) {
                    window.setTimeout ( idrinth.tier.start, 10000 );
                },
                function ( ) {
                    window.setTimeout ( idrinth.tier.start, 10000 );
                }
        );
    },
    import: function ( data ) {
        'use strict';
        data = JSON.parse ( data );
        if ( data ) {
            idrinth.tier.list = data;
        } else {
            window.setTimeout ( idrinth.tier.start, 1000 );
        }
    },
    getTierForName: function ( name ) {
        var clearInnerHtml = function clearInnerHtmlF ( elem ) {
            elem.innerHTML = '';
        };

        var makeList = function ( list ) {
            var makeField = function ( listKey, difficulty, ic ) {
                var ln = {
                    type: 'td'
                };
                try {
                    ln.styles = idrinth.tier.list[listKey].os[difficulty] === idrinth.tier[listKey][difficulty][ic] ? 'is-os' : '';
                } catch ( E ) {
                    idrinth.log ( E.toString ( ) );
                }
                try {
                    ln.content = idrinth.ui.formatNumber ( idrinth.tier.list[listKey][difficulty][ic] ) + ' ' +
                            idrinth.tier.list[listKey].epics[difficulty][ic] + 'E';
                } catch ( E2 ) {
                    idrinth.log ( E2.toString ( ) );
                    try {
                        ln.content = idrinth.ui.formatNumber ( idrinth.tier.list[listKey][difficulty][ic] );
                    } catch ( E3 ) {
                        idrinth.log ( E3.toString ( ) );
                    }
                }
                return ln;
            };
            var makeRow = function ( title, dataset ) {
                return {
                    type: 'tr',
                    children: [
                        {
                            type: 'th',
                            content: title
                        },
                        {
                            type: 'td',
                            content: idrinth.ui.formatNumber ( dataset.n )
                        },
                        {
                            type: 'td',
                            content: idrinth.ui.formatNumber ( dataset.h )
                        },
                        {
                            type: 'td',
                            content: idrinth.ui.formatNumber ( dataset.l )
                        },
                        {
                            type: 'td',
                            content: idrinth.ui.formatNumber ( dataset.nm )
                        }
                    ]
                };
            };
            var wrapper = document.getElementById ( 'idrinth-tierlist' );
            clearInnerHtml ( wrapper );
            for (var count = list.length - 1; count >= 0; count--) {
                var sub = idrinth.ui.buildElement ( {
                    css: 'tier-wrapper',
                    children: [
                        {
                            type: 'img',
                            attributes: [ {
                                    name: 'src',
                                    value: 'https://dotd.idrinth.de/static/raid-image-service/' + idrinth.tier.list[list[count]].url + '/'
                                } ]
                        },
                        {
                            type: 'strong',
                            content: idrinth.tier.list[list[count]].name
                        },
                        {
                            type: 'button',
                            content: 'Tag to screen-top',
                            attributes: [
                                {
                                    name: 'onclick',
                                    value: 'idrinth.tier.addTagged(\'' + list[count].replace ( /'/, '\\\'' ) + '\');'
                                },
                                {
                                    name: 'type',
                                    value: 'action'
                                }
                            ]
                        },
                        {
                            type: 'span',
                            content: 'AP: ' + idrinth.ui.formatNumber ( idrinth.tier.list[list[count]].ap )
                        },
                        {
                            type: 'table',
                            children: [
                                {
                                    type: 'thead',
                                    children: [
                                        {
                                            type: 'tr',
                                            children: [
                                                {
                                                    type: 'th',
                                                    content: '#'
                                                },
                                                {
                                                    type: 'th',
                                                    content: 'Normal'
                                                },
                                                {
                                                    type: 'th',
                                                    content: 'Hard'
                                                },
                                                {
                                                    type: 'th',
                                                    content: 'Legend'
                                                },
                                                {
                                                    type: 'th',
                                                    content: 'Nightmare'
                                                }
                                            ]
                                        }
                                    ]
                                },
                                {
                                    type: 'tbody',
                                    children: [
                                        makeRow ( 'FS', idrinth.tier.list[list[count]].fs ),
                                        makeRow ( 'OS', idrinth.tier.list[list[count]].os ),
                                        makeRow ( '', {
                                            n: '',
                                            l: '',
                                            h: '',
                                            nm: ''
                                        } )
                                    ]
                                }
                            ]
                        }
                    ]
                } );
                var maxTiers = Math.max ( idrinth.tier.list[list[count]].n.length, idrinth.tier.list[list[count]].h.length, idrinth.tier.list[list[count]].l.length, idrinth.tier.list[list[count]].nm.length );
                for (var ic = 0; ic < maxTiers; ic++) {
                    sub.lastChild.lastChild.appendChild ( idrinth.ui.buildElement ( {
                        type: 'tr',
                        children: [
                            {
                                type: 'th',
                                content: ic + 1
                            },
                            makeField ( list[count], 'n', ic ),
                            makeField ( list[count], 'h', ic ),
                            makeField ( list[count], 'l', ic ),
                            makeField ( list[count], 'nm', ic )
                        ]
                    } ) );
                }
                wrapper.appendChild ( sub );
            }
        };
        if ( !name || name.length === 0 ) {
            clearInnerHtml ( document.getElementById ( 'idrinth-tierlist' ) );
            return;
        }
        var result = [ ];
        var regExp = new RegExp ( name, 'i' );
        for (var key in idrinth.tier.list) {
            if ( key.match ( regExp ) ) {
                result.push ( key );
            }
        }
        makeList ( result );
    }
};<commit_msg>fixing condition for loop<commit_after>idrinth.tier = {
    list: { },
    addTagged: function ( name ) {
        if ( !name || !this.list.hasOwnProperty ( name ) || typeof this.list[name] === 'function' || document.getElementById ( 'idrinth-tier-box-' + name ) ) {
            return;
        }
        var make = function ( x, name ) {
            var makeElement = function ( label, number, description ) {
                return {
                    content: label + ' ' + idrinth.ui.formatNumber ( number ),
                    attributes: [ {
                            name: 'title',
                            value: description
                        } ]
                };
            };
            var info = [
                makeElement ( 'FS', this.list[name].fs.nm, 'Fair share' ),
                makeElement ( 'AP', this.list[name].ap, 'Achievement point damage' )
            ];
            if ( this.list[name].os && this.list[name].os.nm ) {
                info.push ( makeElement ( 'OS', this.list[name].os.nm, 'Optimal share' ) );
                info.unshift ( makeElement ( 'MA', this.list[name].nm[this.list[name].nm.length - 1], 'Maximum/highest tier' ) );
                info.unshift ( makeElement ( 'MI', this.list[name].nm[0], 'Minimum/lowest tier' ) );
            }
            info.unshift (
                    {
                        type: 'strong',
                        content: this.list[name].name.replace ( /\(.*$/, '' )
                    } );
            idrinth.ui.body.appendChild ( idrinth.ui.buildElement (
                    {
                        id: 'idrinth-tier-box-' + name,
                        css: 'idrinth-hovering-box idrinth-tier-box',
                        children: [ {
                                children: info
                            } ],
                        attributes: [
                            {
                                name: 'title',
                                value: 'click to close'
                            },
                            {
                                name: 'onclick',
                                value: 'idrinth.ui.removeElement(this.id);idrinth.tier.taggedSlots[\'' + x + '\']=null;'
                            },
                            {
                                name: 'style',
                                value: 'left:' + x + 'px;background-image: url(https://dotd.idrinth.de/static/raid-image-service/' + this.list[name].url + '/);'
                            }
                        ]
                    }
            ) );
        };
        for (var key in this.taggedSlots) {
            if ( this.taggedSots.hasOwnProperty ( key ) && typeof key !== 'function' && this.taggedSlots[key] === null ) {
                return make ( key, name );
            }
        }
        idrinth.alert ( 'There is no space for another tier-box at the moment, please close one first.' );
    },
    taggedSlots: { },
    start: function () {
        'use strict';
        var pos = 1;
        while ( 0 < window.innerWidth - 140 * ( pos + 2 ) ) {
            this.taggedSlots[( pos * 140 ).toString ()] = null;
            pos++;
        }
        idrinth.runAjax (
                'https://dotd.idrinth.de/' + idrinth.platform + '/tier-service/',
                function ( text ) {
                    idrinth.tier.import ( text );
                },
                function ( ) {
                    window.setTimeout ( idrinth.tier.start, 10000 );
                },
                function ( ) {
                    window.setTimeout ( idrinth.tier.start, 10000 );
                }
        );
    },
    import: function ( data ) {
        'use strict';
        data = JSON.parse ( data );
        if ( data ) {
            idrinth.tier.list = data;
        } else {
            window.setTimeout ( idrinth.tier.start, 1000 );
        }
    },
    getTierForName: function ( name ) {
        var clearInnerHtml = function clearInnerHtmlF ( elem ) {
            elem.innerHTML = '';
        };

        var makeList = function ( list ) {
            var makeField = function ( listKey, difficulty, ic ) {
                var ln = {
                    type: 'td'
                };
                try {
                    ln.styles = idrinth.tier.list[listKey].os[difficulty] === idrinth.tier[listKey][difficulty][ic] ? 'is-os' : '';
                } catch ( E ) {
                    idrinth.log ( E.toString ( ) );
                }
                try {
                    ln.content = idrinth.ui.formatNumber ( idrinth.tier.list[listKey][difficulty][ic] ) + ' ' +
                            idrinth.tier.list[listKey].epics[difficulty][ic] + 'E';
                } catch ( E2 ) {
                    idrinth.log ( E2.toString ( ) );
                    try {
                        ln.content = idrinth.ui.formatNumber ( idrinth.tier.list[listKey][difficulty][ic] );
                    } catch ( E3 ) {
                        idrinth.log ( E3.toString ( ) );
                    }
                }
                return ln;
            };
            var makeRow = function ( title, dataset ) {
                return {
                    type: 'tr',
                    children: [
                        {
                            type: 'th',
                            content: title
                        },
                        {
                            type: 'td',
                            content: idrinth.ui.formatNumber ( dataset.n )
                        },
                        {
                            type: 'td',
                            content: idrinth.ui.formatNumber ( dataset.h )
                        },
                        {
                            type: 'td',
                            content: idrinth.ui.formatNumber ( dataset.l )
                        },
                        {
                            type: 'td',
                            content: idrinth.ui.formatNumber ( dataset.nm )
                        }
                    ]
                };
            };
            var wrapper = document.getElementById ( 'idrinth-tierlist' );
            clearInnerHtml ( wrapper );
            for (var count = list.length - 1; count >= 0; count--) {
                var sub = idrinth.ui.buildElement ( {
                    css: 'tier-wrapper',
                    children: [
                        {
                            type: 'img',
                            attributes: [ {
                                    name: 'src',
                                    value: 'https://dotd.idrinth.de/static/raid-image-service/' + idrinth.tier.list[list[count]].url + '/'
                                } ]
                        },
                        {
                            type: 'strong',
                            content: idrinth.tier.list[list[count]].name
                        },
                        {
                            type: 'button',
                            content: 'Tag to screen-top',
                            attributes: [
                                {
                                    name: 'onclick',
                                    value: 'idrinth.tier.addTagged(\'' + list[count].replace ( /'/, '\\\'' ) + '\');'
                                },
                                {
                                    name: 'type',
                                    value: 'action'
                                }
                            ]
                        },
                        {
                            type: 'span',
                            content: 'AP: ' + idrinth.ui.formatNumber ( idrinth.tier.list[list[count]].ap )
                        },
                        {
                            type: 'table',
                            children: [
                                {
                                    type: 'thead',
                                    children: [
                                        {
                                            type: 'tr',
                                            children: [
                                                {
                                                    type: 'th',
                                                    content: '#'
                                                },
                                                {
                                                    type: 'th',
                                                    content: 'Normal'
                                                },
                                                {
                                                    type: 'th',
                                                    content: 'Hard'
                                                },
                                                {
                                                    type: 'th',
                                                    content: 'Legend'
                                                },
                                                {
                                                    type: 'th',
                                                    content: 'Nightmare'
                                                }
                                            ]
                                        }
                                    ]
                                },
                                {
                                    type: 'tbody',
                                    children: [
                                        makeRow ( 'FS', idrinth.tier.list[list[count]].fs ),
                                        makeRow ( 'OS', idrinth.tier.list[list[count]].os ),
                                        makeRow ( '', {
                                            n: '',
                                            l: '',
                                            h: '',
                                            nm: ''
                                        } )
                                    ]
                                }
                            ]
                        }
                    ]
                } );
                var maxTiers = Math.max ( idrinth.tier.list[list[count]].n.length, idrinth.tier.list[list[count]].h.length, idrinth.tier.list[list[count]].l.length, idrinth.tier.list[list[count]].nm.length );
                for (var ic = 0; ic < maxTiers; ic++) {
                    sub.lastChild.lastChild.appendChild ( idrinth.ui.buildElement ( {
                        type: 'tr',
                        children: [
                            {
                                type: 'th',
                                content: ic + 1
                            },
                            makeField ( list[count], 'n', ic ),
                            makeField ( list[count], 'h', ic ),
                            makeField ( list[count], 'l', ic ),
                            makeField ( list[count], 'nm', ic )
                        ]
                    } ) );
                }
                wrapper.appendChild ( sub );
            }
        };
        if ( !name || name.length === 0 ) {
            clearInnerHtml ( document.getElementById ( 'idrinth-tierlist' ) );
            return;
        }
        var result = [ ];
        var regExp = new RegExp ( name, 'i' );
        for (var key in idrinth.tier.list) {
            if ( key.match ( regExp ) ) {
                result.push ( key );
            }
        }
        makeList ( result );
    }
};<|endoftext|>


<commit_before>define(['jquery',
    'underscore',
    'views/components/search/base-filter',
    'bootstrap',
    'arches',
    'select2',
    'knockout',
    'knockout-mapping',
    'view-data',
    'bootstrap-datetimepicker',
    'plugins/knockout-select2'],
function($, _, BaseFilter, bootstrap, arches, select2, ko, koMapping, viewdata) {
    var componentName = 'search-results';
    return ko.components.register(componentName, {
        viewModel: BaseFilter.extend({

            events: {
                'click .related-resources-graph': 'showRelatedResouresGraph',
                'click .navigate-map': 'zoomToFeature',
                'mouseover .arches-search-item': 'itemMouseover',
                'mouseout .arches-search-item': 'itemMouseout'
            },

            initialize: function(options) {
                options.name = 'Search Results';
                BaseFilter.prototype.initialize.call(this, options);

                this.results = ko.observableArray();
                this.showRelationships = ko.observable();
                this.relationshipCandidates = ko.observableArray();
                this.selectedResourceId = ko.observable(null);

                this.showRelationships.subscribe(function(res) {
                    this.selectedResourceId(res.resourceinstanceid);
                }, this);

                this.searchResults.timestamp.subscribe(function(timestamp) {
                    this.updateResults();
                }, this);

                this.filters[componentName](this);
                this.restoreState();

                this.mapFilter = this.getFilter('map-filter');
                this.details = this.getFilter('search-result-details');
                this.relatedResourcesManager = this.getFilter('related-resources-filter');
            },

            mouseoverInstance: function() {
                var self = this;
                return function(resourceinstance){
                    var resourceinstanceid = resourceinstance.resourceinstanceid || '';
                    self.mouseoverInstanceId(resourceinstanceid);
                };
            },

            showRelatedResources: function() {
                var self = this;
                return function(resourceinstance){
                    if (resourceinstance === undefined) {
                        resourceinstance = self.relatedResourcesManager.currentResource();
                        if (self.relatedResourcesManager.showGraph() === true) {
                            self.relatedResourcesManager.showGraph(false);
                        }
                    }
                    self.showRelationships(resourceinstance);
                    if (self.selectedTab() !== 'related-resources-filter') {
                        self.selectedTab('related-resources-filter');
                    }
                };
            },

            showResourceDetails: function(graphId, result) {
                var self = this;
                return function(resourceinstance){
                    self.details.setupReport(resourceinstance.resourceinstanceid, graphId, result._source);
                    if (self.selectedTab() !== 'search-result-details') {
                        self.selectedTab('search-result-details');
                    }
                };
            },

            updateResults: function(){
                var self = this;
                var data = $('div[name="search-result-data"]').data();

                if (!!this.searchResults.results){
                    this.results.removeAll();
                    this.selectedResourceId(null);
                    this.searchResults.results.hits.hits.forEach(function(result){
                        var graphdata = _.find(viewdata.graphs, function(graphdata){
                            return result._source.graph_id === graphdata.graphid;
                        });
                        var point = null;
                        if (result._source.points.length > 0) {
                            point = result._source.points[0].point;
                        }
                        this.results.push({
                            displayname: result._source.displayname,
                            resourceinstanceid: result._source.resourceinstanceid,
                            displaydescription: result._source.displaydescription,
                            "map_popup": result._source.map_popup,
                            "provisional_resource": result._source.provisional_resource,
                            geometries: ko.observableArray(result._source.geometries),
                            iconclass: graphdata ? graphdata.iconclass : '',
                            showrelated: this.showRelatedResources(result._source.resourceinstanceid),
                            showDetails: this.showResourceDetails(result._source.graph_id, result),
                            mouseoverInstance: this.mouseoverInstance(result._source.resourceinstanceid),
                            relationshipcandidacy: this.toggleRelationshipCandidacy(result._source.resourceinstanceid),
                            ontologyclass: result._source.root_ontology_class,
                            relatable: this.isResourceRelatable(result._source.graph_id),
                            point: point,
                            mapLinkClicked: function() {
                                self.selectedResourceId(result._source.resourceinstanceid);
                                if (self.selectedTab() !== 'map-filter') {
                                    self.selectedTab('map-filter');
                                }
                                self.mapLinkData({'properties':result._source});
                            },
                            selected: ko.computed(function() {
                                return result._source.resourceinstanceid === ko.unwrap(self.selectedResourceId);
                            }),
                            canRead: result._source.permissions.users_without_read_perm.indexOf(this.userid) < 0,
                            canEdit: result._source.permissions.users_without_edit_perm.indexOf(this.userid) < 0,
                            // can_delete: result._source.permissions.users_without_delete_perm.indexOf(this.userid) < 0,
                        });
                    }, this);
                }

                return data;
            },

            restoreState: function(){
                this.updateResults();
            },

            viewReport: function(resourceinstance){
                window.open(arches.urls.resource_report + resourceinstance.resourceinstanceid);
            },

            editResource: function(resourceinstance){
                window.open(arches.urls.resource_editor + resourceinstance.resourceinstanceid);
            },

            zoomToFeature: function(evt){
                var data = $(evt.currentTarget).data();
                this.trigger('find_on_map', data.resourceid, data);
            }
        }),
        template: { require: 'text!templates/views/components/search/search-results.htm' }
    });
});
<commit_msg>Resizes map when map tab is selectd<commit_after>define(['jquery',
    'underscore',
    'views/components/search/base-filter',
    'bootstrap',
    'arches',
    'select2',
    'knockout',
    'knockout-mapping',
    'view-data',
    'bootstrap-datetimepicker',
    'plugins/knockout-select2'],
function($, _, BaseFilter, bootstrap, arches, select2, ko, koMapping, viewdata) {
    var componentName = 'search-results';
    return ko.components.register(componentName, {
        viewModel: BaseFilter.extend({

            events: {
                'click .related-resources-graph': 'showRelatedResouresGraph',
                'click .navigate-map': 'zoomToFeature',
                'mouseover .arches-search-item': 'itemMouseover',
                'mouseout .arches-search-item': 'itemMouseout'
            },

            initialize: function(options) {
                options.name = 'Search Results';
                BaseFilter.prototype.initialize.call(this, options);

                this.results = ko.observableArray();
                this.showRelationships = ko.observable();
                this.relationshipCandidates = ko.observableArray();
                this.selectedResourceId = ko.observable(null);

                this.showRelationships.subscribe(function(res) {
                    this.selectedResourceId(res.resourceinstanceid);
                }, this);

                this.searchResults.timestamp.subscribe(function(timestamp) {
                    this.updateResults();
                }, this);

                this.filters[componentName](this);
                this.restoreState();

                this.mapFilter = this.getFilter('map-filter');
                this.details = this.getFilter('search-result-details');
                this.relatedResourcesManager = this.getFilter('related-resources-filter');
                this.selectedTab.subscribe(function(tab){
                    var self = this;
                    if (tab === 'map-filter') {
                        if (ko.unwrap(this.mapFilter.map)) {
                            setTimeout(function() { self.mapFilter.map().resize(); }, 1);
                        }
                    }
                }, this);
            },

            mouseoverInstance: function() {
                var self = this;
                return function(resourceinstance){
                    var resourceinstanceid = resourceinstance.resourceinstanceid || '';
                    self.mouseoverInstanceId(resourceinstanceid);
                };
            },

            showRelatedResources: function() {
                var self = this;
                return function(resourceinstance){
                    if (resourceinstance === undefined) {
                        resourceinstance = self.relatedResourcesManager.currentResource();
                        if (self.relatedResourcesManager.showGraph() === true) {
                            self.relatedResourcesManager.showGraph(false);
                        }
                    }
                    self.showRelationships(resourceinstance);
                    if (self.selectedTab() !== 'related-resources-filter') {
                        self.selectedTab('related-resources-filter');
                    }
                };
            },

            showResourceDetails: function(graphId, result) {
                var self = this;
                return function(resourceinstance){
                    self.details.setupReport(resourceinstance.resourceinstanceid, graphId, result._source);
                    if (self.selectedTab() !== 'search-result-details') {
                        self.selectedTab('search-result-details');
                    }
                };
            },

            updateResults: function(){
                var self = this;
                var data = $('div[name="search-result-data"]').data();

                if (!!this.searchResults.results){
                    this.results.removeAll();
                    this.selectedResourceId(null);
                    this.searchResults.results.hits.hits.forEach(function(result){
                        var graphdata = _.find(viewdata.graphs, function(graphdata){
                            return result._source.graph_id === graphdata.graphid;
                        });
                        var point = null;
                        if (result._source.points.length > 0) {
                            point = result._source.points[0].point;
                        }
                        this.results.push({
                            displayname: result._source.displayname,
                            resourceinstanceid: result._source.resourceinstanceid,
                            displaydescription: result._source.displaydescription,
                            "map_popup": result._source.map_popup,
                            "provisional_resource": result._source.provisional_resource,
                            geometries: ko.observableArray(result._source.geometries),
                            iconclass: graphdata ? graphdata.iconclass : '',
                            showrelated: this.showRelatedResources(result._source.resourceinstanceid),
                            showDetails: this.showResourceDetails(result._source.graph_id, result),
                            mouseoverInstance: this.mouseoverInstance(result._source.resourceinstanceid),
                            relationshipcandidacy: this.toggleRelationshipCandidacy(result._source.resourceinstanceid),
                            ontologyclass: result._source.root_ontology_class,
                            relatable: this.isResourceRelatable(result._source.graph_id),
                            point: point,
                            mapLinkClicked: function() {
                                self.selectedResourceId(result._source.resourceinstanceid);
                                if (self.selectedTab() !== 'map-filter') {
                                    self.selectedTab('map-filter');
                                }
                                self.mapLinkData({'properties':result._source});
                            },
                            selected: ko.computed(function() {
                                return result._source.resourceinstanceid === ko.unwrap(self.selectedResourceId);
                            }),
                            canRead: result._source.permissions.users_without_read_perm.indexOf(this.userid) < 0,
                            canEdit: result._source.permissions.users_without_edit_perm.indexOf(this.userid) < 0,
                            // can_delete: result._source.permissions.users_without_delete_perm.indexOf(this.userid) < 0,
                        });
                    }, this);
                }

                return data;
            },

            restoreState: function(){
                this.updateResults();
            },

            viewReport: function(resourceinstance){
                window.open(arches.urls.resource_report + resourceinstance.resourceinstanceid);
            },

            editResource: function(resourceinstance){
                window.open(arches.urls.resource_editor + resourceinstance.resourceinstanceid);
            },

            zoomToFeature: function(evt){
                var data = $(evt.currentTarget).data();
                this.trigger('find_on_map', data.resourceid, data);
            }
        }),
        template: { require: 'text!templates/views/components/search/search-results.htm' }
    });
});
<|endoftext|>


<commit_before>(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');

ga('create', 'UA-53856291-1', 'auto');
ga('require', 'displayfeatures');
ga('send', 'pageview');

$(document).ready(function () {
  $('.ad a').click(function (e) {
    var id = $(this).parent().data('id')
    ga('send', 'event', 'Hiring Ads', 'click', id)
  })

  $('.npme-group a, .npme-details a').click(function (e) {
    ga('send', 'event', 'npm Enterprise', 'click')
  })

})
<commit_msg>moves google analytics id to match the old one (fixes #281)<commit_after>(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');

ga('create', 'UA-47041310-1', 'auto');
ga('require', 'displayfeatures');
ga('send', 'pageview');

$(document).ready(function () {
  $('.ad a').click(function (e) {
    var id = $(this).parent().data('id')
    ga('send', 'event', 'Hiring Ads', 'click', id)
  })

  $('.npme-group a, .npme-details a').click(function (e) {
    ga('send', 'event', 'npm Enterprise', 'click')
  })

})
<|endoftext|>


<commit_before>'use strict';

// TODO(mkhatib): Seperate these into config/routes.js and
// config/interceptors/httpInterceptors.js and add tests for them.
// TODO(mkhatib): Move the autogenerated appConfig.js to config/constants.js.

angular.module('webClientApp', [
  'ngAnimate',
  'ngCookies',
  'ngLocale',
  'ngResource',
  'ngSanitize',
  'ngRoute',
  'AppConfig',
  'truncate',
  'angulartics',
  'angulartics.google.analytics',
  'angularFileUpload',
  'angular-loading-bar',
  'ipCookie',
  'ng-token-auth'
])
  /**
   * Routing.
   */
  .config(['$routeProvider',
      function ($routeProvider) {

    /**
     * Checks proper access to the route and reject it if unauthenticated.
     */
    var checkAccess = {
      load: ['$q', '$location', '$rootScope', '$auth', '$route', 'LoginService',
          function($q, $location, $rootScope, $auth, $route, LoginService) {
        var isPublic = $route.current.isPublic;
        var isAdmin = $route.current.isAdmin;
        var deferred = $q.defer();
        var callback = function() {
          if(LoginService.isAuthorized(isPublic, isAdmin)) {
            deferred.resolve();
          } else {
            deferred.reject();
            $rootScope.$broadcast('showLoginDialog', {
              'prev': $location.path()
            });
          }
        };
        $auth.validateUser().then(callback, callback);
        return deferred.promise;
      }]
    };

    $routeProvider

      .when('/', {
        templateUrl: 'views/main.html',
        controller: 'MainCtrl',
        title: 'منصة النشر العربية',
        isPublic: true,
        resolve: checkAccess
      })

      .when('/login', {
        templateUrl: 'views/login.html',
        controller: 'LoginCtrl',
        title: 'تسجيل الدخول',
        isPublic: true,
        resolve: checkAccess
      })

      .when('/signup', {
        templateUrl: 'views/signup.html',
        controller: 'SignupCtrl',
        title: 'مستخدم جديد',
        isPublic: true,
        resolve: checkAccess
      })

      .when('/articles/new', {
        templateUrl: 'views/articles/edit.html',
        controller: 'NewArticleCtrl',
        title: 'مقال جديد',
        isPublic: false,
        resolve: checkAccess
      })

      .when('/articles/:articleId/edit', {
        templateUrl: 'views/articles/edit.html',
        controller: 'EditArticleCtrl',
        isPublic: false,
        resolve: checkAccess
      })

      .when('/articles/:articleId', {
        templateUrl: 'views/articles/show.html',
        controller: 'ArticleCtrl',
        isPublic: true,
        resolve: checkAccess
      })

      .when('/accounts/reset_password', {
        templateUrl: 'views/accounts/reset_password.html',
        controller: 'ResetPasswordController',
        isPublic: true,
        resolve: checkAccess
      })

      .when('/accounts/update_password', {
        templateUrl: 'views/accounts/update_password.html',
        controller: 'UpdatePasswordController',
        isPublic: true,
        resolve: checkAccess
      })

      .when('/profiles/:userId', {
        templateUrl: 'views/profiles/show.html',
        controller: 'ProfileCtrl',
        isPublic: true,
        resolve: checkAccess
      })

      .when('/profiles/:userId/edit', {
        templateUrl: 'views/profiles/edit.html',
        controller: 'EditProfileCtrl',
        isPublic: false,
        resolve: checkAccess
      })

      .when('/categories/:categoryId', {
        templateUrl: 'views/categories/show.html',
        controller: 'CategoryCtrl',
        isPublic: true,
        resolve: checkAccess
      })

      .when('/categories/:categoryId/topics/:topicId', {
        templateUrl: 'views/topics/show.html',
        controller: 'TopicCtrl',
        isPublic: true,
        resolve: checkAccess
      })

      .when('/admin', {
        templateUrl: 'views/admin/dashboard.html',
        isPublic: false,
        isAdmin: true,
        resolve: checkAccess
      })

      .when('/admin/manage/categories', {
        templateUrl: 'views/admin/manage/categories.html',
        controller: 'ManageCategoriesCtrl',
        isPublic: false,
        isAdmin: true,
        resolve: checkAccess
      })

      .otherwise({
        redirectTo: '/'
      });
  }])
  .factory('unAuthenticatedInterceptor', ['$location', '$q', '$rootScope',
      function ($location, $q, $rootScope) {
    return {
      'request': function(config) {
        return config;
      },

      'requestError': function(response) {
        console.error(response);
      },

      'response': function(response) {
        return response;
      },

      'responseError': function(response) {
        if (response.status === 401) {
          var previous = $location.path();
          $rootScope.$broadcast('showLoginDialog', {'prev': previous});
          return $q.reject(response);
        }
        else {
          return $q.reject(response);
        }
      }
    };
  }])

  /**
   * Sets up authentication for ng-token-auth.
   */
  .config(['$authProvider', 'API_HOST', function($authProvider, API_HOST) {
    $authProvider.configure({
      apiUrl: '//' + API_HOST,
      confirmationSuccessUrl:  'http://' + window.location.host + '/login',
      passwordResetSuccessUrl: ('http://' + window.location.host +
                                '/accounts/update_password'),
      authProviderPaths: {
        facebook: '/auth/facebook',
        gplus:   '/auth/gplus'
      },
    });
  }])

  /**
   * Intercept every http request and check for 401 Unauthorized
   * error. Clear the current user and redirect to /login page.
   */
  .config(['$httpProvider', '$locationProvider', function ($httpProvider, $locationProvider) {
    $httpProvider.interceptors.push('unAuthenticatedInterceptor');
    $httpProvider.defaults.headers.common['Accept-Encoding'] = 'gzip';

    $locationProvider.html5Mode(true).hashPrefix('!');
  }])
  /**
   * Allow embedding specific sites.
   */
  .config(['$sceDelegateProvider', function ($sceDelegateProvider) {
    $sceDelegateProvider.resourceUrlWhitelist([
      // Allow same origin resource loads.
      'self',
      // Allow loading from YouTube domain.
      'http://www.youtube.com/embed/**',
      'https://www.youtube.com/embed/**'
    ]);
  }])
  /**
   * Disable the spinner for angular-loading-bar.
   */
  .config(['cfpLoadingBarProvider', function(cfpLoadingBarProvider) {
    cfpLoadingBarProvider.includeSpinner = false;
  }])
  /**
   * Everytime the route change check if the user need to login.
   */
  .run(['$location', '$rootScope', '$analytics', '$auth', 'LoginService', 'User', 'GA_TRACKING_ID',
      function ($location, $rootScope, $analytics, $auth, LoginService, User, GA_TRACKING_ID) {

    // ga is the Google analytics global variable.
    if (window.ga) {
      ga('create', GA_TRACKING_ID);
    }

    $rootScope.linkPrefix = 'http://' + document.location.host;

    /**
     * Holds data about page-wide attributes. Like pages title.
     */
    $rootScope.page = {
      title: 'منصة النشر العربية',
      description: 'منصة نشر متخصصة باللغة العربية مفتوحة المصدر',
      image: 'http://' + document.location.host + '/images/manshar@200x200.png'
    };

    /**
     * Logs the user out.
     */
    $rootScope.logout = function () {
      $analytics.eventTrack('Logout', {
        category: 'User'
      });
      LoginService.logout();
    };

    /**
     * Shows the login dialog.
     * @param {string} optPrev Optional previous path to go back to after login.
     */
    $rootScope.showLoginDialog = function(optPrev) {
      $rootScope.$broadcast('showLoginDialog', {
        'prev': optPrev
      });
    };

    /**
     * Returns true if the passed user is the same user that is referenced
     * in the resource. This assumes that the resource always have a user
     * property, otherwise it'll return false.
     * @param {Object} user The object representing the user data.
     * @param {Object} resource The object representing the resource (e.g. Article).
     * @returns {boolean} true if the user is the owner of the resource.
     */
    $rootScope.isOwner = function (user, resource) {
      var id = user && parseInt(user.id);
      return (!!user && !!resource && !!resource.user &&
              id === resource.user.id);
    };

    var checkAccess = function(event, next, current) {

      /**
       * First load to the AngularJS the user might have not been loaded
       * so need to call the callback after validateUser promise is resolved.
       */
      var firstLoadCallback = function() {
        if (!LoginService.isAuthorized(next.isPublic, next.isAdmin)) {
          $location.path('/login').search('prev', $location.path());
        }
      };

      // If this is the first load of the site.
      if(!current) {
        $auth.validateUser().then(firstLoadCallback, firstLoadCallback);
      }
      else if(!LoginService.isAuthorized(next.isPublic, next.isAdmin)) {
        event.preventDefault();
        // Show the dialog instead of redirecting for all navigations.
        // Except first time landing on the site on protected page.
        if (current) {
          $rootScope.$broadcast('showLoginDialog', {
            'prev': $location.path()
          });
        }
      }
    };

    /**
     * If the route to be accessed is private make sure the user is authenticated
     * otherwise, broadcast 'showLoginDialog' to show login modal.
     */
    $rootScope.$on('$routeChangeStart', function(event, next, current) {
      checkAccess(event, next, current);
    });

    $rootScope.$on('$routeChangeSuccess', function (event, current) {
      $rootScope.page.title = current.$$route.title || $rootScope.page.title;
      $rootScope.page.url = document.location.href;
    });

    var getLoggedInUserProfile = function(event, data) {
      User.get({'userId': data.id}, function(user) {
        angular.extend($rootScope.user, user);
      });
    };

    $rootScope.$on('auth:validation-success', getLoggedInUserProfile);
    $rootScope.$on('auth:login-success', getLoggedInUserProfile);

  }]);
<commit_msg>Fix OAuth Facebook/Google login problems #207<commit_after>'use strict';

// TODO(mkhatib): Seperate these into config/routes.js and
// config/interceptors/httpInterceptors.js and add tests for them.
// TODO(mkhatib): Move the autogenerated appConfig.js to config/constants.js.

angular.module('webClientApp', [
  'ngAnimate',
  'ngCookies',
  'ngLocale',
  'ngResource',
  'ngSanitize',
  'ngRoute',
  'AppConfig',
  'truncate',
  'angulartics',
  'angulartics.google.analytics',
  'angularFileUpload',
  'angular-loading-bar',
  'ipCookie',
  'ng-token-auth'
])
  /**
   * Routing.
   */
  .config(['$routeProvider',
      function ($routeProvider) {

    /**
     * Checks proper access to the route and reject it if unauthenticated.
     */
    var checkAccess = {
      load: ['$q', '$location', '$rootScope', '$auth', '$route', 'LoginService',
          function($q, $location, $rootScope, $auth, $route, LoginService) {
        var isPublic = $route.current.isPublic;
        var isAdmin = $route.current.isAdmin;
        var deferred = $q.defer();
        var callback = function() {
          if(LoginService.isAuthorized(isPublic, isAdmin)) {
            deferred.resolve();
          } else {
            deferred.reject();
            $rootScope.$broadcast('showLoginDialog', {
              'prev': $location.path()
            });
          }
        };
        $auth.validateUser().then(callback, callback);
        return deferred.promise;
      }]
    };

    $routeProvider

      .when('/', {
        templateUrl: 'views/main.html',
        controller: 'MainCtrl',
        title: 'منصة النشر العربية',
        isPublic: true,
        resolve: checkAccess
      })

      .when('/login', {
        templateUrl: 'views/login.html',
        controller: 'LoginCtrl',
        title: 'تسجيل الدخول',
        isPublic: true,
        resolve: checkAccess
      })

      .when('/signup', {
        templateUrl: 'views/signup.html',
        controller: 'SignupCtrl',
        title: 'مستخدم جديد',
        isPublic: true,
        resolve: checkAccess
      })

      .when('/articles/new', {
        templateUrl: 'views/articles/edit.html',
        controller: 'NewArticleCtrl',
        title: 'مقال جديد',
        isPublic: false,
        resolve: checkAccess
      })

      .when('/articles/:articleId/edit', {
        templateUrl: 'views/articles/edit.html',
        controller: 'EditArticleCtrl',
        isPublic: false,
        resolve: checkAccess
      })

      .when('/articles/:articleId', {
        templateUrl: 'views/articles/show.html',
        controller: 'ArticleCtrl',
        isPublic: true,
        resolve: checkAccess
      })

      .when('/accounts/reset_password', {
        templateUrl: 'views/accounts/reset_password.html',
        controller: 'ResetPasswordController',
        isPublic: true,
        resolve: checkAccess
      })

      .when('/accounts/update_password', {
        templateUrl: 'views/accounts/update_password.html',
        controller: 'UpdatePasswordController',
        isPublic: true,
        resolve: checkAccess
      })

      .when('/profiles/:userId', {
        templateUrl: 'views/profiles/show.html',
        controller: 'ProfileCtrl',
        isPublic: true,
        resolve: checkAccess
      })

      .when('/profiles/:userId/edit', {
        templateUrl: 'views/profiles/edit.html',
        controller: 'EditProfileCtrl',
        isPublic: false,
        resolve: checkAccess
      })

      .when('/categories/:categoryId', {
        templateUrl: 'views/categories/show.html',
        controller: 'CategoryCtrl',
        isPublic: true,
        resolve: checkAccess
      })

      .when('/categories/:categoryId/topics/:topicId', {
        templateUrl: 'views/topics/show.html',
        controller: 'TopicCtrl',
        isPublic: true,
        resolve: checkAccess
      })

      .when('/admin', {
        templateUrl: 'views/admin/dashboard.html',
        isPublic: false,
        isAdmin: true,
        resolve: checkAccess
      })

      .when('/admin/manage/categories', {
        templateUrl: 'views/admin/manage/categories.html',
        controller: 'ManageCategoriesCtrl',
        isPublic: false,
        isAdmin: true,
        resolve: checkAccess
      })

      .otherwise({
        redirectTo: '/'
      });
  }])
  .factory('unAuthenticatedInterceptor', ['$location', '$q', '$rootScope',
      function ($location, $q, $rootScope) {
    return {
      'request': function(config) {
        return config;
      },

      'requestError': function(response) {
        console.error(response);
      },

      'response': function(response) {
        return response;
      },

      'responseError': function(response) {
        if (response.status === 401) {
          var previous = $location.path();
          $rootScope.$broadcast('showLoginDialog', {'prev': previous});
          return $q.reject(response);
        }
        else {
          return $q.reject(response);
        }
      }
    };
  }])

  /**
   * Sets up authentication for ng-token-auth.
   */
  .config(['$authProvider', 'API_HOST', function($authProvider, API_HOST) {
    $authProvider.configure({
      apiUrl: '//' + API_HOST,
      omniauthWindowType: 'newWindow',
      confirmationSuccessUrl:  'http://' + window.location.host + '/login',
      passwordResetSuccessUrl: ('http://' + window.location.host +
                                '/accounts/update_password'),
      authProviderPaths: {
        facebook: '/auth/facebook',
        gplus:   '/auth/gplus'
      },
    });
  }])

  /**
   * Intercept every http request and check for 401 Unauthorized
   * error. Clear the current user and redirect to /login page.
   */
  .config(['$httpProvider', '$locationProvider', function ($httpProvider, $locationProvider) {
    $httpProvider.interceptors.push('unAuthenticatedInterceptor');
    $httpProvider.defaults.headers.common['Accept-Encoding'] = 'gzip';

    $locationProvider.html5Mode(true).hashPrefix('!');
  }])
  /**
   * Allow embedding specific sites.
   */
  .config(['$sceDelegateProvider', function ($sceDelegateProvider) {
    $sceDelegateProvider.resourceUrlWhitelist([
      // Allow same origin resource loads.
      'self',
      // Allow loading from YouTube domain.
      'http://www.youtube.com/embed/**',
      'https://www.youtube.com/embed/**'
    ]);
  }])
  /**
   * Disable the spinner for angular-loading-bar.
   */
  .config(['cfpLoadingBarProvider', function(cfpLoadingBarProvider) {
    cfpLoadingBarProvider.includeSpinner = false;
  }])
  /**
   * Everytime the route change check if the user need to login.
   */
  .run(['$location', '$rootScope', '$analytics', '$auth', 'LoginService', 'User', 'GA_TRACKING_ID',
      function ($location, $rootScope, $analytics, $auth, LoginService, User, GA_TRACKING_ID) {

    // ga is the Google analytics global variable.
    if (window.ga) {
      ga('create', GA_TRACKING_ID);
    }

    $rootScope.linkPrefix = 'http://' + document.location.host;

    /**
     * Holds data about page-wide attributes. Like pages title.
     */
    $rootScope.page = {
      title: 'منصة النشر العربية',
      description: 'منصة نشر متخصصة باللغة العربية مفتوحة المصدر',
      image: 'http://' + document.location.host + '/images/manshar@200x200.png'
    };

    /**
     * Logs the user out.
     */
    $rootScope.logout = function () {
      $analytics.eventTrack('Logout', {
        category: 'User'
      });
      LoginService.logout();
    };

    /**
     * Shows the login dialog.
     * @param {string} optPrev Optional previous path to go back to after login.
     */
    $rootScope.showLoginDialog = function(optPrev) {
      $rootScope.$broadcast('showLoginDialog', {
        'prev': optPrev
      });
    };

    /**
     * Returns true if the passed user is the same user that is referenced
     * in the resource. This assumes that the resource always have a user
     * property, otherwise it'll return false.
     * @param {Object} user The object representing the user data.
     * @param {Object} resource The object representing the resource (e.g. Article).
     * @returns {boolean} true if the user is the owner of the resource.
     */
    $rootScope.isOwner = function (user, resource) {
      var id = user && parseInt(user.id);
      return (!!user && !!resource && !!resource.user &&
              id === resource.user.id);
    };

    var checkAccess = function(event, next, current) {

      /**
       * First load to the AngularJS the user might have not been loaded
       * so need to call the callback after validateUser promise is resolved.
       */
      var firstLoadCallback = function() {
        if (!LoginService.isAuthorized(next.isPublic, next.isAdmin)) {
          $location.path('/login').search('prev', $location.path());
        }
      };

      // If this is the first load of the site.
      if(!current) {
        $auth.validateUser().then(firstLoadCallback, firstLoadCallback);
      }
      else if(!LoginService.isAuthorized(next.isPublic, next.isAdmin)) {
        event.preventDefault();
        // Show the dialog instead of redirecting for all navigations.
        // Except first time landing on the site on protected page.
        if (current) {
          $rootScope.$broadcast('showLoginDialog', {
            'prev': $location.path()
          });
        }
      }
    };

    /**
     * If the route to be accessed is private make sure the user is authenticated
     * otherwise, broadcast 'showLoginDialog' to show login modal.
     */
    $rootScope.$on('$routeChangeStart', function(event, next, current) {
      checkAccess(event, next, current);
    });

    $rootScope.$on('$routeChangeSuccess', function (event, current) {
      $rootScope.page.title = current.$$route.title || $rootScope.page.title;
      $rootScope.page.url = document.location.href;
    });

    var getLoggedInUserProfile = function(event, data) {
      User.get({'userId': data.id}, function(user) {
        angular.extend($rootScope.user, user);
      });
    };

    $rootScope.$on('auth:validation-success', getLoggedInUserProfile);
    $rootScope.$on('auth:login-success', getLoggedInUserProfile);

  }]);
<|endoftext|>


<commit_before>import actionBuilder from 'action-builder'
import backend from 'backend'
import _ from 'lodash'
import {interval, of, Subject} from 'rxjs'
import {debounce, map, switchMap} from 'rxjs/operators'
import {select, subscribe} from 'store'

export const recipePath = (recipeId, path) => {
    const recipeTabIndex = select('process.tabs')
        .findIndex((recipe) => recipe.id === recipeId)
    if (recipeTabIndex === -1)
        throw new Error(`Recipe not found: ${recipeId}`)
    if (path && Array.isArray(path))
        path = path.join('.')
    return ['process.tabs', recipeTabIndex, path]
        .filter(e => e !== undefined)
        .join('.')
}

export const RecipeState = (recipeId) => {
    if (!recipeExists(recipeId))
        return null

    return (path) =>
        select(recipePath(recipeId, path))
}

export const saveRecipe$ = (recipe) => {
    if (!recipe.type)
        return
    const listItem = {
        id: recipe.id,
        name: recipe.title || recipe.placeholder,
        type: recipe.type
    }
    const recipes = [...select('process.recipes')]
    const index = recipes.findIndex(savedRecipe => savedRecipe.id === recipe.id)
    if (index > -1)
        recipes[index] = listItem
    else
        recipes.push(listItem)

    return of(
        actionBuilder('SET_RECIPES', {recipes})
            .set('process.recipes', recipes)
            .sideEffect(() => saveToBackend$.next(recipe))
            .build()
    )
}

export const saveRecipe = (recipe) =>
    saveRecipe$(recipe).subscribe(action => action.dispatch())

export const exportRecipe = (recipe) => {
    setTimeout(() => {
        const data = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(_.omit(recipe, ['ui']), null, 2))
        var downloadElement = document.createElement('a')
        downloadElement.setAttribute('href', data)
        downloadElement.setAttribute('download', `${recipe.title || recipe.placeholder}.json`)
        document.body.appendChild(downloadElement)
        downloadElement.click()
        downloadElement.remove()
    }, 0)
}

export const loadRecipes$ = () =>
    backend.recipe.loadAll$().pipe(
        map((recipes) => actionBuilder('SET_RECIPES', {recipes})
            .set('process.recipes', recipes)
            .build())
    )

export const deleteRecipe$ = (recipeId) =>
    backend.recipe.delete$(recipeId).pipe(
        map(() => {
            const recipes = select('process.recipes')
                .filter(recipe => recipe.id !== recipeId)
            return actionBuilder('SET_RECIPES', {recipes})
                .set('process.recipes', recipes)
                .build()
        })
    )

export const deleteRecipe = (recipeId) =>
    deleteRecipe$(recipeId).subscribe(action => action.dispatch())

export const loadRecipe$ = (recipeId) => {
    const selectedTabId = select('process.selectedTabId')
    if (recipeExists(recipeId)) {
        const recipe = select(recipePath(recipeId))
        return of([
            actionBuilder('SELECT_RECIPE')
                .set('process.selectedTabId', recipe.id)
                .build()
        ])
    } else {
        return backend.recipe.load$(recipeId).pipe(
            map(recipe =>
                actionBuilder('OPEN_RECIPE')
                    .set(recipePath(selectedTabId), recipe)
                    .set('process.selectedTabId', recipe.id)
                    .build())
        )
    }
}

export const duplicateRecipe$ = (recipeId) => {
    const selectedTabId = select('process.selectedTabId')
    return backend.recipe.load$(recipeId).pipe(
        map(recipe => ({
                ...recipe,
                id: selectedTabId,
                title: (recipe.title || recipe.placeholder) + '_copy'
            })
        ),
        switchMap(duplicate =>
            saveRecipe$(duplicate).pipe(
                map(action => [
                    action,
                    actionBuilder('SELECT_RECIPE', {duplicate})
                        .set(recipePath(selectedTabId), duplicate)
                        .build()
                ])
            )
        )
    )
}

const recipeExists = (recipeId) =>
    select('process.tabs').findIndex(recipe => recipe.id === recipeId) > -1

const saveToBackend$ = new Subject()

let prevTabs = []
subscribe('process.tabs', (recipes) => {
        if (recipes && (prevTabs.length === 0 || prevTabs !== recipes)) {
            const recipesToSave = recipes
                .filter(recipe => {
                    const prevRecipe = prevTabs.find(prevRecipe => prevRecipe.id === recipe.id)
                    return prevRecipe !== recipe
                })
                .filter(recipe =>
                    (select('process.recipes') || []).find(saved =>
                        saved.id === recipe.id
                    )
                )
            if (recipesToSave.length > 0)
                recipesToSave.forEach(recipe => saveToBackend$.next(recipe))
            prevTabs = recipes
        }
    }
)

// TODO: Must debounce for unique recipe ids, otherwise save events will be lost
// Stream per recipe, remove it when it's no longer among the saved recipes?

saveToBackend$.pipe(
    debounce(() => interval(10000))
).subscribe((recipe) =>
    backend.recipe.save$(recipe).subscribe()
)<commit_msg>Added some comments on needed improvements on auto-save.<commit_after>import actionBuilder from 'action-builder'
import backend from 'backend'
import _ from 'lodash'
import {interval, of, Subject} from 'rxjs'
import {debounce, map, switchMap} from 'rxjs/operators'
import {select, subscribe} from 'store'

export const recipePath = (recipeId, path) => {
    const recipeTabIndex = select('process.tabs')
        .findIndex((recipe) => recipe.id === recipeId)
    if (recipeTabIndex === -1)
        throw new Error(`Recipe not found: ${recipeId}`)
    if (path && Array.isArray(path))
        path = path.join('.')
    return ['process.tabs', recipeTabIndex, path]
        .filter(e => e !== undefined)
        .join('.')
}

export const RecipeState = (recipeId) => {
    if (!recipeExists(recipeId))
        return null

    return (path) =>
        select(recipePath(recipeId, path))
}

export const saveRecipe$ = (recipe) => {
    if (!recipe.type)
        return
    const listItem = {
        id: recipe.id,
        name: recipe.title || recipe.placeholder,
        type: recipe.type
    }
    const recipes = [...select('process.recipes')]
    const index = recipes.findIndex(savedRecipe => savedRecipe.id === recipe.id)
    if (index > -1)
        recipes[index] = listItem
    else
        recipes.push(listItem)

    return of(
        actionBuilder('SET_RECIPES', {recipes})
            .set('process.recipes', recipes)
            .sideEffect(() => saveToBackend$.next(recipe))
            .build()
    )
}

export const saveRecipe = (recipe) =>
    saveRecipe$(recipe).subscribe(action => action.dispatch())

export const exportRecipe = (recipe) => {
    setTimeout(() => {
        const data = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(_.omit(recipe, ['ui']), null, 2))
        var downloadElement = document.createElement('a')
        downloadElement.setAttribute('href', data)
        downloadElement.setAttribute('download', `${recipe.title || recipe.placeholder}.json`)
        document.body.appendChild(downloadElement)
        downloadElement.click()
        downloadElement.remove()
    }, 0)
}

export const loadRecipes$ = () =>
    backend.recipe.loadAll$().pipe(
        map((recipes) => actionBuilder('SET_RECIPES', {recipes})
            .set('process.recipes', recipes)
            .build())
    )

export const deleteRecipe$ = (recipeId) =>
    backend.recipe.delete$(recipeId).pipe(
        map(() => {
            const recipes = select('process.recipes')
                .filter(recipe => recipe.id !== recipeId)
            return actionBuilder('SET_RECIPES', {recipes})
                .set('process.recipes', recipes)
                .build()
        })
    )

export const deleteRecipe = (recipeId) =>
    deleteRecipe$(recipeId).subscribe(action => action.dispatch())

export const loadRecipe$ = (recipeId) => {
    const selectedTabId = select('process.selectedTabId')
    if (recipeExists(recipeId)) {
        const recipe = select(recipePath(recipeId))
        return of([
            actionBuilder('SELECT_RECIPE')
                .set('process.selectedTabId', recipe.id)
                .build()
        ])
    } else {
        return backend.recipe.load$(recipeId).pipe(
            map(recipe =>
                actionBuilder('OPEN_RECIPE')
                    .set(recipePath(selectedTabId), recipe)
                    .set('process.selectedTabId', recipe.id)
                    .build())
        )
    }
}

export const duplicateRecipe$ = (recipeId) => {
    const selectedTabId = select('process.selectedTabId')
    return backend.recipe.load$(recipeId).pipe(
        map(recipe => ({
                ...recipe,
                id: selectedTabId,
                title: (recipe.title || recipe.placeholder) + '_copy'
            })
        ),
        switchMap(duplicate =>
            saveRecipe$(duplicate).pipe(
                map(action => [
                    action,
                    actionBuilder('SELECT_RECIPE', {duplicate})
                        .set(recipePath(selectedTabId), duplicate)
                        .build()
                ])
            )
        )
    )
}

const recipeExists = (recipeId) =>
    select('process.tabs').findIndex(recipe => recipe.id === recipeId) > -1

const saveToBackend$ = new Subject()

let prevTabs = []
subscribe('process.tabs', (recipes) => {
        if (recipes && (prevTabs.length === 0 || prevTabs !== recipes)) {
            const recipesToSave = recipes
                .filter(recipe => {
                    const prevRecipe = prevTabs.find(prevRecipe => prevRecipe.id === recipe.id)
                    return prevRecipe !== recipe
                })
                .filter(recipe =>
                    (select('process.recipes') || []).find(saved =>
                        saved.id === recipe.id
                    )
                )
            if (recipesToSave.length > 0)
                recipesToSave.forEach(recipe => saveToBackend$.next(recipe))
            prevTabs = recipes
        }
    }
)

// TODO: Must debounce for unique recipe ids, otherwise save events will be lost
// Stream per recipe, remove it when it's no longer among the saved recipes?

// Some operations must save immediately
//      close tab    - re-open would cause unsaved recipe to fail, saved recipe to get old state
//      close window - changes lost

// Indicate if saving in recipe list and prevent it from being opened, cloned and deleted (?)

saveToBackend$.pipe(
    debounce(() => interval(10000))
).subscribe((recipe) =>
    backend.recipe.save$(recipe).subscribe()
)<|endoftext|>


<commit_before>import { getUTCTime } from 'binary-common-utils/lib/tools';
import { translate } from '../../common/i18n';
import { roundBalance } from '../common/tools';
import { notify } from './broadcast';

export const noop = () => {};

const castBarrierToString = barrier => (barrier > 0 ? `+${barrier}` : `${barrier}`);

export const tradeOptionToProposal = tradeOption =>
    tradeOption.contractTypes.map(type => ({
        duration_unit: tradeOption.duration_unit,
        basis        : 'stake',
        currency     : tradeOption.currency,
        symbol       : tradeOption.symbol,
        duration     : tradeOption.duration,
        amount       : roundBalance({ currency: tradeOption.currency, balance: tradeOption.amount }),
        contract_type: type,
        ...(tradeOption.prediction !== undefined && {
            barrier: tradeOption.prediction,
        }),
        ...(tradeOption.barrierOffset !== undefined && {
            barrier: castBarrierToString(tradeOption.barrierOffset),
        }),
        ...(tradeOption.secondBarrierOffset !== undefined && {
            barrier2: castBarrierToString(tradeOption.secondBarrierOffset),
        }),
    }));

export const getDirection = ticks => {
    const length = ticks.length;
    const [tickOld, tickNew] = ticks.slice(-2);

    let direction = '';
    if (length >= 2) {
        direction = tickOld.quote < tickNew.quote ? 'rise' : direction;
        direction = tickOld.quote > tickNew.quote ? 'fall' : direction;
    }

    return direction;
};

export const subscribeToStream = (observer, name, respHandler, request, registerOnce, type, unregister) =>
    new Promise(resolve => {
        observer.register(
            name,
            (...args) => {
                respHandler(...args);
                resolve();
            },
            registerOnce,
            type && { type, unregister },
            true
        );
        request();
    });

export const registerStream = (observer, name, cb) => {
    if (observer.isRegistered(name)) {
        return;
    }
    observer.register(name, cb);
};

const maxRetries = 12;

const notifyRetry = (msg, error, delay) =>
    notify('warn', `${msg}: ${error.error.msg_type}, ${translate('retrying in')} ${delay}s`);

const getBackoffDelay = (error, delayIndex) => {
    const offset = 0.5; // 500ms

    const errorCode = error && error.name;

    if (errorCode === 'DisconnectError') {
        return offset * 1000;
    }

    const maxExpTries = 4;
    const exponentialIncrease = 2 ** delayIndex + offset;

    if (errorCode === 'RateLimit' || delayIndex < maxExpTries) {
        notifyRetry(translate('Rate limit reached for'), error, exponentialIncrease);
        return exponentialIncrease * 1000;
    }

    const linearIncrease = exponentialIncrease + (maxExpTries - delayIndex + 1);

    notifyRetry(translate('Request failed for'), error, linearIncrease);
    return linearIncrease * 1000;
};

export const shouldThrowError = (e, types = [], delayIndex = 0) =>
    e &&
    (!types
        .concat(['CallError', 'WrongResponse', 'GetProposalFailure', 'RateLimit', 'DisconnectError'])
        .includes(e.name) ||
        (e.name !== 'DisconnectError' && delayIndex > maxRetries));

export const recoverFromError = (f, r, types, delayIndex) =>
    new Promise((resolve, reject) => {
        const promise = f();

        if (!promise) {
            resolve();
            return;
        }

        promise.then(resolve).catch(e => {
            if (shouldThrowError(e, types, delayIndex)) {
                reject(e);
                return;
            }

            r(e.name, () => new Promise(delayPromise => setTimeout(delayPromise, getBackoffDelay(e, delayIndex))));
        });
    });

export const doUntilDone = (f, types) => {
    let delayIndex = 0;

    return new Promise((resolve, reject) => {
        const repeat = () => {
            recoverFromError(f, (errorCode, makeDelay) => makeDelay().then(repeat), types, delayIndex++)
                .then(resolve)
                .catch(reject);
        };
        repeat();
    });
};

export const createDetails = contract => {
    const profit = subtractFixed(contract.sell_price, contract.buy_price);
    const result = profit < 0 ? 'loss' : 'win';

    return [
        contract.transaction_ids.buy,
        +contract.buy_price,
        +contract.sell_price,
        profit,
        contract.contract_type,
        getUTCTime(new Date(parseInt(`${contract.entry_tick_time}000`))),
        +contract.entry_tick,
        getUTCTime(new Date(parseInt(`${contract.exit_tick_time}000`))),
        +contract.exit_tick,
        +(contract.barrier ? contract.barrier : 0),
        result,
    ];
};

export const getUUID = () => `${new Date().getTime() * Math.random()}`;
<commit_msg>Fix subtractFix<commit_after>import { getUTCTime } from 'binary-common-utils/lib/tools';
import { translate } from '../../common/i18n';
import { roundBalance } from '../common/tools';
import { notify } from './broadcast';

export const noop = () => {};

const castBarrierToString = barrier => (barrier > 0 ? `+${barrier}` : `${barrier}`);

export const tradeOptionToProposal = tradeOption =>
    tradeOption.contractTypes.map(type => ({
        duration_unit: tradeOption.duration_unit,
        basis        : 'stake',
        currency     : tradeOption.currency,
        symbol       : tradeOption.symbol,
        duration     : tradeOption.duration,
        amount       : roundBalance({ currency: tradeOption.currency, balance: tradeOption.amount }),
        contract_type: type,
        ...(tradeOption.prediction !== undefined && {
            barrier: tradeOption.prediction,
        }),
        ...(tradeOption.barrierOffset !== undefined && {
            barrier: castBarrierToString(tradeOption.barrierOffset),
        }),
        ...(tradeOption.secondBarrierOffset !== undefined && {
            barrier2: castBarrierToString(tradeOption.secondBarrierOffset),
        }),
    }));

export const getDirection = ticks => {
    const length = ticks.length;
    const [tickOld, tickNew] = ticks.slice(-2);

    let direction = '';
    if (length >= 2) {
        direction = tickOld.quote < tickNew.quote ? 'rise' : direction;
        direction = tickOld.quote > tickNew.quote ? 'fall' : direction;
    }

    return direction;
};

export const subscribeToStream = (observer, name, respHandler, request, registerOnce, type, unregister) =>
    new Promise(resolve => {
        observer.register(
            name,
            (...args) => {
                respHandler(...args);
                resolve();
            },
            registerOnce,
            type && { type, unregister },
            true
        );
        request();
    });

export const registerStream = (observer, name, cb) => {
    if (observer.isRegistered(name)) {
        return;
    }
    observer.register(name, cb);
};

const maxRetries = 12;

const notifyRetry = (msg, error, delay) =>
    notify('warn', `${msg}: ${error.error.msg_type}, ${translate('retrying in')} ${delay}s`);

const getBackoffDelay = (error, delayIndex) => {
    const offset = 0.5; // 500ms

    const errorCode = error && error.name;

    if (errorCode === 'DisconnectError') {
        return offset * 1000;
    }

    const maxExpTries = 4;
    const exponentialIncrease = 2 ** delayIndex + offset;

    if (errorCode === 'RateLimit' || delayIndex < maxExpTries) {
        notifyRetry(translate('Rate limit reached for'), error, exponentialIncrease);
        return exponentialIncrease * 1000;
    }

    const linearIncrease = exponentialIncrease + (maxExpTries - delayIndex + 1);

    notifyRetry(translate('Request failed for'), error, linearIncrease);
    return linearIncrease * 1000;
};

export const shouldThrowError = (e, types = [], delayIndex = 0) =>
    e &&
    (!types
        .concat(['CallError', 'WrongResponse', 'GetProposalFailure', 'RateLimit', 'DisconnectError'])
        .includes(e.name) ||
        (e.name !== 'DisconnectError' && delayIndex > maxRetries));

export const recoverFromError = (f, r, types, delayIndex) =>
    new Promise((resolve, reject) => {
        const promise = f();

        if (!promise) {
            resolve();
            return;
        }

        promise.then(resolve).catch(e => {
            if (shouldThrowError(e, types, delayIndex)) {
                reject(e);
                return;
            }

            r(e.name, () => new Promise(delayPromise => setTimeout(delayPromise, getBackoffDelay(e, delayIndex))));
        });
    });

export const doUntilDone = (f, types) => {
    let delayIndex = 0;

    return new Promise((resolve, reject) => {
        const repeat = () => {
            recoverFromError(f, (errorCode, makeDelay) => makeDelay().then(repeat), types, delayIndex++)
                .then(resolve)
                .catch(reject);
        };
        repeat();
    });
};

export const createDetails = contract => {
    const { sell_price: sellPrice, buy_price: buyPrice, currency } = contract;
    const profit = roundBalance({ currency, balance: sellPrice - buyPrice });
    const result = profit < 0 ? 'loss' : 'win';

    return [
        contract.transaction_ids.buy,
        +contract.buy_price,
        +contract.sell_price,
        profit,
        contract.contract_type,
        getUTCTime(new Date(parseInt(`${contract.entry_tick_time}000`))),
        +contract.entry_tick,
        getUTCTime(new Date(parseInt(`${contract.exit_tick_time}000`))),
        +contract.exit_tick,
        +(contract.barrier ? contract.barrier : 0),
        result,
    ];
};

export const getUUID = () => `${new Date().getTime() * Math.random()}`;
<|endoftext|>


<commit_before>/**
 *  PathHandler.js
 *
 *  @copyright 2017 Kevin Lindsey
 */

if (typeof module !== "undefine") {
    var Shapes = require('../index').Shapes;
}

/**
 *  PathHandler
 *
 *  @constructor
 */
function PathHandler() {
    this.shapes = [];
    this.firstX = null;
    this.firstY = null;
    this.lastX = null;
    this.lastY = null;
    this.lastCommand = null;
}

/**
 * beginParse
 */
PathHandler.prototype.beginParse = function() {
    // zero out the sub-path array
    this.shapes = [];

    // clear firstX, firstY, lastX, and lastY
    this.firstX = null;
    this.firstY = null;
    this.lastX = null;
    this.lastY = null;

    // need to remember last command type to determine how to handle the
    // relative Bezier commands
    this.lastCommand = null;
};

/**
 *  addShape
 *
 *  @param {IntersectionArgs} shape
 */
PathHandler.prototype.addShape = function(shape) {
    this.shapes.push(shape);
};

/**
 *  arcAbs - A
 *
 *  @param {Number} rx
 *  @param {Number} ry
 *  @param {Number} xAxisRotation
 *  @param {Boolean} largeArcFlag
 *  @param {Boolean} sweepFlag
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.arcAbs = function(rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) {
    // TODO: implement once we support arcs
    this.lastCommand = "A";
};

/**
 *  arcRel - a
 *
 *  @param {Number} rx
 *  @param {Number} ry
 *  @param {Number} xAxisRotation
 *  @param {Boolean} largeArcFlag
 *  @param {Boolean} sweepFlag
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.arcRel = function(rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) {
    // TODO: implement once we support arcs
    this.lastCommand = "a";
};

/**
 *  curvetoCubicAbs - C
 *
 *  @param {Number} x1
 *  @param {Number} y1
 *  @param {Number} x2
 *  @param {Number} y2
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.curvetoCubicAbs = function(x1, y1, x2, y2, x, y) {
    this.addShape(Shapes.cubicBezier(
        this.lastX, this.lastY,
        x1, y1,
        x2, y2,
        x, y
    ));

    this.lastX = x;
    this.lastY = y;
    this.lastCommand = "C";
};

/**
 *  curvetoCubicRel - c
 *
 *  @param {Number} x1
 *  @param {Number} y1
 *  @param {Number} x2
 *  @param {Number} y2
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.curvetoCubicRel = function(x1, y1, x2, y2, x, y) {
    this.addShape(Shapes.cubicBezier(
        this.lastX, this.lastY,
        this.lastX + x1, this.lastY + y1,
        this.lastX + x2, this.lastY + y2,
        this.lastX + x, this.lastY + y
    ));

    this.lastX += x;
    this.lastY += y;
    this.lastCommand = "c";
};

/**
 *  linetoHorizontalAbs - H
 *
 *  @param {Number} x
 */
PathHandler.prototype.linetoHorizontalAbs = function(x) {
    this.addShape(Shapes.line(
        this.lastX, this.lastY,
        x, this.lastY
    ));

    this.lastX = x;
    this.lastCommand = "H";
};

/**
 *  linetoHorizontalRel - h
 *
 *  @param {Number} x
 */
PathHandler.prototype.linetoHorizontalRel = function(x) {
    this.addShape(Shapes.line(
        this.lastX, this.lastY,
        this.lastX + x, this.lastY
    ));

    this.lastX += x;
    this.lastCommand = "h";
};

/**
 *  linetoAbs - L
 *
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.linetoAbs = function(x, y) {
    this.addShape(Shapes.line(
        this.lastX, this.lastY,
        x, y
    ));

    this.lastX = x;
    this.lastY = y;
    this.lastCommand = "L";
};

/**
 *  linetoRel - l
 *
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.linetoRel = function(x, y) {
    this.addShape(Shapes.line(
        this.lastX, this.lastY,
        this.lastX + x, this.lastY + y
    ));

    this.lastX += x;
    this.lastY += y;
    this.lastCommand = "l";
};

/**
 *  movetoAbs - M
 *
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.movetoAbs = function(x, y) {
    this.firstX = x;
    this.firstY = y;
    this.lastX = x;
    this.lastY = y;
    this.lastCommand = "M";
};

/**
 *  movetoRel - m
 *
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.movetoRel = function(x, y) {
    this.firstX += x;
    this.firstY += y;
    this.lastX += x;
    this.lastY += y;
    this.lastCommand = "m";
};

/**
 *  curvetoQuadraticAbs - Q
 *
 *  @param {Number} x1
 *  @param {Number} y1
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.curvetoQuadraticAbs = function(x1, y1, x, y) {
    this.addShape(Shapes.quadraticBezier(
        this.lastX, this.lastY,
        x1, y1,
        x, y
    ));

    this.lastX = x;
    this.lastY = y;
    this.lastCommand = "Q";
};

/**
 *  curvetoQuadraticRel - q
 *
 *  @param {Number} x1
 *  @param {Number} y1
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.curvetoQuadraticRel = function(x1, y1, x, y) {
    this.addShape(Shapes.quadraticBezier(
        this.lastX, this.lastY,
        this.lastX + x1, this.lastY + y,
        this.lastX + x, this.lastY + y
    ));

    this.lastX += x;
    this.lastY += y;
    this.lastCommand = "q";
};

/**
 *  curvetoCubicSmoothAbs - S
 *
 *  @param {Number} x2
 *  @param {Number} y2
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.curvetoCubicSmoothAbs = function(x2, y2, x, y) {
    var controlX, controlY;

    if ( this.lastCommand.match(/^[SsCc]$/) ) {
        var secondToLast = this.shapes[this.shapes - 1].args[2];

        controlX = 2 * this.lastX - secondToLast.x,
        controlY = 2 * this.lastX - secondToLast.y
    }
    else {
        controlX = this.lastX;
        controlY = this.lastY;
    }

    this.addShape(Shapes.cubicBezier(
        controlX, controlY,
        x2, y2,
        x, y
    ));

    this.lastX = x;
    this.lastY = y;
    this.lastCommand = "S";
};

/**
 *  curvetoCubicSmoothRel - s
 *
 *  @param {Number} x2
 *  @param {Number} y2
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.curvetoCubicSmoothRel = function(x2, y2, x, y) {
    var controlX, controlY;

    if ( this.lastCommand.match(/^[SsCc]$/) ) {
        var secondToLast = this.shapes[this.shapes - 1].args[2];

        controlX = 2 * this.lastX - secondToLast.x,
        controlY = 2 * this.lastY - secondToLast.y
    }
    else {
        controlX = this.lastX;
        controlY = this.lastY;
    }

    this.addShape(Shapes.cubicBezier(
        controlX, controlY,
        this.lastX + x2, this.lastY + y2,
        this.lastX + x, this.lastY + y
    ));

    this.lastX += x;
    this.lastY += y;
    this.lastCommand = "s";
};

/**
 *  curvetoQuadraticSmoothAbs - T
 *
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.curvetoQuadraticSmoothAbs = function(x, y) {
    var controlX, controlY;

    if ( this.lastCommand.match(/^[QqTt]$/) ) {
        var secondToLast = this.shapes[this.shapes - 1].args[1];

        controlX = 2 * this.lastX - secondToLast.x,
        controlY = 2 * this.lastY - secondToLast.y
    }
    else {
        controlX = this.lastX;
        controlY = this.lastY;
    }

    this.addShape(Shapes.quadraticBezier(
        controlX, controlY,
        x, y
    ));

    this.lastX = x;
    this.lastY = y;
    this.lastCommand = "T";
};

/**
 *  curvetoQuadraticSmoothRel - t
 *
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.curvetoQuadraticSmoothRel = function(x, y) {
    var controlX, controlY;

    if ( this.lastCommand.match(/^[QqTt]$/) ) {
        var secondToLast = this.shapes[this.shapes - 1].args[1];

        controlX = 2 * this.lastX - secondToLast.x,
        controlY = 2 * this.lastY - secondToLast.y
    }
    else {
        controlX = this.lastX;
        controlY = this.lastY;
    }

    this.addShape(Shapes.quadraticBezier(
        controlX, controlY,
        this.lastX + x, this,lastY + y
    ));

    this.lastX += x;
    this.lastY += y;
    this.lastCommand = "t";
};

/**
 *  linetoVerticalAbs - V
 *
 *  @param {Number} y
 */
PathHandler.prototype.linetoVerticalAbs = function(y) {
    this.addShape(Shapes.line(
        this.lastX, this.lastY,
        this.lastX, y
    ));

    this.lastY = y;

    this.lastCommand = "V";
};

/**
 *  linetoVerticalRel - v
 *
 *  @param {Number} y
 */
PathHandler.prototype.linetoVerticalRel = function(y) {
    this.addShape(Shapes.line(
        this.lastX, this.lastY,
        this.lastX, this.lastY + y
    ));

    this.lastY += y;

    this.lastCommand = "v";
};

/**
 *  closePath - z or Z
 */
PathHandler.prototype.closePath = function() {
    this.addShape(Shapes.line(
        this.lastX, this.lastY,
        this.firstX, this.firstY
    ));

    this.lastX = this.firstX,
    this.lastY = this.firstY;
    this.lastCommand = "z";
};

if (typeof module !== "undefine") {
    module.exports = PathHandler;
}
<commit_msg>Fix relative quadratic curveTo<commit_after>/**
 *  PathHandler.js
 *
 *  @copyright 2017 Kevin Lindsey
 */

if (typeof module !== "undefine") {
    var Shapes = require('../index').Shapes;
}

/**
 *  PathHandler
 *
 *  @constructor
 */
function PathHandler() {
    this.shapes = [];
    this.firstX = null;
    this.firstY = null;
    this.lastX = null;
    this.lastY = null;
    this.lastCommand = null;
}

/**
 * beginParse
 */
PathHandler.prototype.beginParse = function() {
    // zero out the sub-path array
    this.shapes = [];

    // clear firstX, firstY, lastX, and lastY
    this.firstX = null;
    this.firstY = null;
    this.lastX = null;
    this.lastY = null;

    // need to remember last command type to determine how to handle the
    // relative Bezier commands
    this.lastCommand = null;
};

/**
 *  addShape
 *
 *  @param {IntersectionArgs} shape
 */
PathHandler.prototype.addShape = function(shape) {
    this.shapes.push(shape);
};

/**
 *  arcAbs - A
 *
 *  @param {Number} rx
 *  @param {Number} ry
 *  @param {Number} xAxisRotation
 *  @param {Boolean} largeArcFlag
 *  @param {Boolean} sweepFlag
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.arcAbs = function(rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) {
    // TODO: implement once we support arcs
    this.lastCommand = "A";
};

/**
 *  arcRel - a
 *
 *  @param {Number} rx
 *  @param {Number} ry
 *  @param {Number} xAxisRotation
 *  @param {Boolean} largeArcFlag
 *  @param {Boolean} sweepFlag
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.arcRel = function(rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) {
    // TODO: implement once we support arcs
    this.lastCommand = "a";
};

/**
 *  curvetoCubicAbs - C
 *
 *  @param {Number} x1
 *  @param {Number} y1
 *  @param {Number} x2
 *  @param {Number} y2
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.curvetoCubicAbs = function(x1, y1, x2, y2, x, y) {
    this.addShape(Shapes.cubicBezier(
        this.lastX, this.lastY,
        x1, y1,
        x2, y2,
        x, y
    ));

    this.lastX = x;
    this.lastY = y;
    this.lastCommand = "C";
};

/**
 *  curvetoCubicRel - c
 *
 *  @param {Number} x1
 *  @param {Number} y1
 *  @param {Number} x2
 *  @param {Number} y2
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.curvetoCubicRel = function(x1, y1, x2, y2, x, y) {
    this.addShape(Shapes.cubicBezier(
        this.lastX, this.lastY,
        this.lastX + x1, this.lastY + y1,
        this.lastX + x2, this.lastY + y2,
        this.lastX + x, this.lastY + y
    ));

    this.lastX += x;
    this.lastY += y;
    this.lastCommand = "c";
};

/**
 *  linetoHorizontalAbs - H
 *
 *  @param {Number} x
 */
PathHandler.prototype.linetoHorizontalAbs = function(x) {
    this.addShape(Shapes.line(
        this.lastX, this.lastY,
        x, this.lastY
    ));

    this.lastX = x;
    this.lastCommand = "H";
};

/**
 *  linetoHorizontalRel - h
 *
 *  @param {Number} x
 */
PathHandler.prototype.linetoHorizontalRel = function(x) {
    this.addShape(Shapes.line(
        this.lastX, this.lastY,
        this.lastX + x, this.lastY
    ));

    this.lastX += x;
    this.lastCommand = "h";
};

/**
 *  linetoAbs - L
 *
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.linetoAbs = function(x, y) {
    this.addShape(Shapes.line(
        this.lastX, this.lastY,
        x, y
    ));

    this.lastX = x;
    this.lastY = y;
    this.lastCommand = "L";
};

/**
 *  linetoRel - l
 *
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.linetoRel = function(x, y) {
    this.addShape(Shapes.line(
        this.lastX, this.lastY,
        this.lastX + x, this.lastY + y
    ));

    this.lastX += x;
    this.lastY += y;
    this.lastCommand = "l";
};

/**
 *  movetoAbs - M
 *
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.movetoAbs = function(x, y) {
    this.firstX = x;
    this.firstY = y;
    this.lastX = x;
    this.lastY = y;
    this.lastCommand = "M";
};

/**
 *  movetoRel - m
 *
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.movetoRel = function(x, y) {
    this.firstX += x;
    this.firstY += y;
    this.lastX += x;
    this.lastY += y;
    this.lastCommand = "m";
};

/**
 *  curvetoQuadraticAbs - Q
 *
 *  @param {Number} x1
 *  @param {Number} y1
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.curvetoQuadraticAbs = function(x1, y1, x, y) {
    this.addShape(Shapes.quadraticBezier(
        this.lastX, this.lastY,
        x1, y1,
        x, y
    ));

    this.lastX = x;
    this.lastY = y;
    this.lastCommand = "Q";
};

/**
 *  curvetoQuadraticRel - q
 *
 *  @param {Number} x1
 *  @param {Number} y1
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.curvetoQuadraticRel = function(x1, y1, x, y) {
    this.addShape(Shapes.quadraticBezier(
        this.lastX, this.lastY,
        this.lastX + x1, this.lastY + y1,
        this.lastX + x, this.lastY + y
    ));

    this.lastX += x;
    this.lastY += y;
    this.lastCommand = "q";
};

/**
 *  curvetoCubicSmoothAbs - S
 *
 *  @param {Number} x2
 *  @param {Number} y2
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.curvetoCubicSmoothAbs = function(x2, y2, x, y) {
    var controlX, controlY;

    if ( this.lastCommand.match(/^[SsCc]$/) ) {
        var secondToLast = this.shapes[this.shapes - 1].args[2];

        controlX = 2 * this.lastX - secondToLast.x,
        controlY = 2 * this.lastX - secondToLast.y
    }
    else {
        controlX = this.lastX;
        controlY = this.lastY;
    }

    this.addShape(Shapes.cubicBezier(
        controlX, controlY,
        x2, y2,
        x, y
    ));

    this.lastX = x;
    this.lastY = y;
    this.lastCommand = "S";
};

/**
 *  curvetoCubicSmoothRel - s
 *
 *  @param {Number} x2
 *  @param {Number} y2
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.curvetoCubicSmoothRel = function(x2, y2, x, y) {
    var controlX, controlY;

    if ( this.lastCommand.match(/^[SsCc]$/) ) {
        var secondToLast = this.shapes[this.shapes - 1].args[2];

        controlX = 2 * this.lastX - secondToLast.x,
        controlY = 2 * this.lastY - secondToLast.y
    }
    else {
        controlX = this.lastX;
        controlY = this.lastY;
    }

    this.addShape(Shapes.cubicBezier(
        controlX, controlY,
        this.lastX + x2, this.lastY + y2,
        this.lastX + x, this.lastY + y
    ));

    this.lastX += x;
    this.lastY += y;
    this.lastCommand = "s";
};

/**
 *  curvetoQuadraticSmoothAbs - T
 *
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.curvetoQuadraticSmoothAbs = function(x, y) {
    var controlX, controlY;

    if ( this.lastCommand.match(/^[QqTt]$/) ) {
        var secondToLast = this.shapes[this.shapes - 1].args[1];

        controlX = 2 * this.lastX - secondToLast.x,
        controlY = 2 * this.lastY - secondToLast.y
    }
    else {
        controlX = this.lastX;
        controlY = this.lastY;
    }

    this.addShape(Shapes.quadraticBezier(
        controlX, controlY,
        x, y
    ));

    this.lastX = x;
    this.lastY = y;
    this.lastCommand = "T";
};

/**
 *  curvetoQuadraticSmoothRel - t
 *
 *  @param {Number} x
 *  @param {Number} y
 */
PathHandler.prototype.curvetoQuadraticSmoothRel = function(x, y) {
    var controlX, controlY;

    if ( this.lastCommand.match(/^[QqTt]$/) ) {
        var secondToLast = this.shapes[this.shapes - 1].args[1];

        controlX = 2 * this.lastX - secondToLast.x,
        controlY = 2 * this.lastY - secondToLast.y
    }
    else {
        controlX = this.lastX;
        controlY = this.lastY;
    }

    this.addShape(Shapes.quadraticBezier(
        controlX, controlY,
        this.lastX + x, this,lastY + y
    ));

    this.lastX += x;
    this.lastY += y;
    this.lastCommand = "t";
};

/**
 *  linetoVerticalAbs - V
 *
 *  @param {Number} y
 */
PathHandler.prototype.linetoVerticalAbs = function(y) {
    this.addShape(Shapes.line(
        this.lastX, this.lastY,
        this.lastX, y
    ));

    this.lastY = y;

    this.lastCommand = "V";
};

/**
 *  linetoVerticalRel - v
 *
 *  @param {Number} y
 */
PathHandler.prototype.linetoVerticalRel = function(y) {
    this.addShape(Shapes.line(
        this.lastX, this.lastY,
        this.lastX, this.lastY + y
    ));

    this.lastY += y;

    this.lastCommand = "v";
};

/**
 *  closePath - z or Z
 */
PathHandler.prototype.closePath = function() {
    this.addShape(Shapes.line(
        this.lastX, this.lastY,
        this.firstX, this.firstY
    ));

    this.lastX = this.firstX,
    this.lastY = this.firstY;
    this.lastCommand = "z";
};

if (typeof module !== "undefine") {
    module.exports = PathHandler;
}
<|endoftext|>


<commit_before>'use strict';

angular.module('kiwiWeb')
.controller('CompanyCtrl', function ($scope, lang) {
  $scope.lang = lang.data;
});
<commit_msg>Adds title to app.<commit_after>'use strict';

angular.module('kiwiWeb')
.controller('CompanyCtrl', function ($scope, lang) {
  $scope.lang = lang.data;
  $scope.changeTitle($scope.lang.title);
});
<|endoftext|>