<commit_before>const fs = require('fs');
const Promise = require('bluebird');
const writeFile = Promise.promisify(fs.writeFile);
const compile = require('idyll-compiler');
const UglifyJS = require('uglify-js');
const {
  getASTJSON,
  getComponentsJS,
  getDataJS,
  getHighlightJS,
  getHTML
} = require('./parse');
const css = require('./css');
const bundleJS = require('./bundle-js');

let output;

const build = (opts, paths, inputConfig) => {
  // always store source in opts.inputString
  if (paths.IDYLL_INPUT_FILE) {
    opts.inputString = fs.readFileSync(paths.IDYLL_INPUT_FILE, 'utf8');
  }

  return Promise.try(
    // this is all synchronous so we wrap it with Promise.try
    // to start a Promise chain and turn any synchronous exceptions into a rejection
    () => {
      const ast = compile(opts.inputString, opts.compilerOptions);
      output = {
        ast: getASTJSON(ast),
        components: getComponentsJS(ast, paths, inputConfig),
        css: css(opts),
        data: getDataJS(ast, paths.DATA_DIR),
        syntaxHighlighting: getHighlightJS(ast, paths)
      };
      output.html = getHTML(
        paths,
        output.ast,
        output.components,
        output.data,
        fs.readFileSync(paths.HTML_TEMPLATE_FILE, 'utf8')
      )
    }
  )
  .then(() => {
    return bundleJS(opts, paths, output); // create index.js bundle
  })
  .then((js) => {
    // minify bundle if necessary and store it
    if (opts.minify) {
      js = UglifyJS.minify(js, {fromString: true}).code;
    }
    output.js = js;
  })
  .then(() => {
    return Promise.all([
      writeFile(paths.JS_OUTPUT_FILE, output.js),
      writeFile(paths.CSS_OUTPUT_FILE, output.css),
      writeFile(paths.HTML_OUTPUT_FILE, output.html),
    ]);
  })
  .then(() => {
    return output; // return all results
  });
}

const updateCSS = (opts, paths) => {
  output.css = css(opts);
  return writeFile(paths.CSS_OUTPUT_FILE, output.css)
    .then(() => {
      return output; // return all results
    });
}

module.exports = {
  build,
  updateCSS
}
<commit_msg>use raw AST for html generation<commit_after>const fs = require('fs');
const Promise = require('bluebird');
const writeFile = Promise.promisify(fs.writeFile);
const compile = require('idyll-compiler');
const UglifyJS = require('uglify-js');
const {
  getASTJSON,
  getComponentsJS,
  getDataJS,
  getHighlightJS,
  getHTML
} = require('./parse');
const css = require('./css');
const bundleJS = require('./bundle-js');

let output;

const build = (opts, paths, inputConfig) => {
  // always store source in opts.inputString
  if (paths.IDYLL_INPUT_FILE) {
    opts.inputString = fs.readFileSync(paths.IDYLL_INPUT_FILE, 'utf8');
  }

  return Promise.try(
    // this is all synchronous so we wrap it with Promise.try
    // to start a Promise chain and turn any synchronous exceptions into a rejection
    () => {
      const ast = compile(opts.inputString, opts.compilerOptions);
      output = {
        ast: getASTJSON(ast),
        components: getComponentsJS(ast, paths, inputConfig),
        css: css(opts),
        data: getDataJS(ast, paths.DATA_DIR),
        syntaxHighlighting: getHighlightJS(ast, paths)
      };
      output.html = getHTML(
        paths,
        ast,
        output.components,
        output.data,
        fs.readFileSync(paths.HTML_TEMPLATE_FILE, 'utf8')
      )
    }
  )
  .then(() => {
    return bundleJS(opts, paths, output); // create index.js bundle
  })
  .then((js) => {
    // minify bundle if necessary and store it
    if (opts.minify) {
      js = UglifyJS.minify(js, {fromString: true}).code;
    }
    output.js = js;
  })
  .then(() => {
    return Promise.all([
      writeFile(paths.JS_OUTPUT_FILE, output.js),
      writeFile(paths.CSS_OUTPUT_FILE, output.css),
      writeFile(paths.HTML_OUTPUT_FILE, output.html),
    ]);
  })
  .then(() => {
    return output; // return all results
  });
}

const updateCSS = (opts, paths) => {
  output.css = css(opts);
  return writeFile(paths.CSS_OUTPUT_FILE, output.css)
    .then(() => {
      return output; // return all results
    });
}

module.exports = {
  build,
  updateCSS
}
<|endoftext|>


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

    window.apply = null;

    var timeoutTime = 300;

    var defaultLocationRadius = 2000;

    var skeletonData = {
        global: {
            isVerified: false,
            isCombined: false,
            viewMode: 'map',
            filterEntity: null,
            openEntity: {
                id: null,
                type: null
            },
            locationFilters: {
                enabled: null, // circle, address, neighborhood
                circle: {
                    center: {
                        lat: null,
                        lng: null
                    },
                    radius: null
                },
                neighborhood: {
                    center: {
                        lat: null,
                        lng: null
                    },
                    radius: defaultLocationRadius
                },
                address: {
                    text: '',
                    center: {
                        lat: null,
                        lng: null
                    },
                    radius: defaultLocationRadius
                }
            },
            map: {
                zoom: null,
                center: {
                    lat: null,
                    lng: null
                }
            },

            enabled: {
                agent: false,
                space: false,
                event: false
            }
        },
        agent: {
            keyword: '',
            areas: [],
            type: null,
            isVerified: false
        },
        space: {
            keyword: '',
            areas: [],
            types: [],
            acessibilidade: false,
            isVerified: false
        },
        event: {
            keyword: '',
            linguagens: [],
            from: moment().format('YYYY-MM-DD'),
            to: moment().add(1, 'week').format('YYYY-MM-DD'),
            classificacaoEtaria: [],
            isVerified: false
        },
        project: {
            keyword: '',
            linguagens: [],
            types: [],
            isVerified: false,
            // registration open
            ropen: false
        }
    };

    var diffFilter = function (input) {
        return _diffFilter(input, skeletonData);
    };

    var isEmpty = function (value) {
        if(typeof value === 'undefined' ||
           value === null) return true;

        if(angular.isObject(value)) {
            if(angular.equals(value, {}) ||
               angular.equals(value, []))
                return true;
        }

        return false;
    };

    var _diffFilter = function (input, skeleton) {
        // returns the difference from the input structure and skeleton
        // don't include nulls

        if(typeof input === 'undefined' || typeof skeleton === 'undefined' || input === skeleton) return;

        if(!angular.isObject(input)|| angular.isArray(skeleton)) {
            return input;
        }

        var output = {};

        angular.forEach(input, function(value, key){
            var currVal = _diffFilter(value, skeleton[key]);

            if(isEmpty(currVal)) return;
            this[key] = currVal;
        }, output);

        return output;
    };

    var deepExtend = function (skeleton, extension) {
        angular.forEach(extension, function(value, key){
            if(angular.isObject(value) && !angular.isArray(value)) {
                deepExtend(skeleton[key], value);
                delete extension[key];
            }
        });
        angular.extend(skeleton, extension);
        return skeleton;
    };

    var app = angular.module('search', ['ng-mapasculturais', 'SearchService', 'FindOneService', 'SearchMap', 'SearchSpatial', 'rison', 'infinite-scroll', 'ui.date']);

    app.controller('SearchController', ['$scope', '$rootScope', '$location', '$log', '$rison', '$window', '$timeout', 'searchService', 'FindOneService', function($scope, $rootScope, $location, $log, $rison, $window, $timeout, searchService, FindOneService){
        $scope.defaultLocationRadius = defaultLocationRadius;

        $rootScope.resetPagination = function(){
            $rootScope.pagination = {
                agent: 1,
                space: 1,
                event: 1,
                project: 1
            };
        }
        $rootScope.resetPagination();

        $scope.defaultImageURL = MapasCulturais.defaultAvatarURL;
        $scope.getName = function(valores, id){
            return valores.filter(function(e){if(e.id === id) return true;})[0].name;
        };

        $scope.getId = function(valores, name){
            return valores.filter(function(e){if(e.name === name) return true;})[0].id;
        };

        $scope.isSelected = function(array, id){
            return (array.indexOf(id) !== -1);
        };

        $scope.toggleSelection = function(array, id){
            var index = array.indexOf(id);
            if(index !== -1){
                array.splice(index, 1);
            } else {
                array.push(id);
            }
        };

        $scope.toggleVerified = function (entity) {

                $scope.data[entity].isVerified = !$scope.data[entity].isVerified;
        };

        $scope.showInfobox = function (){
            return $scope.data.global.openEntity.id>0 && $scope.data.global.viewMode==='map' && $scope.data.global.enabled[$scope.data.global.openEntity.type];
        };
        $scope.showFilters = function(entity){
            if($scope.data.global.viewMode === 'map')
                return $scope.data.global.enabled[entity];
            else
                return $scope.data.global.filterEntity === entity;
        }

        $scope.hasFilter = function() {
            var ctx = {has: false};
            angular.forEach($scope.data, function(value, key) {
                if(key === 'global') return;
                this.has = this.has || !angular.equals(_diffFilter($scope.data[key], skeletonData[key]), {});
            }, ctx);

            return ctx.has ||
                   $scope.data.global.isVerified ||
                   $scope.data.global.locationFilters.enabled !== null;
        };

        $scope.cleanAllFilters = function () {
            angular.forEach($scope.data, function(value, key) {
                if(key === 'global') return;
                $scope.data[key] = angular.copy(skeletonData[key]);
            });
            $scope.data.global.isVerified = false;
            $scope.data.global.locationFilters = angular.copy(skeletonData.global.locationFilters);
        };

        $scope.cleanLocationFilters = function() {
            $scope.data.global.locationFilters = angular.copy(skeletonData.global.locationFilters);
        };

        $scope.tabClick = function(entity){
            var g = $scope.data.global;
            g.filterEntity = entity;
            if(entity === 'project')
                g.viewMode = 'list';

            if(g.viewMode === 'map'){
                var n = 0;
                for(var e in g.enabled)
                    if(g.enabled[e])
                        n++;

                if(n===0 || n === 1 && !g.enabled[entity]){
                    for(var e in g.enabled)
                        if(e === entity)
                            g.enabled[e] = true;
                        else
                            g.enabled[e] = false;
                }else if(n > 1 && !g.enabled[entity]){
                    g.enabled[entity] = true;
                }
            }
        };

        $scope.parseHash = function(){
            var newValue = $location.hash();
            if(newValue === '') {
                $scope.tabClick('agent');
                return;
            }

            if(newValue !== $rison.stringify(diffFilter($scope.data))){
                $scope.data = deepExtend(angular.copy(skeletonData), $rison.parse(newValue));
                $timeout.cancel($scope.timer);
                $scope.timer = $timeout(function() {
                    $rootScope.$emit('searchDataChange', $scope.data);
                },timeoutTime);
            }
        };

        $scope.dataChange = function(newValue, oldValue){
            if(newValue === undefined) return;
            if(newValue.global.viewMode === 'map'){
                var filterEntity = newValue.global.filterEntity;
                if(!newValue.global.enabled[filterEntity]){
                    var enabledEntities = 0;

                    angular.forEach(newValue.global.enabled, function(v,k){ if(v) enabledEntities++; });

                    if(enabledEntities === 1){
                        var obj = {space:false, agent:false, event:false};
                        obj[filterEntity] = true;
                        newValue.global.enabled = obj;
                    }else{
                        newValue.global.enabled[filterEntity] = true;
                    }
                    return;
                }
            }
            var serialized = $rison.stringify(diffFilter(newValue));
            $window.$timout = $timeout;
            if($location.hash() !== serialized){
                $timeout.cancel($scope.timer);
                if(oldValue && !angular.equals(oldValue.global.enabled, newValue.global.enabled)) {
                    $location.hash(serialized);
                    $scope.timer = $timeout(function() {
                        $rootScope.$emit('searchDataChange', $scope.data);
                    },timeoutTime);
                } else {
                    $scope.timer = $timeout(function() {
                        $location.hash(serialized);
                        $rootScope.$emit('searchDataChange', $scope.data);
                    }, timeoutTime);
                    $window.dataTimeout = $scope.timer;
                }
            }
            $window.scrollTo(0, $window.scrollY+1);
            $window.scrollTo(0, $window.scrollY-1);
        };

        $scope.data = angular.copy(skeletonData);

        $scope.areas = MapasCulturais.taxonomyTerms.area.map(function(el, i){ return {id: i, name: el}; });
        $scope.linguagens = MapasCulturais.taxonomyTerms.linguagem.map(function(el, i){ return {id: i, name: el}; });
        $scope.classificacoes = MapasCulturais.classificacoesEtarias.map(function(el, i){ return {id: i, name: el}; });

        MapasCulturais.entityTypes.agent.unshift({id:null, name: 'Todos'});
        $scope.types = MapasCulturais.entityTypes;
        $scope.location = $location;

        $rootScope.$on('$locationChangeSuccess', $scope.parseHash);

        if($location.hash() === '') {
            $scope.tabClick('agent');
        } else {
            $scope.parseHash();
        }

        $scope.$watch('data', $scope.dataChange, true);

        $scope.agents = [];
        $scope.spaces = [];
        $scope.events = [];
        $scope.projects = [];

        $rootScope.$on('searchResultsReady', function(ev, results){
            if($scope.data.global.viewMode !== 'list')
                return;

            $rootScope.isPaginating = false;

            if(results.paginating){
                $scope.agents = $scope.agents.concat(results.agent ? results.agent : []);
                $scope.events = $scope.events.concat(results.event ? results.event : []);
                $scope.spaces = $scope.spaces.concat(results.space ? results.space : []);
                $scope.projects = $scope.projects.concat(results.project ? results.project : []);
            }else{
                $scope.agents = results.agent ? results.agent : [];
                $scope.events = results.event ? results.event : [];
                $scope.spaces = results.space ? results.space : [];
                $scope.projects = results.project ? results.project : [];
            }
        });

        var infiniteScrollTimeout = null;

        $scope.addMore = function(entity){
            if($scope.data.global.viewMode !== 'list')
                return;

            if(entity !== $scope.data.global.filterEntity)
                return;

            if($rootScope.isPaginating)
                return;

            if($scope[entity + 's'].length === 0 || $scope[entity + 's'].length < 10)
                return;

            $rootScope.pagination[entity]++;
            // para não chamar 2 vezes o search quando está carregando a primeira página (o filtro mudou)
            if($rootScope.pagination[entity] > 1)
                $rootScope.$emit('resultPagination', $scope.data);
        };

        $scope.numResults = function (num, entity){
            if($scope.data.global.viewMode === 'map' && $scope.resultsNotInMap && $scope.resultsNotInMap[entity]){
                return num - $scope.resultsNotInMap[entity];
            }else{
                return num;
            }
        }

        $scope.numAgents = 0;
        $scope.numSpaces = 0;
        $scope.numEvents = {
            events: 0,
            spaces: 0
        };
        $scope.numEventsInList = 0;
        $scope.numProjects = 0;

        $rootScope.$on('searchCountResultsReady', function(ev, results){
            $scope.numAgents = parseInt(results.agent);
            $scope.numSpaces = parseInt(results.space);

            if($scope.data.global.viewMode === 'list'){
                $scope.numEventsInList = results.event;
            }else{
                if(results.event){
                    $scope.numEvents = {
                        events: parseInt(results.event.events),
                        spaces: parseInt(results.event.spaces)
                    };
                }else{
                    $scope.numEvents = {
                        events: 0,
                        spaces: 0
                    };
                };
            }
            $scope.numProjects = parseInt(results.project);
        });

        $rootScope.$on('findOneResultReady', function(ev, result){
            $scope.openEntity = result;
        });

        var formatDate = function(date){
            return moment(date).format('DD/MM/YYYY');
        };

        $scope.dateOptions = {
            dateFormat: 'dd/mm/yy'
        };

        $scope.$watch('data.event.from', function(){
            if(new Date($scope.data.event.from) > new Date($scope.data.event.to))
                $scope.data.event.to = $scope.data.event.from;
        });

        $scope.$watch('data.event.to', function(newValue, oldValue){
            if(new Date($scope.data.event.to) < new Date($scope.data.event.from))
                $scope.data.event.from = $scope.data.event.to;
        });

       $scope.showEventDateFilter = function(){
            var from = $scope.data.event.from,
                to = $scope.data.event.to;

            return from !== skeletonData.event.from || to !== skeletonData.event.to;
        };

        $scope.eventDateFilter = function(){
            var from = $scope.data.event.from,
                to = $scope.data.event.to;

            if(from === to)
                return formatDate(from);
            else
                return 'de ' + formatDate(from) + ' a ' + formatDate(to);
        };

        $scope.cleanEventDateFilters = function(){
            $scope.data.event.from = skeletonData.event.from;
            $scope.data.event.to = skeletonData.event.to;
        }

        $scope.readableProjectRegistrationDates = function(project){
            if(!project.registrationFrom)
                return false;

            var from = moment(project.registrationFrom).format('DD/MM/YYYY');
            var to = moment(project.registrationTo).format('DD/MM/YYYY');

            return from !== to ? from + ' - ' + to : from;
        };
    }]);
})(angular);<commit_msg>Event Search: default date interval set to one month (refs #413)<commit_after>(function(angular){
    "use strict";

    window.apply = null;

    var timeoutTime = 300;

    var defaultLocationRadius = 2000;

    var skeletonData = {
        global: {
            isVerified: false,
            isCombined: false,
            viewMode: 'map',
            filterEntity: null,
            openEntity: {
                id: null,
                type: null
            },
            locationFilters: {
                enabled: null, // circle, address, neighborhood
                circle: {
                    center: {
                        lat: null,
                        lng: null
                    },
                    radius: null
                },
                neighborhood: {
                    center: {
                        lat: null,
                        lng: null
                    },
                    radius: defaultLocationRadius
                },
                address: {
                    text: '',
                    center: {
                        lat: null,
                        lng: null
                    },
                    radius: defaultLocationRadius
                }
            },
            map: {
                zoom: null,
                center: {
                    lat: null,
                    lng: null
                }
            },

            enabled: {
                agent: false,
                space: false,
                event: false
            }
        },
        agent: {
            keyword: '',
            areas: [],
            type: null,
            isVerified: false
        },
        space: {
            keyword: '',
            areas: [],
            types: [],
            acessibilidade: false,
            isVerified: false
        },
        event: {
            keyword: '',
            linguagens: [],
            from: moment().format('YYYY-MM-DD'),
            to: moment().add(1, 'month').format('YYYY-MM-DD'),
            classificacaoEtaria: [],
            isVerified: false
        },
        project: {
            keyword: '',
            linguagens: [],
            types: [],
            isVerified: false,
            // registration open
            ropen: false
        }
    };

    var diffFilter = function (input) {
        return _diffFilter(input, skeletonData);
    };

    var isEmpty = function (value) {
        if(typeof value === 'undefined' ||
           value === null) return true;

        if(angular.isObject(value)) {
            if(angular.equals(value, {}) ||
               angular.equals(value, []))
                return true;
        }

        return false;
    };

    var _diffFilter = function (input, skeleton) {
        // returns the difference from the input structure and skeleton
        // don't include nulls

        if(typeof input === 'undefined' || typeof skeleton === 'undefined' || input === skeleton) return;

        if(!angular.isObject(input)|| angular.isArray(skeleton)) {
            return input;
        }

        var output = {};

        angular.forEach(input, function(value, key){
            var currVal = _diffFilter(value, skeleton[key]);

            if(isEmpty(currVal)) return;
            this[key] = currVal;
        }, output);

        return output;
    };

    var deepExtend = function (skeleton, extension) {
        angular.forEach(extension, function(value, key){
            if(angular.isObject(value) && !angular.isArray(value)) {
                deepExtend(skeleton[key], value);
                delete extension[key];
            }
        });
        angular.extend(skeleton, extension);
        return skeleton;
    };

    var app = angular.module('search', ['ng-mapasculturais', 'SearchService', 'FindOneService', 'SearchMap', 'SearchSpatial', 'rison', 'infinite-scroll', 'ui.date']);

    app.controller('SearchController', ['$scope', '$rootScope', '$location', '$log', '$rison', '$window', '$timeout', 'searchService', 'FindOneService', function($scope, $rootScope, $location, $log, $rison, $window, $timeout, searchService, FindOneService){
        $scope.defaultLocationRadius = defaultLocationRadius;

        $rootScope.resetPagination = function(){
            $rootScope.pagination = {
                agent: 1,
                space: 1,
                event: 1,
                project: 1
            };
        }
        $rootScope.resetPagination();

        $scope.defaultImageURL = MapasCulturais.defaultAvatarURL;
        $scope.getName = function(valores, id){
            return valores.filter(function(e){if(e.id === id) return true;})[0].name;
        };

        $scope.getId = function(valores, name){
            return valores.filter(function(e){if(e.name === name) return true;})[0].id;
        };

        $scope.isSelected = function(array, id){
            return (array.indexOf(id) !== -1);
        };

        $scope.toggleSelection = function(array, id){
            var index = array.indexOf(id);
            if(index !== -1){
                array.splice(index, 1);
            } else {
                array.push(id);
            }
        };

        $scope.toggleVerified = function (entity) {

                $scope.data[entity].isVerified = !$scope.data[entity].isVerified;
        };

        $scope.showInfobox = function (){
            return $scope.data.global.openEntity.id>0 && $scope.data.global.viewMode==='map' && $scope.data.global.enabled[$scope.data.global.openEntity.type];
        };
        $scope.showFilters = function(entity){
            if($scope.data.global.viewMode === 'map')
                return $scope.data.global.enabled[entity];
            else
                return $scope.data.global.filterEntity === entity;
        }

        $scope.hasFilter = function() {
            var ctx = {has: false};
            angular.forEach($scope.data, function(value, key) {
                if(key === 'global') return;
                this.has = this.has || !angular.equals(_diffFilter($scope.data[key], skeletonData[key]), {});
            }, ctx);

            return ctx.has ||
                   $scope.data.global.isVerified ||
                   $scope.data.global.locationFilters.enabled !== null;
        };

        $scope.cleanAllFilters = function () {
            angular.forEach($scope.data, function(value, key) {
                if(key === 'global') return;
                $scope.data[key] = angular.copy(skeletonData[key]);
            });
            $scope.data.global.isVerified = false;
            $scope.data.global.locationFilters = angular.copy(skeletonData.global.locationFilters);
        };

        $scope.cleanLocationFilters = function() {
            $scope.data.global.locationFilters = angular.copy(skeletonData.global.locationFilters);
        };

        $scope.tabClick = function(entity){
            var g = $scope.data.global;
            g.filterEntity = entity;
            if(entity === 'project')
                g.viewMode = 'list';

            if(g.viewMode === 'map'){
                var n = 0;
                for(var e in g.enabled)
                    if(g.enabled[e])
                        n++;

                if(n===0 || n === 1 && !g.enabled[entity]){
                    for(var e in g.enabled)
                        if(e === entity)
                            g.enabled[e] = true;
                        else
                            g.enabled[e] = false;
                }else if(n > 1 && !g.enabled[entity]){
                    g.enabled[entity] = true;
                }
            }
        };

        $scope.parseHash = function(){
            var newValue = $location.hash();
            if(newValue === '') {
                $scope.tabClick('agent');
                return;
            }

            if(newValue !== $rison.stringify(diffFilter($scope.data))){
                $scope.data = deepExtend(angular.copy(skeletonData), $rison.parse(newValue));
                $timeout.cancel($scope.timer);
                $scope.timer = $timeout(function() {
                    $rootScope.$emit('searchDataChange', $scope.data);
                },timeoutTime);
            }
        };

        $scope.dataChange = function(newValue, oldValue){
            if(newValue === undefined) return;
            if(newValue.global.viewMode === 'map'){
                var filterEntity = newValue.global.filterEntity;
                if(!newValue.global.enabled[filterEntity]){
                    var enabledEntities = 0;

                    angular.forEach(newValue.global.enabled, function(v,k){ if(v) enabledEntities++; });

                    if(enabledEntities === 1){
                        var obj = {space:false, agent:false, event:false};
                        obj[filterEntity] = true;
                        newValue.global.enabled = obj;
                    }else{
                        newValue.global.enabled[filterEntity] = true;
                    }
                    return;
                }
            }
            var serialized = $rison.stringify(diffFilter(newValue));
            $window.$timout = $timeout;
            if($location.hash() !== serialized){
                $timeout.cancel($scope.timer);
                if(oldValue && !angular.equals(oldValue.global.enabled, newValue.global.enabled)) {
                    $location.hash(serialized);
                    $scope.timer = $timeout(function() {
                        $rootScope.$emit('searchDataChange', $scope.data);
                    },timeoutTime);
                } else {
                    $scope.timer = $timeout(function() {
                        $location.hash(serialized);
                        $rootScope.$emit('searchDataChange', $scope.data);
                    }, timeoutTime);
                    $window.dataTimeout = $scope.timer;
                }
            }
            $window.scrollTo(0, $window.scrollY+1);
            $window.scrollTo(0, $window.scrollY-1);
        };

        $scope.data = angular.copy(skeletonData);

        $scope.areas = MapasCulturais.taxonomyTerms.area.map(function(el, i){ return {id: i, name: el}; });
        $scope.linguagens = MapasCulturais.taxonomyTerms.linguagem.map(function(el, i){ return {id: i, name: el}; });
        $scope.classificacoes = MapasCulturais.classificacoesEtarias.map(function(el, i){ return {id: i, name: el}; });

        MapasCulturais.entityTypes.agent.unshift({id:null, name: 'Todos'});
        $scope.types = MapasCulturais.entityTypes;
        $scope.location = $location;

        $rootScope.$on('$locationChangeSuccess', $scope.parseHash);

        if($location.hash() === '') {
            $scope.tabClick('agent');
        } else {
            $scope.parseHash();
        }

        $scope.$watch('data', $scope.dataChange, true);

        $scope.agents = [];
        $scope.spaces = [];
        $scope.events = [];
        $scope.projects = [];

        $rootScope.$on('searchResultsReady', function(ev, results){
            if($scope.data.global.viewMode !== 'list')
                return;

            $rootScope.isPaginating = false;

            if(results.paginating){
                $scope.agents = $scope.agents.concat(results.agent ? results.agent : []);
                $scope.events = $scope.events.concat(results.event ? results.event : []);
                $scope.spaces = $scope.spaces.concat(results.space ? results.space : []);
                $scope.projects = $scope.projects.concat(results.project ? results.project : []);
            }else{
                $scope.agents = results.agent ? results.agent : [];
                $scope.events = results.event ? results.event : [];
                $scope.spaces = results.space ? results.space : [];
                $scope.projects = results.project ? results.project : [];
            }
        });

        var infiniteScrollTimeout = null;

        $scope.addMore = function(entity){
            if($scope.data.global.viewMode !== 'list')
                return;

            if(entity !== $scope.data.global.filterEntity)
                return;

            if($rootScope.isPaginating)
                return;

            if($scope[entity + 's'].length === 0 || $scope[entity + 's'].length < 10)
                return;

            $rootScope.pagination[entity]++;
            // para não chamar 2 vezes o search quando está carregando a primeira página (o filtro mudou)
            if($rootScope.pagination[entity] > 1)
                $rootScope.$emit('resultPagination', $scope.data);
        };

        $scope.numResults = function (num, entity){
            if($scope.data.global.viewMode === 'map' && $scope.resultsNotInMap && $scope.resultsNotInMap[entity]){
                return num - $scope.resultsNotInMap[entity];
            }else{
                return num;
            }
        }

        $scope.numAgents = 0;
        $scope.numSpaces = 0;
        $scope.numEvents = {
            events: 0,
            spaces: 0
        };
        $scope.numEventsInList = 0;
        $scope.numProjects = 0;

        $rootScope.$on('searchCountResultsReady', function(ev, results){
            $scope.numAgents = parseInt(results.agent);
            $scope.numSpaces = parseInt(results.space);

            if($scope.data.global.viewMode === 'list'){
                $scope.numEventsInList = results.event;
            }else{
                if(results.event){
                    $scope.numEvents = {
                        events: parseInt(results.event.events),
                        spaces: parseInt(results.event.spaces)
                    };
                }else{
                    $scope.numEvents = {
                        events: 0,
                        spaces: 0
                    };
                };
            }
            $scope.numProjects = parseInt(results.project);
        });

        $rootScope.$on('findOneResultReady', function(ev, result){
            $scope.openEntity = result;
        });

        var formatDate = function(date){
            return moment(date).format('DD/MM/YYYY');
        };

        $scope.dateOptions = {
            dateFormat: 'dd/mm/yy'
        };

        $scope.$watch('data.event.from', function(){
            if(new Date($scope.data.event.from) > new Date($scope.data.event.to))
                $scope.data.event.to = $scope.data.event.from;
        });

        $scope.$watch('data.event.to', function(newValue, oldValue){
            if(new Date($scope.data.event.to) < new Date($scope.data.event.from))
                $scope.data.event.from = $scope.data.event.to;
        });

       $scope.showEventDateFilter = function(){
            var from = $scope.data.event.from,
                to = $scope.data.event.to;

            return from !== skeletonData.event.from || to !== skeletonData.event.to;
        };

        $scope.eventDateFilter = function(){
            var from = $scope.data.event.from,
                to = $scope.data.event.to;

            if(from === to)
                return formatDate(from);
            else
                return 'de ' + formatDate(from) + ' a ' + formatDate(to);
        };

        $scope.cleanEventDateFilters = function(){
            $scope.data.event.from = skeletonData.event.from;
            $scope.data.event.to = skeletonData.event.to;
        }

        $scope.readableProjectRegistrationDates = function(project){
            if(!project.registrationFrom)
                return false;

            var from = moment(project.registrationFrom).format('DD/MM/YYYY');
            var to = moment(project.registrationTo).format('DD/MM/YYYY');

            return from !== to ? from + ' - ' + to : from;
        };
    }]);
})(angular);<|endoftext|>


<commit_before>console.time('init')

const menubar = require('./menubar')
const { Menu, shell, dialog, globalShortcut } = require('electron')
const updater = require('./updater')
const { SHORTCUT } = require('../constants')
const createMenuTemplate = require('./menuTemplate')
const bindIpcMain = require('./ipc')
const log = require('./log')

function openExternal (e, url) {
  e.preventDefault()
  shell.openExternal(url)
}

function registerGlobalShortcut (accelerator, mb) {
  globalShortcut.unregisterAll()
  globalShortcut.register(accelerator, () => {
    menubar.window.isVisible() ? menubar.hideWindow() : menubar.showWindow()
  })
}

function checkForUpdates () {
  updater((err, newVersion) => {
    if (err) return
    const confirm = dialog.showMessageBox({
      type: 'info',
      title: 'Update available',
      message: `A new version (${newVersion}) of Todoo is available.`,
      detail: 'Do you want to download it now?',
      buttons: ['Yes', 'No']
    })
    if (confirm === 0) {
      shell.openExternal('https://github.com/vesparny/todoo/releases')
    }
  })
}

process.on('uncaughtException', function (err) {
  dialog.showErrorBox('Uncaught Exception: ' + err.message, err.stack || '')
  menubar.app.quit()
})

menubar.app.ipcReady = false

bindIpcMain()

menubar.on('ready', () => {
  log('App is ready')
  const menu = Menu.buildFromTemplate(createMenuTemplate(menubar, shell))
  Menu.setApplicationMenu(menu)
  checkForUpdates()
  registerGlobalShortcut(SHORTCUT)
  menubar.showWindow()

  menubar.window.webContents.on('new-window', openExternal)
  menubar.window.webContents.on('will-navigate', openExternal)
})

menubar.app.on('will-quit', function () {
  console.log('app is about to quit')
  globalShortcut.unregisterAll()
})

menubar.app.on('activate', function () {
  menubar.showWindow()
})

menubar.app.on('browser-window-focus', function () {
  menubar.showWindow()
})

menubar.app.once('ipcReady', function () {
  log('ipcReady event received from renderer process')
  console.timeEnd('init')
})
<commit_msg>feat(app): hide dock<commit_after>console.time('init')

const menubar = require('./menubar')
const { Menu, shell, dialog, globalShortcut } = require('electron')
const updater = require('./updater')
const { SHORTCUT } = require('../constants')
const createMenuTemplate = require('./menuTemplate')
const bindIpcMain = require('./ipc')
const log = require('./log')

function openExternal (e, url) {
  e.preventDefault()
  shell.openExternal(url)
}

function registerGlobalShortcut (accelerator, mb) {
  globalShortcut.unregisterAll()
  globalShortcut.register(accelerator, () => {
    menubar.window.isVisible() ? menubar.hideWindow() : menubar.showWindow()
  })
}

function checkForUpdates () {
  updater((err, newVersion) => {
    if (err) return
    const confirm = dialog.showMessageBox({
      type: 'info',
      title: 'Update available',
      message: `A new version (${newVersion}) of Todoo is available.`,
      detail: 'Do you want to download it now?',
      buttons: ['Yes', 'No']
    })
    if (confirm === 0) {
      shell.openExternal('https://github.com/vesparny/todoo/releases')
    }
  })
}

process.on('uncaughtException', function (err) {
  dialog.showErrorBox('Uncaught Exception: ' + err.message, err.stack || '')
  menubar.app.quit()
})

menubar.app.ipcReady = false

bindIpcMain()

menubar.on('ready', () => {
  log('App is ready')
  const menu = Menu.buildFromTemplate(createMenuTemplate(menubar, shell))
  Menu.setApplicationMenu(menu)
  checkForUpdates()
  registerGlobalShortcut(SHORTCUT)
  menubar.showWindow()

  menubar.window.webContents.on('new-window', openExternal)
  menubar.window.webContents.on('will-navigate', openExternal)
  // https://github.com/maxogden/menubar/issues/76
  setTimeout(() => menubar.showWindow(), 1)
})

menubar.app.on('will-quit', function () {
  console.log('app is about to quit')
  globalShortcut.unregisterAll()
})

/*
menubar.app.on('activate', function () {
  menubar.showWindow()
})

menubar.app.on('browser-window-focus', function () {
  menubar.showWindow()
})
*/
menubar.app.once('ipcReady', function () {
  log('ipcReady event received from renderer process')
  console.timeEnd('init')
})
<|endoftext|>


<commit_before>import { getExternalResources } from '../../../core/util/resource';
import VectorLayer from '../../../layer/VectorLayer';
import OverlayLayerCanvasRenderer from './OverlayLayerCanvasRenderer';
import PointExtent from '../../../geo/PointExtent';
import * as vec3 from '../../../core/util/vec3';
import { now } from '../../../core/util/common';
import { getPointsResultPts } from '../../../core/util';
const TEMP_EXTENT = new PointExtent();
const TEMP_VEC3 = [];
const TEMP_FIXEDEXTENT = new PointExtent();
const PLACEMENT_CENTER = 'center';

/**
 * @classdesc
 * Renderer class based on HTML5 Canvas2D for VectorLayers
 * @protected
 * @memberOf renderer
 * @name VectorLayerCanvasRenderer
 * @extends renderer.OverlaylayerCanvasRenderer
 * @param {VectorLayer} layer - layer to render
 */
class VectorLayerRenderer extends OverlayLayerCanvasRenderer {

    getImageData() {
        //如果不开启geometry event 或者 渲染频率很高 不要取缓存了，因为getImageData是个很昂贵的操作
        if (!this.layer.options['geometryEvents'] || (!this._lastRenderTime) || (now() - this._lastRenderTime) < 32) {
            return null;
        }
        if (!this._imageData) {
            const { width, height } = this.context.canvas;
            this._imageData = this.context.getImageData(0, 0, width, height);
        }
        return this._imageData;
    }

    clearImageData() {
        //每次渲染完成清除缓存的imageData
        this._imageData = null;
        delete this._imageData;
        this._lastRenderTime = now();
    }

    checkResources() {
        const resources = super.checkResources.apply(this, arguments);
        let style = this.layer.getStyle();
        if (style) {
            if (!Array.isArray(style)) {
                style = [style];
            }
            style.forEach(s => {
                const res = getExternalResources(s['symbol'], true);
                for (let i = 0, l = res.length; i < l; i++) {
                    if (!this.resources.isResourceLoaded(res[i])) {
                        resources.push(res[i]);
                    }
                }
            });
        }
        return resources;
    }

    needToRedraw() {
        const map = this.getMap();
        if (map.isInteracting() && this.layer.options['enableAltitude']) {
            return true;
        }
        // don't redraw when map is zooming without pitch and layer doesn't have any point symbolizer.
        if (map.isZooming() && !map.isRotating() && !map.getPitch() && !this._hasPoint && this.layer.constructor === VectorLayer) {
            return false;
        }
        return super.needToRedraw();
    }

    /**
     * render layer
     * @param  {Geometry[]} geometries   geometries to render
     * @param  {Boolean} ignorePromise   whether escape step of promise
     */
    draw() {
        if (!this.getMap()) {
            return;
        }
        if (!this.layer.isVisible() || this.layer.isEmpty()) {
            this.clearCanvas();
            this.completeRender();
            return;
        }

        this.prepareCanvas();

        this.drawGeos();

        this.completeRender();
    }

    isBlank() {
        if (!this.context) {
            return false;
        }
        return !this.context.canvas._drawn;
    }

    drawOnInteracting() {
        if (!this._geosToDraw) {
            return;
        }
        this._updateMapStateCache();
        this._updateDisplayExtent();
        const map = this.getMap();
        //refresh geometries on zooming
        const count = this.layer.getCount();
        const res = this.mapStateCache.resolution;
        if (map.isZooming() &&
            map.options['seamlessZoom'] && this._drawnRes !== undefined && res > this._drawnRes * 1.5 &&
            this._geosToDraw.length < count || map.isMoving() || map.isInteracting()) {
            this.prepareToDraw();
            this._batchConversionMarkers(this.mapStateCache.glRes);
            if (!this._onlyHasPoint) {
                this.forEachGeo(this.checkGeo, this);
            }
            this._drawnRes = res;
        }
        this._sortByDistanceToCamera(map.cameraPosition);
        for (let i = 0, l = this._geosToDraw.length; i < l; i++) {
            const geo = this._geosToDraw[i];
            if (!geo._isCheck) {
                if (!geo.isVisible()) {
                    delete geo._cPoint;
                    delete geo._inCurrentView;
                    continue;
                }
            }
            geo._paint(this._displayExtent);
            // https://richardartoul.github.io/jekyll/update/2015/04/26/hidden-classes.html
            // https://juejin.cn/post/6972702293636415519
            this._geosToDraw[i]._cPoint = undefined;
            this._geosToDraw[i]._inCurrentView = undefined;
        }
        this.clearImageData();
    }

    /**
     * Show and render
     * @override
     */
    show() {
        this.layer.forEach(function (geo) {
            geo._repaint();
        });
        super.show.apply(this, arguments);
    }

    forEachGeo(fn, context) {
        this.layer.forEach(fn, context);
    }

    drawGeos() {
        this._updateMapStateCache();
        this._drawnRes = this.mapStateCache.resolution;
        this._updateDisplayExtent();
        this.prepareToDraw();
        this._batchConversionMarkers(this.mapStateCache.glRes);
        if (!this._onlyHasPoint) {
            this.forEachGeo(this.checkGeo, this);
        }
        this._sortByDistanceToCamera(this.getMap().cameraPosition);
        for (let i = 0, len = this._geosToDraw.length; i < len; i++) {
            this._geosToDraw[i]._paint();
            this._geosToDraw[i]._cPoint = undefined;
            this._geosToDraw[i]._inCurrentView = undefined;
        }
        this.clearImageData();
    }

    prepareToDraw() {
        this._hasPoint = false;
        this._geosToDraw = [];
    }

    checkGeo(geo) {
        //点的话已经在批量处理里判断过了
        if (geo.type === 'Point' && this._onlyHasPoint !== undefined) {
            if (geo._inCurrentView) {
                this._hasPoint = true;
                geo._isCheck = true;
                this._geosToDraw.push(geo);
            }
            return;
        }
        // LineString ,Polygon,Circle etc
        geo._isCheck = false;
        if (!geo || !geo.isVisible() || !geo.getMap() ||
            !geo.getLayer() || (!geo.getLayer().isCanvasRender())) {
            return;
        }

        const painter = geo._getPainter();
        let inCurrentView = true;
        if (geo._inCurrentView) {
            inCurrentView = true;
        } else if (geo._inCurrentView === false) {
            inCurrentView = false;
        } else {
            const extent2D = painter.get2DExtent(this.resources, TEMP_EXTENT);
            if (!extent2D || !extent2D.intersects(this._displayExtent)) {
                inCurrentView = false;
            }
        }
        if (!inCurrentView) {
            return;
        }
        if (painter.hasPoint()) {
            this._hasPoint = true;
        }
        geo._isCheck = true;
        this._geosToDraw.push(geo);
    }

    onZoomEnd() {
        delete this.canvasExtent2D;
        super.onZoomEnd.apply(this, arguments);
    }

    onRemove() {
        this.forEachGeo(function (g) {
            g.onHide();
        });
        delete this._geosToDraw;
    }

    onGeometryPropertiesChange(param) {
        if (param) {
            this.layer._styleGeometry(param['target']);
        }
        super.onGeometryPropertiesChange(param);
    }

    _updateDisplayExtent() {
        let extent2D = this.canvasExtent2D;
        if (this._maskExtent) {
            if (!this._maskExtent.intersects(extent2D)) {
                this.completeRender();
                return;
            }
            extent2D = extent2D.intersection(this._maskExtent);
        }
        this._displayExtent = extent2D;
    }

    identifyAtPoint(point, options = {}) {
        const geometries = this._geosToDraw;
        if (!geometries) {
            return [];
        }
        return this.layer._hitGeos(geometries, point, options);
    }

    _updateMapStateCache() {
        const map = this.getMap();
        const offset = map._pointToContainerPoint(this.southWest)._add(0, -map.height);
        const resolution = map.getResolution();
        const pitch = map.getPitch();
        const bearing = map.getBearing();
        const glScale = map.getGLScale();
        const glRes = map.getGLRes();
        const containerExtent = map.getContainerExtent();
        const _2DExtent = map._get2DExtent();
        const glExtent = map._get2DExtentAtRes(glRes);
        this.mapStateCache = {
            resolution,
            pitch,
            bearing,
            glScale,
            glRes,
            _2DExtent,
            glExtent,
            containerExtent,
            offset
        };
        return this;
    }

    // Better performance of batch coordinate conversion
    // 优化前 11fps
    // 优化后 15fps
    _batchConversionMarkers(glRes) {
        this._onlyHasPoint = undefined;
        if (!this._constructorIsThis()) {
            return [];
        }
        const cPoints = [];
        const markers = [];
        const altitudes = [];
        const altitudeCache = {};
        const layer = this.layer;
        const layerOpts = layer.options;
        const layerAltitude = layer.getAltitude ? layer.getAltitude() : 0;
        const isCanvasRender = layer.isCanvasRender();
        this._onlyHasPoint = true;
        //Traverse all Geo
        let idx = 0;
        for (let i = 0, len = this.layer._geoList.length; i < len; i++) {
            const geo = this.layer._geoList[i];
            const type = geo.getType();
            if (type === 'Point') {
                let painter = geo._painter;
                if (!painter) {
                    painter = geo._getPainter();
                }
                const point = painter.getRenderPoints(PLACEMENT_CENTER)[0][0];
                const altitude = layerOpts['enableAltitude'] ? geo.getAltitude() : layerAltitude;
                //减少方法的调用
                if (altitudeCache[altitude] === undefined) {
                    altitudeCache[altitude] = painter.getAltitude();
                }
                cPoints[idx] = point;
                altitudes[idx] = altitudeCache[altitude];
                markers[idx] = geo;
                idx++;
            } else {
                this._onlyHasPoint = false;
            }
        }
        if (idx === 0) {
            return [];
        }
        const map = this.getMap();
        let pts = getPointsResultPts(cPoints, '_pt');
        pts = map._pointsAtResToContainerPoints(cPoints, glRes, altitudes, pts);
        const containerExtent = map.getContainerExtent();
        const { xmax, ymax, xmin, ymin } = containerExtent;
        const extentCache = {};
        for (let i = 0, len = markers.length; i < len; i++) {
            const geo = markers[i];
            geo._cPoint = pts[i];
            if (!geo._cPoint) {
                geo._inCurrentView = false;
                continue;
            }
            const { x, y } = pts[i];
            //Is the point in view
            geo._inCurrentView = (x >= xmin && y >= ymin && x <= xmax && y <= ymax);
            //不在视野内的，再用fixedExtent 精确判断下
            if (!geo._inCurrentView) {
                const symbolkey = geo.getSymbolHash();
                let fixedExtent;
                if (symbolkey) {
                    //相同的symbol 不要重复计算
                    fixedExtent = extentCache[symbolkey] = (extentCache[symbolkey] || geo._painter.getFixedExtent());
                } else {
                    fixedExtent = geo._painter.getFixedExtent();
                }
                TEMP_FIXEDEXTENT.set(fixedExtent.xmin, fixedExtent.ymin, fixedExtent.xmax, fixedExtent.ymax);
                TEMP_FIXEDEXTENT._add(pts[i]);
                geo._inCurrentView = TEMP_FIXEDEXTENT.intersects(containerExtent);
            }
            if (geo._inCurrentView) {
                if (!geo.isVisible() || !isCanvasRender) {
                    geo._inCurrentView = false;
                }
                //如果当前图层上只有点，整个checkGeo都不用执行了,这里已经把所有的点都判断了
                if (this._onlyHasPoint && geo._inCurrentView) {
                    this._hasPoint = true;
                    geo._isCheck = true;
                    this._geosToDraw.push(geo);
                }
            }
        }
        return pts;
    }

    _sortByDistanceToCamera(cameraPosition) {
        if (!this.layer.options['sortByDistanceToCamera']) {
            return;
        }
        if (!this._geosToDraw.length) {
            return;
        }
        const map = this.getMap();
        const p = map.distanceToPoint(1000, 0, map.getGLScale()).x;
        const meterScale = p / 1000;
        const placement = 'center';
        this._geosToDraw.sort((a, b) => {
            const type0 = a.getType();
            const type1 = b.getType();
            if (type0 !== 'Point' || type1 !== 'Point') {
                return 0;
            }
            const painter0 = a._painter;
            const painter1 = b._painter;
            if (!painter0 || !painter1) {
                return 0;
            }
            const point0 = painter0.getRenderPoints(placement)[0][0];
            const point1 = painter1.getRenderPoints(placement)[0][0];
            const alt0 = painter0.getAltitude() * meterScale;
            const alt1 = painter1.getAltitude() * meterScale;
            vec3.set(TEMP_VEC3, point0.x, point0.y, alt0);
            const dist0 = vec3.distance(TEMP_VEC3, cameraPosition);
            vec3.set(TEMP_VEC3, point1.x, point1.y, alt1);
            const dist1 = vec3.distance(TEMP_VEC3, cameraPosition);
            return dist1 - dist0;
        });
    }

    _constructorIsThis() {
        return this.constructor === VectorLayerRenderer;
    }
}

VectorLayer.registerRenderer('canvas', VectorLayerRenderer);

export default VectorLayerRenderer;
<commit_msg>fix VectorLayerRenderer getImageData error when some geometries have external resources in another domain fix #1543 (#1544)<commit_after>import { getExternalResources } from '../../../core/util/resource';
import VectorLayer from '../../../layer/VectorLayer';
import OverlayLayerCanvasRenderer from './OverlayLayerCanvasRenderer';
import PointExtent from '../../../geo/PointExtent';
import * as vec3 from '../../../core/util/vec3';
import { now } from '../../../core/util/common';
import { getPointsResultPts } from '../../../core/util';
const TEMP_EXTENT = new PointExtent();
const TEMP_VEC3 = [];
const TEMP_FIXEDEXTENT = new PointExtent();
const PLACEMENT_CENTER = 'center';

/**
 * @classdesc
 * Renderer class based on HTML5 Canvas2D for VectorLayers
 * @protected
 * @memberOf renderer
 * @name VectorLayerCanvasRenderer
 * @extends renderer.OverlaylayerCanvasRenderer
 * @param {VectorLayer} layer - layer to render
 */
class VectorLayerRenderer extends OverlayLayerCanvasRenderer {

    getImageData() {
        //如果不开启geometry event 或者 渲染频率很高 不要取缓存了，因为getImageData是个很昂贵的操作
        if (!this.layer.options['geometryEvents'] || (!this._lastRenderTime) || (now() - this._lastRenderTime) < 32) {
            return null;
        }
        if (!this._imageData) {
            const { width, height } = this.context.canvas;
            try {
                this._imageData = this.context.getImageData(0, 0, width, height);
            } catch (error) {
                console.warn('hit detect failed with tainted canvas, some geometries have external resources in another domain:\n', error);
            }
        }
        return this._imageData;
    }

    clearImageData() {
        //每次渲染完成清除缓存的imageData
        this._imageData = null;
        delete this._imageData;
        this._lastRenderTime = now();
    }

    checkResources() {
        const resources = super.checkResources.apply(this, arguments);
        let style = this.layer.getStyle();
        if (style) {
            if (!Array.isArray(style)) {
                style = [style];
            }
            style.forEach(s => {
                const res = getExternalResources(s['symbol'], true);
                for (let i = 0, l = res.length; i < l; i++) {
                    if (!this.resources.isResourceLoaded(res[i])) {
                        resources.push(res[i]);
                    }
                }
            });
        }
        return resources;
    }

    needToRedraw() {
        const map = this.getMap();
        if (map.isInteracting() && this.layer.options['enableAltitude']) {
            return true;
        }
        // don't redraw when map is zooming without pitch and layer doesn't have any point symbolizer.
        if (map.isZooming() && !map.isRotating() && !map.getPitch() && !this._hasPoint && this.layer.constructor === VectorLayer) {
            return false;
        }
        return super.needToRedraw();
    }

    /**
     * render layer
     * @param  {Geometry[]} geometries   geometries to render
     * @param  {Boolean} ignorePromise   whether escape step of promise
     */
    draw() {
        if (!this.getMap()) {
            return;
        }
        if (!this.layer.isVisible() || this.layer.isEmpty()) {
            this.clearCanvas();
            this.completeRender();
            return;
        }

        this.prepareCanvas();

        this.drawGeos();

        this.completeRender();
    }

    isBlank() {
        if (!this.context) {
            return false;
        }
        return !this.context.canvas._drawn;
    }

    drawOnInteracting() {
        if (!this._geosToDraw) {
            return;
        }
        this._updateMapStateCache();
        this._updateDisplayExtent();
        const map = this.getMap();
        //refresh geometries on zooming
        const count = this.layer.getCount();
        const res = this.mapStateCache.resolution;
        if (map.isZooming() &&
            map.options['seamlessZoom'] && this._drawnRes !== undefined && res > this._drawnRes * 1.5 &&
            this._geosToDraw.length < count || map.isMoving() || map.isInteracting()) {
            this.prepareToDraw();
            this._batchConversionMarkers(this.mapStateCache.glRes);
            if (!this._onlyHasPoint) {
                this.forEachGeo(this.checkGeo, this);
            }
            this._drawnRes = res;
        }
        this._sortByDistanceToCamera(map.cameraPosition);
        for (let i = 0, l = this._geosToDraw.length; i < l; i++) {
            const geo = this._geosToDraw[i];
            if (!geo._isCheck) {
                if (!geo.isVisible()) {
                    delete geo._cPoint;
                    delete geo._inCurrentView;
                    continue;
                }
            }
            geo._paint(this._displayExtent);
            // https://richardartoul.github.io/jekyll/update/2015/04/26/hidden-classes.html
            // https://juejin.cn/post/6972702293636415519
            this._geosToDraw[i]._cPoint = undefined;
            this._geosToDraw[i]._inCurrentView = undefined;
        }
        this.clearImageData();
    }

    /**
     * Show and render
     * @override
     */
    show() {
        this.layer.forEach(function (geo) {
            geo._repaint();
        });
        super.show.apply(this, arguments);
    }

    forEachGeo(fn, context) {
        this.layer.forEach(fn, context);
    }

    drawGeos() {
        this._updateMapStateCache();
        this._drawnRes = this.mapStateCache.resolution;
        this._updateDisplayExtent();
        this.prepareToDraw();
        this._batchConversionMarkers(this.mapStateCache.glRes);
        if (!this._onlyHasPoint) {
            this.forEachGeo(this.checkGeo, this);
        }
        this._sortByDistanceToCamera(this.getMap().cameraPosition);
        for (let i = 0, len = this._geosToDraw.length; i < len; i++) {
            this._geosToDraw[i]._paint();
            this._geosToDraw[i]._cPoint = undefined;
            this._geosToDraw[i]._inCurrentView = undefined;
        }
        this.clearImageData();
    }

    prepareToDraw() {
        this._hasPoint = false;
        this._geosToDraw = [];
    }

    checkGeo(geo) {
        //点的话已经在批量处理里判断过了
        if (geo.type === 'Point' && this._onlyHasPoint !== undefined) {
            if (geo._inCurrentView) {
                this._hasPoint = true;
                geo._isCheck = true;
                this._geosToDraw.push(geo);
            }
            return;
        }
        // LineString ,Polygon,Circle etc
        geo._isCheck = false;
        if (!geo || !geo.isVisible() || !geo.getMap() ||
            !geo.getLayer() || (!geo.getLayer().isCanvasRender())) {
            return;
        }

        const painter = geo._getPainter();
        let inCurrentView = true;
        if (geo._inCurrentView) {
            inCurrentView = true;
        } else if (geo._inCurrentView === false) {
            inCurrentView = false;
        } else {
            const extent2D = painter.get2DExtent(this.resources, TEMP_EXTENT);
            if (!extent2D || !extent2D.intersects(this._displayExtent)) {
                inCurrentView = false;
            }
        }
        if (!inCurrentView) {
            return;
        }
        if (painter.hasPoint()) {
            this._hasPoint = true;
        }
        geo._isCheck = true;
        this._geosToDraw.push(geo);
    }

    onZoomEnd() {
        delete this.canvasExtent2D;
        super.onZoomEnd.apply(this, arguments);
    }

    onRemove() {
        this.forEachGeo(function (g) {
            g.onHide();
        });
        delete this._geosToDraw;
    }

    onGeometryPropertiesChange(param) {
        if (param) {
            this.layer._styleGeometry(param['target']);
        }
        super.onGeometryPropertiesChange(param);
    }

    _updateDisplayExtent() {
        let extent2D = this.canvasExtent2D;
        if (this._maskExtent) {
            if (!this._maskExtent.intersects(extent2D)) {
                this.completeRender();
                return;
            }
            extent2D = extent2D.intersection(this._maskExtent);
        }
        this._displayExtent = extent2D;
    }

    identifyAtPoint(point, options = {}) {
        const geometries = this._geosToDraw;
        if (!geometries) {
            return [];
        }
        return this.layer._hitGeos(geometries, point, options);
    }

    _updateMapStateCache() {
        const map = this.getMap();
        const offset = map._pointToContainerPoint(this.southWest)._add(0, -map.height);
        const resolution = map.getResolution();
        const pitch = map.getPitch();
        const bearing = map.getBearing();
        const glScale = map.getGLScale();
        const glRes = map.getGLRes();
        const containerExtent = map.getContainerExtent();
        const _2DExtent = map._get2DExtent();
        const glExtent = map._get2DExtentAtRes(glRes);
        this.mapStateCache = {
            resolution,
            pitch,
            bearing,
            glScale,
            glRes,
            _2DExtent,
            glExtent,
            containerExtent,
            offset
        };
        return this;
    }

    // Better performance of batch coordinate conversion
    // 优化前 11fps
    // 优化后 15fps
    _batchConversionMarkers(glRes) {
        this._onlyHasPoint = undefined;
        if (!this._constructorIsThis()) {
            return [];
        }
        const cPoints = [];
        const markers = [];
        const altitudes = [];
        const altitudeCache = {};
        const layer = this.layer;
        const layerOpts = layer.options;
        const layerAltitude = layer.getAltitude ? layer.getAltitude() : 0;
        const isCanvasRender = layer.isCanvasRender();
        this._onlyHasPoint = true;
        //Traverse all Geo
        let idx = 0;
        for (let i = 0, len = this.layer._geoList.length; i < len; i++) {
            const geo = this.layer._geoList[i];
            const type = geo.getType();
            if (type === 'Point') {
                let painter = geo._painter;
                if (!painter) {
                    painter = geo._getPainter();
                }
                const point = painter.getRenderPoints(PLACEMENT_CENTER)[0][0];
                const altitude = layerOpts['enableAltitude'] ? geo.getAltitude() : layerAltitude;
                //减少方法的调用
                if (altitudeCache[altitude] === undefined) {
                    altitudeCache[altitude] = painter.getAltitude();
                }
                cPoints[idx] = point;
                altitudes[idx] = altitudeCache[altitude];
                markers[idx] = geo;
                idx++;
            } else {
                this._onlyHasPoint = false;
            }
        }
        if (idx === 0) {
            return [];
        }
        const map = this.getMap();
        let pts = getPointsResultPts(cPoints, '_pt');
        pts = map._pointsAtResToContainerPoints(cPoints, glRes, altitudes, pts);
        const containerExtent = map.getContainerExtent();
        const { xmax, ymax, xmin, ymin } = containerExtent;
        const extentCache = {};
        for (let i = 0, len = markers.length; i < len; i++) {
            const geo = markers[i];
            geo._cPoint = pts[i];
            if (!geo._cPoint) {
                geo._inCurrentView = false;
                continue;
            }
            const { x, y } = pts[i];
            //Is the point in view
            geo._inCurrentView = (x >= xmin && y >= ymin && x <= xmax && y <= ymax);
            //不在视野内的，再用fixedExtent 精确判断下
            if (!geo._inCurrentView) {
                const symbolkey = geo.getSymbolHash();
                let fixedExtent;
                if (symbolkey) {
                    //相同的symbol 不要重复计算
                    fixedExtent = extentCache[symbolkey] = (extentCache[symbolkey] || geo._painter.getFixedExtent());
                } else {
                    fixedExtent = geo._painter.getFixedExtent();
                }
                TEMP_FIXEDEXTENT.set(fixedExtent.xmin, fixedExtent.ymin, fixedExtent.xmax, fixedExtent.ymax);
                TEMP_FIXEDEXTENT._add(pts[i]);
                geo._inCurrentView = TEMP_FIXEDEXTENT.intersects(containerExtent);
            }
            if (geo._inCurrentView) {
                if (!geo.isVisible() || !isCanvasRender) {
                    geo._inCurrentView = false;
                }
                //如果当前图层上只有点，整个checkGeo都不用执行了,这里已经把所有的点都判断了
                if (this._onlyHasPoint && geo._inCurrentView) {
                    this._hasPoint = true;
                    geo._isCheck = true;
                    this._geosToDraw.push(geo);
                }
            }
        }
        return pts;
    }

    _sortByDistanceToCamera(cameraPosition) {
        if (!this.layer.options['sortByDistanceToCamera']) {
            return;
        }
        if (!this._geosToDraw.length) {
            return;
        }
        const map = this.getMap();
        const p = map.distanceToPoint(1000, 0, map.getGLScale()).x;
        const meterScale = p / 1000;
        const placement = 'center';
        this._geosToDraw.sort((a, b) => {
            const type0 = a.getType();
            const type1 = b.getType();
            if (type0 !== 'Point' || type1 !== 'Point') {
                return 0;
            }
            const painter0 = a._painter;
            const painter1 = b._painter;
            if (!painter0 || !painter1) {
                return 0;
            }
            const point0 = painter0.getRenderPoints(placement)[0][0];
            const point1 = painter1.getRenderPoints(placement)[0][0];
            const alt0 = painter0.getAltitude() * meterScale;
            const alt1 = painter1.getAltitude() * meterScale;
            vec3.set(TEMP_VEC3, point0.x, point0.y, alt0);
            const dist0 = vec3.distance(TEMP_VEC3, cameraPosition);
            vec3.set(TEMP_VEC3, point1.x, point1.y, alt1);
            const dist1 = vec3.distance(TEMP_VEC3, cameraPosition);
            return dist1 - dist0;
        });
    }

    _constructorIsThis() {
        return this.constructor === VectorLayerRenderer;
    }
}

VectorLayer.registerRenderer('canvas', VectorLayerRenderer);

export default VectorLayerRenderer;
<|endoftext|>


<commit_before>'use strict';

const sinon = require('sinon');

describe('Locals', () => {
  const Locals = require('../../../lib/hexo/locals');
  const locals = new Locals();

  it('get() - name must be a string', () => {
    const errorCallback = sinon.spy(err => {
      err.should.have.property('message', 'name must be a string!');
    });

    try {
      locals.get();
    } catch (err) {
      errorCallback(err);
    }

    errorCallback.calledOnce.should.be.true;
  });

  it('set() - function', () => {
    locals.set('foo', () => 'foo');

    // cache should be clear after new data is set
    should.not.exist(locals.cache.cache.foo);
    locals.get('foo').should.eql('foo');
    // cache should be saved once it's get
    locals.cache.cache.foo.should.eql('foo');
  });

  it('set() - not function', () => {
    locals.set('foo', 'foo');
    locals.get('foo').should.eql('foo');
  });

  it('set() - name must be a string', () => {
    const errorCallback = sinon.spy(err => {
      err.should.have.property('message', 'name must be a string!');
    });

    try {
      locals.set();
    } catch (err) {
      errorCallback(err);
    }

    errorCallback.calledOnce.should.be.true;
  });

  it('set() - value is required', () => {
    const errorCallback = sinon.spy(err => {
      err.should.have.property('message', 'value is required!');
    });

    try {
      locals.set('test');
    } catch (err) {
      errorCallback(err);
    }

    errorCallback.calledOnce.should.be.true;
  });

  it('remove()', () => {
    locals.set('foo', 'foo');
    locals.get('foo');
    locals.remove('foo');

    should.not.exist(locals.getters.foo);
    should.not.exist(locals.cache.cache.foo);
  });

  it('remove() - name must be a string', () => {
    const errorCallback = sinon.spy(err => {
      err.should.have.property('message', 'name must be a string!');
    });

    try {
      locals.remove();
    } catch (err) {
      errorCallback(err);
    }

    errorCallback.calledOnce.should.be.true;
  });

  it('toObject()', () => {
    const locals = new Locals();

    locals.set('foo', 'foo');
    locals.set('bar', 'bar');
    locals.remove('bar');
    locals.toObject().should.eql({foo: 'foo'});
  });

  it('invalidate()', () => {
    locals.set('foo', 'foo');
    locals.get('foo');
    locals.invalidate();

    should.not.exist(locals.cache.cache.foo);
  });
});
<commit_msg>test(locals): use cache syntax<commit_after>'use strict';

const sinon = require('sinon');

describe('Locals', () => {
  const Locals = require('../../../lib/hexo/locals');
  const locals = new Locals();

  it('get() - name must be a string', () => {
    const errorCallback = sinon.spy(err => {
      err.should.have.property('message', 'name must be a string!');
    });

    try {
      locals.get();
    } catch (err) {
      errorCallback(err);
    }

    errorCallback.calledOnce.should.be.true;
  });

  it('set() - function', () => {
    locals.set('foo', () => 'foo');

    // cache should be clear after new data is set
    locals.cache.has('foo').should.eql(false);
    locals.get('foo').should.eql('foo');
    // cache should be saved once it's get
    locals.cache.get('foo').should.eql('foo');
  });

  it('set() - not function', () => {
    locals.set('foo', 'foo');
    locals.get('foo').should.eql('foo');
  });

  it('set() - name must be a string', () => {
    const errorCallback = sinon.spy(err => {
      err.should.have.property('message', 'name must be a string!');
    });

    try {
      locals.set();
    } catch (err) {
      errorCallback(err);
    }

    errorCallback.calledOnce.should.be.true;
  });

  it('set() - value is required', () => {
    const errorCallback = sinon.spy(err => {
      err.should.have.property('message', 'value is required!');
    });

    try {
      locals.set('test');
    } catch (err) {
      errorCallback(err);
    }

    errorCallback.calledOnce.should.be.true;
  });

  it('remove()', () => {
    locals.set('foo', 'foo');
    locals.get('foo');
    locals.remove('foo');

    should.not.exist(locals.getters.foo);
    locals.cache.has('foo').should.eql(false);
  });

  it('remove() - name must be a string', () => {
    const errorCallback = sinon.spy(err => {
      err.should.have.property('message', 'name must be a string!');
    });

    try {
      locals.remove();
    } catch (err) {
      errorCallback(err);
    }

    errorCallback.calledOnce.should.be.true;
  });

  it('toObject()', () => {
    const locals = new Locals();

    locals.set('foo', 'foo');
    locals.set('bar', 'bar');
    locals.remove('bar');
    locals.toObject().should.eql({foo: 'foo'});
  });

  it('invalidate()', () => {
    locals.set('foo', 'foo');
    locals.get('foo');
    locals.invalidate();

    locals.cache.has('foo').should.eql(false);
  });
});
<|endoftext|>


<commit_before>const mongojs = require('../app/js/infrastructure/mongojs')
const { db } = mongojs
const async = require('async')
const minilist = require('minimist')

const newTimeout = 240
const oldTimeoutLimits = {$gt: 60, $lt: 240}

const updateUser = function (user, callback) {
  console.log(`Updating user ${user._id}`)
  const update = {
    $set: {
      'features.compileTimeout': newTimeout
    }
  }
  db.users.update({
    _id: user._id,
    'features.compileTimeout': oldTimeoutLimits
  }, update, callback)
}

const updateUsers = (users, callback) =>
  async.eachLimit(users, ASYNC_LIMIT, updateUser, function (error) {
    if (error) {
      callback(error)
      return
    }
    counter += users.length
    console.log(`${counter} users updated`);
    if (DO_ALL) {
      return loopForUsers(callback)
    } else {
      console.log('*** run again to continue updating ***')
      return callback()
    }
  })

var loopForUsers = callback =>
  db.users.find(
    { 'features.compileTimeout': oldTimeoutLimits },
    { 'features.compileTimeout': 1 }
  ).limit(FETCH_LIMIT, function (error, users) {
    if (error) {
      callback(error)
      return
    }
    if (users.length === 0) {
      console.log(`DONE (${counter} users updated)`)
      return callback()
    }
    updateUsers(users, callback)
  })

var counter = 0
var run = () =>
  loopForUsers(function (error) {
    if (error) { throw error }
    process.exit()
  })

let FETCH_LIMIT, ASYNC_LIMIT, DO_ALL
var setup = function () {
  let args = minilist(process.argv.slice(2))
  // --fetch N  get N users each time
  FETCH_LIMIT = (args.fetch) ? args.fetch : 100
  // --async M  run M updates in parallel
  ASYNC_LIMIT = (args.async) ? args.async : 10
  // --all means run to completion
  if (args.all) {
    if (args.fetch) {
      console.error('error: do not use --fetch with --all')
      process.exit(1)
    } else {
      DO_ALL = true
      // if we are updating for all users then ignore the fetch limit.
      FETCH_LIMIT = 0
      // A limit() value of 0 (i.e. .limit(0)) is equivalent to setting
      // no limit.
      // https://docs.mongodb.com/manual/reference/method/cursor.limit
    }
  }
}

setup()
run()
<commit_msg>lint fix<commit_after>const mongojs = require('../app/js/infrastructure/mongojs')
const { db } = mongojs
const async = require('async')
const minilist = require('minimist')

const newTimeout = 240
const oldTimeoutLimits = {$gt: 60, $lt: 240}

const updateUser = function (user, callback) {
  console.log(`Updating user ${user._id}`)
  const update = {
    $set: {
      'features.compileTimeout': newTimeout
    }
  }
  db.users.update({
    _id: user._id,
    'features.compileTimeout': oldTimeoutLimits
  }, update, callback)
}

const updateUsers = (users, callback) =>
  async.eachLimit(users, ASYNC_LIMIT, updateUser, function (error) {
    if (error) {
      callback(error)
      return
    }
    counter += users.length
    console.log(`${counter} users updated`)
    if (DO_ALL) {
      return loopForUsers(callback)
    } else {
      console.log('*** run again to continue updating ***')
      return callback()
    }
  })

var loopForUsers = callback =>
  db.users.find(
    { 'features.compileTimeout': oldTimeoutLimits },
    { 'features.compileTimeout': 1 }
  ).limit(FETCH_LIMIT, function (error, users) {
    if (error) {
      callback(error)
      return
    }
    if (users.length === 0) {
      console.log(`DONE (${counter} users updated)`)
      return callback()
    }
    updateUsers(users, callback)
  })

var counter = 0
var run = () =>
  loopForUsers(function (error) {
    if (error) { throw error }
    process.exit()
  })

let FETCH_LIMIT, ASYNC_LIMIT, DO_ALL
var setup = function () {
  let args = minilist(process.argv.slice(2))
  // --fetch N  get N users each time
  FETCH_LIMIT = (args.fetch) ? args.fetch : 100
  // --async M  run M updates in parallel
  ASYNC_LIMIT = (args.async) ? args.async : 10
  // --all means run to completion
  if (args.all) {
    if (args.fetch) {
      console.error('error: do not use --fetch with --all')
      process.exit(1)
    } else {
      DO_ALL = true
      // if we are updating for all users then ignore the fetch limit.
      FETCH_LIMIT = 0
      // A limit() value of 0 (i.e. .limit(0)) is equivalent to setting
      // no limit.
      // https://docs.mongodb.com/manual/reference/method/cursor.limit
    }
  }
}

setup()
run()
<|endoftext|>


<commit_before>// @flow
import * as ConfigGen from '../config-gen'
import * as GregorGen from '../gregor-gen'
import * as RPCTypes from '../../constants/types/rpc-gen'
import * as SafeElectron from '../../util/safe-electron.desktop'
import * as Saga from '../../util/saga'
import logger from '../../logger'
import path from 'path'
import {NotifyPopup} from '../../native/notifications'
import {execFile} from 'child_process'
import {getEngine} from '../../engine'
import {getMainWindow} from '../../desktop/remote/util.desktop'
import {isWindows, socketPath} from '../../constants/platform.desktop'
import {kbfsNotification} from '../../util/kbfs-notifications'
import {quit} from '../../util/quit-helper'
import {showDockIcon} from '../../desktop/app/dock-icon.desktop'
import {writeLogLinesToFile} from '../../util/forward-logs'

function showShareActionSheet(options: {
  url?: ?any,
  message?: ?any,
}): Promise<{completed: boolean, method: string}> {
  throw new Error('Show Share Action - unsupported on this platform')
}

function downloadAndShowShareActionSheet(fileURL: string) {
  throw new Error('Download and show share action - unsupported on this platform')
}

type NextURI = string
function saveAttachmentDialog(filePath: string): Promise<NextURI> {
  throw new Error('Save Attachment - unsupported on this platform')
}

async function saveAttachmentToCameraRoll(filePath: string, mimeType: string): Promise<void> {
  throw new Error('Save Attachment to camera roll - unsupported on this platform')
}

const showMainWindow = () => {
  const mw = getMainWindow()
  mw && mw.show()
  showDockIcon()
}

function displayNewMessageNotification(
  text: string,
  convID: ?string,
  badgeCount: ?number,
  myMsgID: ?number,
  soundName: ?string
) {
  throw new Error('Display new message notification not available on this platform')
}

function clearAllNotifications() {
  throw new Error('Clear all notifications not available on this platform')
}

const getContentTypeFromURL = (
  url: string,
  cb: ({error?: any, statusCode?: number, contentType?: string}) => void
) => {
  const req = SafeElectron.getRemote().net.request({url, method: 'HEAD'})
  req.on('response', response => {
    let contentType = ''
    if (response.statusCode === 200) {
      const contentTypeHeader = response.headers['content-type']
      contentType = Array.isArray(contentTypeHeader) && contentTypeHeader.length ? contentTypeHeader[0] : ''
    }
    cb({statusCode: response.statusCode, contentType})
  })
  req.on('error', error => cb({error}))
  req.end()
}

const writeElectronSettingsOpenAtLogin = (_: any, action: ConfigGen.SetOpenAtLoginPayload) =>
  action.payload.writeFile &&
  SafeElectron.getIpcRenderer().send('setAppState', {openAtLogin: action.payload.open})

const writeElectronSettingsNotifySound = (_: any, action: ConfigGen.SetNotifySoundPayload) =>
  action.payload.writeFile &&
  SafeElectron.getIpcRenderer().send('setAppState', {notifySound: action.payload.sound})

// get this value from electron and update our store version
function* initializeAppSettingsState(): Generator<any, void, any> {
  const getAppState = () =>
    new Promise((resolve, reject) => {
      SafeElectron.getIpcRenderer().once('getAppStateReply', (event, data) => resolve(data))
      SafeElectron.getIpcRenderer().send('getAppState')
    })

  const state = yield Saga.call(getAppState)
  if (state) {
    yield Saga.put(ConfigGen.createSetOpenAtLogin({open: state.openAtLogin, writeFile: false}))
    yield Saga.put(ConfigGen.createSetNotifySound({sound: state.notifySound, writeFile: false}))
  }
}

export const dumpLogs = (_: any, action: ?ConfigGen.DumpLogsPayload) =>
  logger
    .dump()
    .then(fromRender => {
      // $ForceType
      const globalLogger: typeof logger = SafeElectron.getRemote().getGlobal('globalLogger')
      return globalLogger.dump().then(fromMain => writeLogLinesToFile([...fromRender, ...fromMain]))
    })
    .then(() => {
      // quit as soon as possible
      if (action && action.payload.reason === 'quitting through menu') {
        quit('quitButton')
      }
    })

const checkRPCOwnership = () =>
  Saga.call(function*() {
    const waitKey = 'pipeCheckFail'
    yield Saga.put(ConfigGen.createDaemonHandshakeWait({increment: true, name: waitKey}))
    try {
      logger.info('Checking RPC ownership')

      const localAppData = String(process.env.LOCALAPPDATA)
      var binPath = localAppData ? path.resolve(localAppData, 'Keybase', 'keybase.exe') : 'keybase.exe'
      const args = ['pipeowner', socketPath]
      yield Saga.call(
        () =>
          new Promise((resolve, reject) => {
            execFile(binPath, args, {windowsHide: true}, (error, stdout, stderr) => {
              if (error) {
                logger.info(`pipeowner check result: ${stdout.toString()}`)
                // error will be logged in bootstrap check
                getEngine().reset()
                reject(error)
                return
              }
              const result = JSON.parse(stdout.toString())
              if (result.isOwner) {
                resolve()
                return
              }
              logger.info(`pipeowner check result: ${stdout.toString()}`)
              reject(new Error('pipeowner check failed'))
            })
          })
      )
    } catch (e) {
      yield Saga.put(
        ConfigGen.createDaemonHandshakeWait({
          failedFatal: true,
          failedReason: e.message,
          increment: false,
          name: waitKey,
        })
      )
    }
  })

const setupReachabilityWatcher = () =>
  Saga.call(function*() {
    const channel = Saga.eventChannel(emitter => {
      window.addEventListener('online', () => emitter('online'))
      window.addEventListener('offline', () => emitter('offline'))
      return () => {}
    }, Saga.buffers.dropping(1))
    while (true) {
      yield Saga.take(channel)
      yield Saga.put(GregorGen.createCheckReachability())
    }
  })

const setupEngineListeners = () => {
  getEngine().setIncomingActionCreators('keybase.1.NotifyApp.exit', () => {
    console.log('App exit requested')
    SafeElectron.getApp().exit(0)
  })

  getEngine().setIncomingActionCreators(
    'keybase.1.NotifyFS.FSActivity',
    ({notification}, _, __, getState) => {
      kbfsNotification(notification, NotifyPopup, getState)
    }
  )

  getEngine().setIncomingActionCreators('keybase.1.NotifyPGP.pgpKeyInSecretStoreFile', () => {
    RPCTypes.pgpPgpStorageDismissRpcPromise().catch(err => {
      console.warn('Error in sending pgpPgpStorageDismissRpc:', err)
    })
  })

  getEngine().setIncomingActionCreators('keybase.1.NotifyService.shutdown', ({code}, response) => {
    response && response.result()
    if (isWindows && code !== RPCTypes.ctlExitCode.restart) {
      console.log('Quitting due to service shutdown')
      // Quit just the app, not the service
      SafeElectron.getApp().quit()
    }
  })

  getEngine().setIncomingActionCreators(
    'keybase.1.NotifySession.clientOutOfDate',
    ({upgradeTo, upgradeURI, upgradeMsg}) => {
      const body = upgradeMsg || `Please update to ${upgradeTo} by going to ${upgradeURI}`
      NotifyPopup('Client out of date!', {body}, 60 * 60)
    }
  )

  getEngine().setIncomingActionCreators('keybase.1.logsend.prepareLogsend', (_, response) => {
    dumpLogs().then(() => {
      response && response.result()
    })
  })
}

function* loadStartupDetails() {
  yield Saga.put(
    ConfigGen.createSetStartupDetails({
      startupConversation: null,
      startupFollowUser: '',
      startupLink: '',
      startupTab: null,
      startupWasFromPush: false,
    })
  )
}

const copyToClipboard = (_: any, action: ConfigGen.CopyToClipboardPayload) => {
  SafeElectron.getClipboard().writeText(action.payload.text)
}

function* platformConfigSaga(): Saga.SagaGenerator<any, any> {
  yield Saga.actionToAction(ConfigGen.setOpenAtLogin, writeElectronSettingsOpenAtLogin)
  yield Saga.actionToAction(ConfigGen.setNotifySound, writeElectronSettingsNotifySound)
  yield Saga.actionToAction(ConfigGen.showMain, showMainWindow)
  yield Saga.actionToAction(ConfigGen.dumpLogs, dumpLogs)
  yield Saga.actionToAction(ConfigGen.setupEngineListeners, setupReachabilityWatcher)
  yield Saga.actionToAction(ConfigGen.setupEngineListeners, setupEngineListeners)
  yield Saga.actionToAction(ConfigGen.copyToClipboard, copyToClipboard)
  yield Saga.fork(initializeAppSettingsState)

  if (isWindows) {
    yield Saga.actionToAction(ConfigGen.daemonHandshake, checkRPCOwnership)
  }

  // Start this immediately
  yield Saga.fork(loadStartupDetails)
}

export {
  saveAttachmentDialog,
  saveAttachmentToCameraRoll,
  showShareActionSheet,
  downloadAndShowShareActionSheet,
  displayNewMessageNotification,
  clearAllNotifications,
  getContentTypeFromURL,
  platformConfigSaga,
}
<commit_msg>undo wait on success for windows pipe fail (#13485)<commit_after>// @flow
import * as ConfigGen from '../config-gen'
import * as GregorGen from '../gregor-gen'
import * as RPCTypes from '../../constants/types/rpc-gen'
import * as SafeElectron from '../../util/safe-electron.desktop'
import * as Saga from '../../util/saga'
import logger from '../../logger'
import path from 'path'
import {NotifyPopup} from '../../native/notifications'
import {execFile} from 'child_process'
import {getEngine} from '../../engine'
import {getMainWindow} from '../../desktop/remote/util.desktop'
import {isWindows, socketPath} from '../../constants/platform.desktop'
import {kbfsNotification} from '../../util/kbfs-notifications'
import {quit} from '../../util/quit-helper'
import {showDockIcon} from '../../desktop/app/dock-icon.desktop'
import {writeLogLinesToFile} from '../../util/forward-logs'

function showShareActionSheet(options: {
  url?: ?any,
  message?: ?any,
}): Promise<{completed: boolean, method: string}> {
  throw new Error('Show Share Action - unsupported on this platform')
}

function downloadAndShowShareActionSheet(fileURL: string) {
  throw new Error('Download and show share action - unsupported on this platform')
}

type NextURI = string
function saveAttachmentDialog(filePath: string): Promise<NextURI> {
  throw new Error('Save Attachment - unsupported on this platform')
}

async function saveAttachmentToCameraRoll(filePath: string, mimeType: string): Promise<void> {
  throw new Error('Save Attachment to camera roll - unsupported on this platform')
}

const showMainWindow = () => {
  const mw = getMainWindow()
  mw && mw.show()
  showDockIcon()
}

function displayNewMessageNotification(
  text: string,
  convID: ?string,
  badgeCount: ?number,
  myMsgID: ?number,
  soundName: ?string
) {
  throw new Error('Display new message notification not available on this platform')
}

function clearAllNotifications() {
  throw new Error('Clear all notifications not available on this platform')
}

const getContentTypeFromURL = (
  url: string,
  cb: ({error?: any, statusCode?: number, contentType?: string}) => void
) => {
  const req = SafeElectron.getRemote().net.request({url, method: 'HEAD'})
  req.on('response', response => {
    let contentType = ''
    if (response.statusCode === 200) {
      const contentTypeHeader = response.headers['content-type']
      contentType = Array.isArray(contentTypeHeader) && contentTypeHeader.length ? contentTypeHeader[0] : ''
    }
    cb({statusCode: response.statusCode, contentType})
  })
  req.on('error', error => cb({error}))
  req.end()
}

const writeElectronSettingsOpenAtLogin = (_: any, action: ConfigGen.SetOpenAtLoginPayload) =>
  action.payload.writeFile &&
  SafeElectron.getIpcRenderer().send('setAppState', {openAtLogin: action.payload.open})

const writeElectronSettingsNotifySound = (_: any, action: ConfigGen.SetNotifySoundPayload) =>
  action.payload.writeFile &&
  SafeElectron.getIpcRenderer().send('setAppState', {notifySound: action.payload.sound})

// get this value from electron and update our store version
function* initializeAppSettingsState(): Generator<any, void, any> {
  const getAppState = () =>
    new Promise((resolve, reject) => {
      SafeElectron.getIpcRenderer().once('getAppStateReply', (event, data) => resolve(data))
      SafeElectron.getIpcRenderer().send('getAppState')
    })

  const state = yield Saga.call(getAppState)
  if (state) {
    yield Saga.put(ConfigGen.createSetOpenAtLogin({open: state.openAtLogin, writeFile: false}))
    yield Saga.put(ConfigGen.createSetNotifySound({sound: state.notifySound, writeFile: false}))
  }
}

export const dumpLogs = (_: any, action: ?ConfigGen.DumpLogsPayload) =>
  logger
    .dump()
    .then(fromRender => {
      // $ForceType
      const globalLogger: typeof logger = SafeElectron.getRemote().getGlobal('globalLogger')
      return globalLogger.dump().then(fromMain => writeLogLinesToFile([...fromRender, ...fromMain]))
    })
    .then(() => {
      // quit as soon as possible
      if (action && action.payload.reason === 'quitting through menu') {
        quit('quitButton')
      }
    })

const checkRPCOwnership = () =>
  Saga.call(function*() {
    const waitKey = 'pipeCheckFail'
    yield Saga.put(ConfigGen.createDaemonHandshakeWait({increment: true, name: waitKey}))
    try {
      logger.info('Checking RPC ownership')

      const localAppData = String(process.env.LOCALAPPDATA)
      var binPath = localAppData ? path.resolve(localAppData, 'Keybase', 'keybase.exe') : 'keybase.exe'
      const args = ['pipeowner', socketPath]
      yield Saga.call(
        () =>
          new Promise((resolve, reject) => {
            execFile(binPath, args, {windowsHide: true}, (error, stdout, stderr) => {
              if (error) {
                logger.info(`pipeowner check result: ${stdout.toString()}`)
                // error will be logged in bootstrap check
                getEngine().reset()
                reject(error)
                return
              }
              const result = JSON.parse(stdout.toString())
              if (result.isOwner) {
                resolve()
                return
              }
              logger.info(`pipeowner check result: ${stdout.toString()}`)
              reject(new Error('pipeowner check failed'))
            })
          })
      )
      yield Saga.put(
        ConfigGen.createDaemonHandshakeWait({
          increment: false,
          name: waitKey,
        })
      )
    } catch (e) {
      yield Saga.put(
        ConfigGen.createDaemonHandshakeWait({
          failedFatal: true,
          failedReason: e.message || 'windows pipe owner fail',
          increment: false,
          name: waitKey,
        })
      )
    }
  })

const setupReachabilityWatcher = () =>
  Saga.call(function*() {
    const channel = Saga.eventChannel(emitter => {
      window.addEventListener('online', () => emitter('online'))
      window.addEventListener('offline', () => emitter('offline'))
      return () => {}
    }, Saga.buffers.dropping(1))
    while (true) {
      yield Saga.take(channel)
      yield Saga.put(GregorGen.createCheckReachability())
    }
  })

const setupEngineListeners = () => {
  getEngine().setIncomingActionCreators('keybase.1.NotifyApp.exit', () => {
    console.log('App exit requested')
    SafeElectron.getApp().exit(0)
  })

  getEngine().setIncomingActionCreators(
    'keybase.1.NotifyFS.FSActivity',
    ({notification}, _, __, getState) => {
      kbfsNotification(notification, NotifyPopup, getState)
    }
  )

  getEngine().setIncomingActionCreators('keybase.1.NotifyPGP.pgpKeyInSecretStoreFile', () => {
    RPCTypes.pgpPgpStorageDismissRpcPromise().catch(err => {
      console.warn('Error in sending pgpPgpStorageDismissRpc:', err)
    })
  })

  getEngine().setIncomingActionCreators('keybase.1.NotifyService.shutdown', ({code}, response) => {
    response && response.result()
    if (isWindows && code !== RPCTypes.ctlExitCode.restart) {
      console.log('Quitting due to service shutdown')
      // Quit just the app, not the service
      SafeElectron.getApp().quit()
    }
  })

  getEngine().setIncomingActionCreators(
    'keybase.1.NotifySession.clientOutOfDate',
    ({upgradeTo, upgradeURI, upgradeMsg}) => {
      const body = upgradeMsg || `Please update to ${upgradeTo} by going to ${upgradeURI}`
      NotifyPopup('Client out of date!', {body}, 60 * 60)
    }
  )

  getEngine().setIncomingActionCreators('keybase.1.logsend.prepareLogsend', (_, response) => {
    dumpLogs().then(() => {
      response && response.result()
    })
  })
}

function* loadStartupDetails() {
  yield Saga.put(
    ConfigGen.createSetStartupDetails({
      startupConversation: null,
      startupFollowUser: '',
      startupLink: '',
      startupTab: null,
      startupWasFromPush: false,
    })
  )
}

const copyToClipboard = (_: any, action: ConfigGen.CopyToClipboardPayload) => {
  SafeElectron.getClipboard().writeText(action.payload.text)
}

function* platformConfigSaga(): Saga.SagaGenerator<any, any> {
  yield Saga.actionToAction(ConfigGen.setOpenAtLogin, writeElectronSettingsOpenAtLogin)
  yield Saga.actionToAction(ConfigGen.setNotifySound, writeElectronSettingsNotifySound)
  yield Saga.actionToAction(ConfigGen.showMain, showMainWindow)
  yield Saga.actionToAction(ConfigGen.dumpLogs, dumpLogs)
  yield Saga.actionToAction(ConfigGen.setupEngineListeners, setupReachabilityWatcher)
  yield Saga.actionToAction(ConfigGen.setupEngineListeners, setupEngineListeners)
  yield Saga.actionToAction(ConfigGen.copyToClipboard, copyToClipboard)
  yield Saga.fork(initializeAppSettingsState)

  if (isWindows) {
    yield Saga.actionToAction(ConfigGen.daemonHandshake, checkRPCOwnership)
  }

  // Start this immediately
  yield Saga.fork(loadStartupDetails)
}

export {
  saveAttachmentDialog,
  saveAttachmentToCameraRoll,
  showShareActionSheet,
  downloadAndShowShareActionSheet,
  displayNewMessageNotification,
  clearAllNotifications,
  getContentTypeFromURL,
  platformConfigSaga,
}
<|endoftext|>


<commit_before>import Plugin from "../Plugin";
import request from "request";

export default class Reddit extends Plugin {
    static get plugin() {
        return {
            name: "Reddit",
            description: "Get a random post from the Reddit frontpage or a subreddit",
            help: "`/reddit` gets a random post; `/reddit sub` gets a random post from the subreddit."
        };
    }

    onCommand({message, command, args}, reply) {
        if (command !== "reddit") return;
        const sub = args[0];
        const url = "https://reddit.com" + (sub ? `/r/${sub}` : "") + "/.json";
        request(url, (error, response, body) => {
            if (error || response.statusCode !== 200)
                return reply({
                    type: "text",
                    text: "An error occurred."
                });
            try {
                const data = JSON.parse(body);
                const results = data.data.children;
                const item = results[Math.floor(Math.random() * results.length)].data;
                reply({
                    type: "text",
                    text: `[${item.title}](https://reddit.com${item.permalink}) - r/${item.subreddit}`,
                    options: {
                        parse_mode: "Markdown"
                    }
                });
            } catch (e) {
                return reply({
                    type: "text",
                    text: "An error occurred."
                });
            }
        });
    }
}
<commit_msg>Add redimage command to Reddit Plugin<commit_after>import Plugin from "../Plugin";
import request from "request";

export default class Reddit extends Plugin {
    static get plugin() {
        return {
            name: "Reddit",
            description: "Get a random post from the Reddit frontpage or a subreddit",
            help: `\`/reddit\` gets a random post; \`/reddit sub\` gets a random post from the subreddit.
            \`/redimg _subreddit_\` : gets a random image from _subreddit_
            `
        };
    }

    onCommand({message, command, args}, reply) {
        if (command !== "reddit" && command !== "redimg") {
            return;
        }

        const sub = args[0];
        const url = "https://reddit.com" + (sub ? `/r/${sub}` : "") + "/.json";
        request(url, (error, response, body) => {
            if (error || response.statusCode !== 200) {
                return reply({
                    type: "text",
                    text: "An error occurred."
                });
            }
            try {
                switch (command) {
                case "reddit":
                    this.reddit(body, reply);
                    break;
                case "redimg":
                    this.redimg(body, reply);
                    break;
                default:
                    return;
                }
            } catch (e) {
                return reply({
                    type: "text",
                    text: "An error occurred."
                });
            }
        });
    }

    reddit(body, reply) {
        const data = JSON.parse(body);
        const results = data.data.children;
        const item = results[Math.floor(Math.random() * results.length)].data;
        reply({
            type: "text",
            text: `[${item.title}](https://reddit.com${item.permalink}) - r/${item.subreddit}`,
            options: {
                parse_mode: "Markdown"
            }
        });
    }

    redimg(body, reply) {
        const data = JSON.parse(body);
        const results = data.data.children
            .filter(c => c.data.post_hint === "image")
            .map(r => r.data.url);
        const item = results[Math.floor(Math.random() * results.length)];

        reply({
            type: "text",
            text: item
        });
    }}

<|endoftext|>