<commit_before>module.exports = function(grunt) {
    grunt.loadNpmTasks('grunt-contrib-symlink');
    grunt.loadNpmTasks('grunt-contrib-less');
    grunt.loadNpmTasks('grunt-contrib-cssmin');
    grunt.loadNpmTasks('grunt-contrib-coffee');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-commands');

    var filesLess = {},
        filesCoffee = {};

    // Configuration
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),

        symlink: {
            // BOWER COMPONENT
            fontawesome_font_otf: {
                src: 'bower_components/font-awesome/fonts/FontAwesome.otf',
                dest: 'web/fonts/FontAwesome.otf'
            },
            fontawesome_font_eot: {
                src: 'bower_components/font-awesome/fonts/fontawesome-webfont.eot',
                dest: 'web/fonts/fontawesome-webfont.eot'
            },
            fontawesome_font_svg: {
                src: 'bower_components/font-awesome/fonts/fontawesome-webfont.svg',
                dest: 'web/fonts/fontawesome-webfont.svg'
            },
            fontawesome_font_ttf: {
                src: 'bower_components/font-awesome/fonts/fontawesome-webfont.ttf',
                dest: 'web/fonts/fontawesome-webfont.ttf'
            },
            fontawesome_font_woff: {
                src: 'bower_components/font-awesome/fonts/fontawesome-webfont.woff',
                dest: 'web/fonts/fontawesome-webfont.woff'
            },
            bootstrap_font_eot: {
                src: 'bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.eot',
                dest: 'web/fonts/glyphicons-halflings-regular.eot'
            },
            bootstrap_font_svg: {
                src: 'bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.svg',
                dest: 'web/fonts/glyphicons-halflings-regular.svg'
            },
            bootstrap_font_ttf: {
                src: 'bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf',
                dest: 'web/fonts/glyphicons-halflings-regular.ttf'
            },
            bootstrap_font_woff: {
                src: 'bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.woff',
                dest: 'web/fonts/glyphicons-halflings-regular.woff'
            },
            datatable_img_sort_asc: {
                src: 'bower_components/datatables/media/images/sort_asc.png',
                dest: 'web/images/sort_asc.png'
            },
            datatable_img_sort_both: {
                src: 'bower_components/datatables/media/images/sort_both.png',
                dest: 'web/images/sort_both.png'
            },
            datatable_img_sort_desc: {
                src: 'bower_components/datatables/media/images/sort_desc.png',
                dest: 'web/images/sort_desc.png'
            },
            pace_js: {
                src: 'bower_components/pace/pace.min.js',
                dest: 'web/js/pace.min.js'
            },

            // SMARTADMIN ASSETS
            smartadmin_bg: {
                src: 'web/bundles/phporchestrabackoffice/smartadmin/img/mybg.png',
                dest: 'web/img/mybg.png'
            },
            smartadmin_flags: {
                src: 'web/bundles/phporchestrabackoffice/smartadmin/img/flags/flags.png',
                dest: 'web/img/flags/flags.png'
            }
        },

        clean: {
            built: ["web/built"]
        },

        less: {
            bundles: {
                files: filesLess
            }
        },

        cssmin: {
            minify: {
                expand: true,
                src: ['web/css/all.css'],
                ext: '.min.css'
            }
        },

        coffee: {
            compileBare: {
                options: {
                    bare: true
                },
                files: filesCoffee
            }
        },

        uglify: {
            dist: {
                files: {
                    'web/js/all.min.js': ['web/js/all.js']
                }
            }
        },

        concat: {
            smartadminjs: {
                src: [
                    'bower_components/jquery/jquery.js',
                    'bower_components/jquery-ui/ui/jquery-ui.js',
                    'web/bundles/phporchestrabackoffice/smartadmin/js/app.config.js',
                    'bower_components/bootstrap/dist/js/bootstrap.js',
                    'web/bundles/phporchestrabackoffice/smartadmin/js/notification/SmartNotification.min.js',
                    'web/bundles/phporchestrabackoffice/smartadmin/js/smartwidgets/jarvis.widget.min.js',
                    'web/bundles/phporchestrabackoffice/smartadmin/js/app.min.js',
                ],
                dest: 'web/built/smartadmin.js'
            },

            libjs: {
                src: [
                    'bower_components/underscore/underscore.js',
                    'bower_components/backbone/backbone.js',
                    'bower_components/angular/angular.js',
                    'bower_components/datatables/media/js/jquery.dataTables.js',
                    'bower_components/jquery-form/jquery.form.js',
                    'web/bundles/phporchestrabackoffice/js/latinise.js',
                    'web/bundles/phporchestrabackoffice/js/jQuery.DOMNodeAppear.js',
                ],
                dest: 'web/built/lib.js'
            },

            orchestrajs: {
                src: [
                     'web/built/phporchestrabackoffice/js/orchestraLib.js',
                     'web/built/phporchestrabackoffice/js/orchestraListeners.js',

                     // MISC
                     'web/built/phporchestrabackoffice/js/include.js',
                     'web/built/phporchestrabackoffice/js/addPrototype.js',
                     'web/built/phporchestrabackoffice/js/modelBackbone.js',
                     'web/built/phporchestrabackoffice/js/adminFormView.js',
                     'web/built/phporchestrabackoffice/js/generateAlias.js',
                     'web/built/phporchestrabackoffice/js/page/makeSortable.js',
                     'web/built/phporchestrabackoffice/js/page/areaView.js',
                     'web/built/phporchestrabackoffice/js/page/blockView.js',
                     'web/built/phporchestrabackoffice/js/page/nodeView.js',
                     'web/built/phporchestrabackoffice/js/page/NodeVersionView.js',
                     'web/built/phporchestrabackoffice/js/page/NodeLanguageView.js',
                     'web/built/phporchestrabackoffice/js/page/previewLinkView.js',
                     'web/built/phporchestrabackoffice/js/table/TableviewView.js',
                     'web/built/phporchestrabackoffice/js/table/TableviewCollectionView.js',
                     'web/built/phporchestrabackoffice/js/FullPageFormView.js',
                     'web/built/phporchestrabackoffice/js/page/templateView.js',
                     'web/built/phporchestrabackoffice/js/page/showNode.js',
                     'web/built/phporchestrabackoffice/js/page/showTemplate.js',
                     'web/built/phporchestrabackoffice/js/table/tableviewLoader.js',
                     'web/built/phporchestrabackoffice/js/page/nodeConstructor.js',
                     'web/built/phporchestrabackoffice/js/treeAjaxDelete.js',
                     'web/built/phporchestrabackoffice/js/configurableContentFormListener.js',
                     'web/built/phporchestrabackoffice/js/page/blocksPanel.js',
                     'web/built/phporchestrabackoffice/js/security.js',

                     // MEDIATHEQUE
                     'web/built/phporchestrabackoffice/js/mediatheque/orchestraMediaType.js',
                     'web/built/phporchestrabackoffice/js/mediatheque/mediaModalListeners.js',
                     'web/built/phporchestrabackoffice/js/mediatheque/mediaModalView.js',
                     'web/built/phporchestrabackoffice/js/mediatheque/mediaFormView.js',
                     'web/built/phporchestrabackoffice/js/mediatheque/GalleryCollectionView.js',
                     'web/built/phporchestrabackoffice/js/mediatheque/GalleryLoad.js',
                     'web/built/phporchestrabackoffice/js/mediatheque/GalleryView.js',

                     // INDEXATION
                     'web/bundles/phporchestraindexation/js/*.js',

                     // LEXIKTRANSLATION
                     'web/bundles/lexiktranslation/ng-table/ng-table.min.js',
                     'web/built/phporchestratranslation/js/translationView.js',

                     // BACKBONE ROUTER
                     'web/bundles/phporchestrabackoffice/js/backboneRouter.js',
                ],
                dest: 'web/built/orchestra.js'
            },

            js: {
                src: [
                    'web/built/smartadmin.js',
                    'web/built/lib.js',
                    'web/built/orchestra.js'
                ],
                dest: 'web/js/all.js'
            },

            smartadmincss: {
                src: [
                    // SMARTADMIN PACKAGE
//                    'bower_components/bootstrap/dist/css/bootstrap.min.css',
                    'web/bundles/phporchestrabackoffice/smartadmin/css/bootstrap.min.css',
                    'bower_components/font-awesome/css/font-awesome.min.css',
                    'web/bundles/phporchestrabackoffice/smartadmin/css/smartadmin-production.min.css',
                    'web/bundles/phporchestrabackoffice/smartadmin/css/smartadmin-skins.min.css',

                    // ORCHESTRA PATCHES
                    'web/built/phporchestrabackoffice/css/smartadminPatches/flags.css',
                    'web/built/phporchestrabackoffice/css/smartadminPatches/title.css',
                    'web/built/phporchestrabackoffice/css/smartadminPatches/modal.css',
                    'web/built/phporchestrabackoffice/css/smartadminPatches/widget.css'
                ],
                dest: 'web/built/smartadmin.css'
            },

            libcss: {
                src: [
                      'bower_components/jquery-ui/themes/base/jquery-ui.css',
                      'bower_components/datatables/media/css/jquery.dataTables.css'
                ],
                dest: 'web/built/lib.css'
            },

            orchestracss: {
                src: [
                      'web/built/phporchestrabackoffice/css/phporchestra.css',
                      'web/built/phporchestrabackoffice/css/mediatheque.css',
                      'web/built/phporchestracms/css/template.css',
                      'web/built/phporchestrabackoffice/css/blocksPanel.css',
                      'web/built/phporchestrabackoffice/css/mediaModal.css',
                      'web/bundles/lexiktranslation/ng-table/ng-table.min.css'
                ],
                dest: 'web/built/orchestra.css'
            },

            css: {
                src: [
                      'web/built/smartadmin.css',
                      'web/built/lib.css',
                      'web/built/orchestra.css'
                ],
                dest: 'web/css/all.css'
            }
        },

        watch: {
            css: {
                files: ['web/bundles/*/less/*.less'],
                tasks: ['css', 'command:assetic_dump']
            },
            javascript: {
                files: ['web/bundles/*/coffee/*.coffee'],
                tasks: ['javascript', 'command:assetic_dump']
            }
        },

        command : {
            assets_install: {
                cmd  : 'php app/console assets:install --symlink'
            },
            assetic_dump: {
                cmd  : 'php app/console assetic:dump'
            }
        }
    });

    // Default task(s).
    grunt.registerTask('default', ['clean', 'command:assets_install', 'symlink', 'css', 'javascript', 'javascriptProd', 'command:assetic_dump']);
    grunt.registerTask('css', ['less:discovering', 'less', 'concat:smartadmincss', 'concat:libcss', 'concat:orchestracss', 'concat:css', 'cssmin']);
    grunt.registerTask('javascript', ['coffee:discovering', 'coffee', 'concat:smartadminjs', 'concat:libjs', 'concat:orchestrajs', 'concat:js']);
    grunt.registerTask('javascriptProd', ['uglify']);
    grunt.registerTask('less:discovering', 'This is a function', function() {
        // LESS Files management
        // Source LESS files are located inside : bundles/[bundle]/less/
        // Destination CSS files are located inside : built/[bundle]/css/
        var mappingFileLess = grunt.file.expandMapping(
            ['*/less/*.less', '*/less/*/*.less'],
            'web/built/', {
                cwd: 'web/bundles/',
                rename: function(dest, matchedSrcPath, options) {
                    return dest + matchedSrcPath.replace(/less/g, 'css');
                }
            });

        grunt.util._.each(mappingFileLess, function(value) {
            // Why value.src is an array ??
            filesLess[value.dest] = value.src[0];
        });
    });
    grunt.registerTask('coffee:discovering', 'This is a function', function() {
        // COFFEE Files management
        // Source COFFEE files are located inside : bundles/[bundle]/coffee/
        // Destination JS files are located inside : built/[bundle]/js/
        var mappingFileCoffee = grunt.file.expandMapping(
            ['*/coffee/*.coffee', '*/coffee/*/*.coffee'],
            'web/built/', {
                cwd: 'web/bundles/',
                rename: function(dest, matchedSrcPath, options) {
                    return dest + matchedSrcPath.replace(/coffee/g, 'js');
                }
            });

        grunt.util._.each(mappingFileCoffee, function(value) {
            // Why value.src is an array ??
            filesCoffee[value.dest] = value.src[0];
        });
    });
};
<commit_msg>Fix tab and checkbox css bugs<commit_after>module.exports = function(grunt) {
    grunt.loadNpmTasks('grunt-contrib-symlink');
    grunt.loadNpmTasks('grunt-contrib-less');
    grunt.loadNpmTasks('grunt-contrib-cssmin');
    grunt.loadNpmTasks('grunt-contrib-coffee');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-commands');

    var filesLess = {},
        filesCoffee = {};

    // Configuration
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),

        symlink: {
            // BOWER COMPONENT
            fontawesome_font_otf: {
                src: 'bower_components/font-awesome/fonts/FontAwesome.otf',
                dest: 'web/fonts/FontAwesome.otf'
            },
            fontawesome_font_eot: {
                src: 'bower_components/font-awesome/fonts/fontawesome-webfont.eot',
                dest: 'web/fonts/fontawesome-webfont.eot'
            },
            fontawesome_font_svg: {
                src: 'bower_components/font-awesome/fonts/fontawesome-webfont.svg',
                dest: 'web/fonts/fontawesome-webfont.svg'
            },
            fontawesome_font_ttf: {
                src: 'bower_components/font-awesome/fonts/fontawesome-webfont.ttf',
                dest: 'web/fonts/fontawesome-webfont.ttf'
            },
            fontawesome_font_woff: {
                src: 'bower_components/font-awesome/fonts/fontawesome-webfont.woff',
                dest: 'web/fonts/fontawesome-webfont.woff'
            },
            bootstrap_font_eot: {
                src: 'bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.eot',
                dest: 'web/fonts/glyphicons-halflings-regular.eot'
            },
            bootstrap_font_svg: {
                src: 'bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.svg',
                dest: 'web/fonts/glyphicons-halflings-regular.svg'
            },
            bootstrap_font_ttf: {
                src: 'bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf',
                dest: 'web/fonts/glyphicons-halflings-regular.ttf'
            },
            bootstrap_font_woff: {
                src: 'bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.woff',
                dest: 'web/fonts/glyphicons-halflings-regular.woff'
            },
            datatable_img_sort_asc: {
                src: 'bower_components/datatables/media/images/sort_asc.png',
                dest: 'web/images/sort_asc.png'
            },
            datatable_img_sort_both: {
                src: 'bower_components/datatables/media/images/sort_both.png',
                dest: 'web/images/sort_both.png'
            },
            datatable_img_sort_desc: {
                src: 'bower_components/datatables/media/images/sort_desc.png',
                dest: 'web/images/sort_desc.png'
            },
            pace_js: {
                src: 'bower_components/pace/pace.min.js',
                dest: 'web/js/pace.min.js'
            },

            // SMARTADMIN ASSETS
            smartadmin_bg: {
                src: 'web/bundles/phporchestrabackoffice/smartadmin/img/mybg.png',
                dest: 'web/img/mybg.png'
            },
            smartadmin_flags: {
                src: 'web/bundles/phporchestrabackoffice/smartadmin/img/flags/flags.png',
                dest: 'web/img/flags/flags.png'
            }
        },

        clean: {
            built: ["web/built"]
        },

        less: {
            bundles: {
                files: filesLess
            }
        },

        cssmin: {
            minify: {
                expand: true,
                src: ['web/css/all.css'],
                ext: '.min.css'
            }
        },

        coffee: {
            compileBare: {
                options: {
                    bare: true
                },
                files: filesCoffee
            }
        },

        uglify: {
            dist: {
                files: {
                    'web/js/all.min.js': ['web/js/all.js']
                }
            }
        },

        concat: {
            smartadminjs: {
                src: [
                    'bower_components/jquery/jquery.js',
                    'bower_components/jquery-ui/ui/jquery-ui.js',
                    'web/bundles/phporchestrabackoffice/smartadmin/js/app.config.js',
                    'bower_components/bootstrap/dist/js/bootstrap.js',
                    'web/bundles/phporchestrabackoffice/smartadmin/js/notification/SmartNotification.min.js',
                    'web/bundles/phporchestrabackoffice/smartadmin/js/smartwidgets/jarvis.widget.min.js',
                    'web/bundles/phporchestrabackoffice/smartadmin/js/app.min.js',
                ],
                dest: 'web/built/smartadmin.js'
            },

            libjs: {
                src: [
                    'bower_components/underscore/underscore.js',
                    'bower_components/backbone/backbone.js',
                    'bower_components/angular/angular.js',
                    'bower_components/datatables/media/js/jquery.dataTables.js',
                    'bower_components/jquery-form/jquery.form.js',
                    'web/bundles/phporchestrabackoffice/js/latinise.js',
                    'web/bundles/phporchestrabackoffice/js/jQuery.DOMNodeAppear.js',
                ],
                dest: 'web/built/lib.js'
            },

            orchestrajs: {
                src: [
                     'web/built/phporchestrabackoffice/js/orchestraLib.js',
                     'web/built/phporchestrabackoffice/js/orchestraListeners.js',

                     // MISC
                     'web/built/phporchestrabackoffice/js/include.js',
                     'web/built/phporchestrabackoffice/js/addPrototype.js',
                     'web/built/phporchestrabackoffice/js/modelBackbone.js',
                     'web/built/phporchestrabackoffice/js/adminFormView.js',
                     'web/built/phporchestrabackoffice/js/generateAlias.js',
                     'web/built/phporchestrabackoffice/js/page/makeSortable.js',
                     'web/built/phporchestrabackoffice/js/page/areaView.js',
                     'web/built/phporchestrabackoffice/js/page/blockView.js',
                     'web/built/phporchestrabackoffice/js/page/nodeView.js',
                     'web/built/phporchestrabackoffice/js/page/NodeVersionView.js',
                     'web/built/phporchestrabackoffice/js/page/NodeLanguageView.js',
                     'web/built/phporchestrabackoffice/js/page/previewLinkView.js',
                     'web/built/phporchestrabackoffice/js/table/TableviewView.js',
                     'web/built/phporchestrabackoffice/js/table/TableviewCollectionView.js',
                     'web/built/phporchestrabackoffice/js/FullPageFormView.js',
                     'web/built/phporchestrabackoffice/js/page/templateView.js',
                     'web/built/phporchestrabackoffice/js/page/showNode.js',
                     'web/built/phporchestrabackoffice/js/page/showTemplate.js',
                     'web/built/phporchestrabackoffice/js/table/tableviewLoader.js',
                     'web/built/phporchestrabackoffice/js/page/nodeConstructor.js',
                     'web/built/phporchestrabackoffice/js/treeAjaxDelete.js',
                     'web/built/phporchestrabackoffice/js/configurableContentFormListener.js',
                     'web/built/phporchestrabackoffice/js/page/blocksPanel.js',
                     'web/built/phporchestrabackoffice/js/security.js',

                     // MEDIATHEQUE
                     'web/built/phporchestrabackoffice/js/mediatheque/orchestraMediaType.js',
                     'web/built/phporchestrabackoffice/js/mediatheque/mediaModalListeners.js',
                     'web/built/phporchestrabackoffice/js/mediatheque/mediaModalView.js',
                     'web/built/phporchestrabackoffice/js/mediatheque/mediaFormView.js',
                     'web/built/phporchestrabackoffice/js/mediatheque/GalleryCollectionView.js',
                     'web/built/phporchestrabackoffice/js/mediatheque/GalleryLoad.js',
                     'web/built/phporchestrabackoffice/js/mediatheque/GalleryView.js',

                     // INDEXATION
                     'web/bundles/phporchestraindexation/js/*.js',

                     // LEXIKTRANSLATION
                     'web/bundles/lexiktranslation/ng-table/ng-table.min.js',
                     'web/built/phporchestratranslation/js/translationView.js',

                     // BACKBONE ROUTER
                     'web/bundles/phporchestrabackoffice/js/backboneRouter.js',
                ],
                dest: 'web/built/orchestra.js'
            },

            js: {
                src: [
                    'web/built/smartadmin.js',
                    'web/built/lib.js',
                    'web/built/orchestra.js'
                ],
                dest: 'web/js/all.js'
            },

            smartadmincss: {
                src: [
                    // SMARTADMIN PACKAGE
//                    'bower_components/bootstrap/dist/css/bootstrap.min.css',
                    'web/bundles/phporchestrabackoffice/smartadmin/css/bootstrap.min.css',
                    'bower_components/font-awesome/css/font-awesome.min.css',
                    'web/bundles/phporchestrabackoffice/smartadmin/css/smartadmin-production.min.css',
                    'web/bundles/phporchestrabackoffice/smartadmin/css/smartadmin-skins.min.css',

                    // ORCHESTRA PATCHES
                    'web/built/phporchestrabackoffice/css/smartadminPatches/flags.css',
                    'web/built/phporchestrabackoffice/css/smartadminPatches/title.css',
                    'web/built/phporchestrabackoffice/css/smartadminPatches/modal.css',
                    'web/built/phporchestrabackoffice/css/smartadminPatches/widget.css',
                    'web/built/phporchestrabackoffice/css/smartadminPatches/checkbox.css',
                    'web/built/phporchestrabackoffice/css/smartadminPatches/tab.css'
                ],
                dest: 'web/built/smartadmin.css'
            },

            libcss: {
                src: [
                      'bower_components/jquery-ui/themes/base/jquery-ui.css',
                      'bower_components/datatables/media/css/jquery.dataTables.css'
                ],
                dest: 'web/built/lib.css'
            },

            orchestracss: {
                src: [
                      'web/built/phporchestrabackoffice/css/phporchestra.css',
                      'web/built/phporchestrabackoffice/css/mediatheque.css',
                      'web/built/phporchestracms/css/template.css',
                      'web/built/phporchestrabackoffice/css/blocksPanel.css',
                      'web/built/phporchestrabackoffice/css/mediaModal.css',
                      'web/bundles/lexiktranslation/ng-table/ng-table.min.css'
                ],
                dest: 'web/built/orchestra.css'
            },

            css: {
                src: [
                      'web/built/smartadmin.css',
                      'web/built/lib.css',
                      'web/built/orchestra.css'
                ],
                dest: 'web/css/all.css'
            }
        },

        watch: {
            css: {
                files: ['web/bundles/*/less/*.less'],
                tasks: ['css', 'command:assetic_dump']
            },
            javascript: {
                files: ['web/bundles/*/coffee/*.coffee'],
                tasks: ['javascript', 'command:assetic_dump']
            }
        },

        command : {
            assets_install: {
                cmd  : 'php app/console assets:install --symlink'
            },
            assetic_dump: {
                cmd  : 'php app/console assetic:dump'
            }
        }
    });

    // Default task(s).
    grunt.registerTask('default', ['clean', 'command:assets_install', 'symlink', 'css', 'javascript', 'javascriptProd', 'command:assetic_dump']);
    grunt.registerTask('css', ['less:discovering', 'less', 'concat:smartadmincss', 'concat:libcss', 'concat:orchestracss', 'concat:css', 'cssmin']);
    grunt.registerTask('javascript', ['coffee:discovering', 'coffee', 'concat:smartadminjs', 'concat:libjs', 'concat:orchestrajs', 'concat:js']);
    grunt.registerTask('javascriptProd', ['uglify']);
    grunt.registerTask('less:discovering', 'This is a function', function() {
        // LESS Files management
        // Source LESS files are located inside : bundles/[bundle]/less/
        // Destination CSS files are located inside : built/[bundle]/css/
        var mappingFileLess = grunt.file.expandMapping(
            ['*/less/*.less', '*/less/*/*.less'],
            'web/built/', {
                cwd: 'web/bundles/',
                rename: function(dest, matchedSrcPath, options) {
                    return dest + matchedSrcPath.replace(/less/g, 'css');
                }
            });

        grunt.util._.each(mappingFileLess, function(value) {
            // Why value.src is an array ??
            filesLess[value.dest] = value.src[0];
        });
    });
    grunt.registerTask('coffee:discovering', 'This is a function', function() {
        // COFFEE Files management
        // Source COFFEE files are located inside : bundles/[bundle]/coffee/
        // Destination JS files are located inside : built/[bundle]/js/
        var mappingFileCoffee = grunt.file.expandMapping(
            ['*/coffee/*.coffee', '*/coffee/*/*.coffee'],
            'web/built/', {
                cwd: 'web/bundles/',
                rename: function(dest, matchedSrcPath, options) {
                    return dest + matchedSrcPath.replace(/coffee/g, 'js');
                }
            });

        grunt.util._.each(mappingFileCoffee, function(value) {
            // Why value.src is an array ??
            filesCoffee[value.dest] = value.src[0];
        });
    });
};
<|endoftext|>


<commit_before>// when document loads
$(document).ready(function () {

  // get User
  var thisUser = this.User
  $.get('/api/whoami', function (data) {
    thisUser = data;
    if (typeof(thisUser.favoriteCourses) != 'undefined')
    {
      getFavorites();
    }
  })

  var dispFavorites = function() {
    $('#favs').html('');

    // call api to get favorites and display
    $.get('/api/user/favorites', function(courses) {
      for (var courseIndex in courses) {
        var thisCourse = courses[courseIndex];

        // append favorite into favs pane
        $('#favs').append(newResultEntry(thisCourse));

        // attach object to DOM element
        $('#favs').children().last()[0].course = thisCourse;
      }
    });
    /*
    // display favorites
    var toalert = '';
    for (var courseIndex in thisUser.favoriteCourses) {
      var favCourse = thisUser.favoriteCourses[courseIndex];
      toalert.append(favCourse + ' ');
      alert(favCourse);
      // Bensu TODO: append result into #favs list
    }
    alert("Favorite courses are :" + toalert);
    */
  }

  // function for updating search results
  var getCourseData = function () {
    var query = {
      $text: {
        $search: $('#searchbox').val()
      },
      semester: $('#semester').val()
    }

    $.post('/api/courses',
    {
      query: JSON.stringify(query),
      sort: $('#sort').val()
    }, function (courses, status) {
      // Basic error handling
      if (status !== 'success') {
        window.alert('An error occured and your search could not be completed.')
      }

      // clear results
      $('#results').html('')
      $('#search-title').html('')

      $('#search-title').append(courses.length + ' Search Results')

      for (var courseIndex in courses) {
        var thisCourse = courses[courseIndex]

        // append result into results pane
        $('#results').append(newResultEntry(thisCourse))
        //$('#results')[0].appendChild(newResultEntry(thisCourse))

        // attach object to DOM element
        $('#results').children().last()[0].course = thisCourse
      }
    })
  }

  // function for displaying course details
  var dispCourseData = function() {
    $(".search-result").removeClass("active");
    $(this).addClass("active")

    $('#disp-title').html('')
    $('#disp-subtitle').html('')
    $('#disp-profs').html('')
    $('#disp-body').html('')
    $('#evals').html('')
    $('#comments').html('')

    var thisCourse = this.course

    // string for course listings
    var listings =  getListings(thisCourse)

    $('#disp-title').append(thisCourse.title)

    $('#disp-subtitle').append(listings + ' '
                            + (thisCourse.distribution == undefined ? '' : ' ' + thisCourse.distribution + '')
                            + (thisCourse.pdf["required"]  ? ' PDF ONLY'
                            : (thisCourse.pdf["permitted"] ? ' PDF'
                                                                   : ' NPDF'))
                            + (thisCourse.audit ? ' AUDIT' : '')
                            + ' Favorite'
                            + (thisCourse.website == undefined ? '' : ' '))

    $('#fav-button')[0].course = thisCourse;

    //$('#comments').append(thisCourse.evaluations.studentComments)

    // stuff for course evaluations
    var evals = ""
    for (var field in thisCourse.evaluations.scores) {
      var val = thisCourse.evaluations.scores[field]
      evals += '
' + field + '
'
             + '

'
             + val.toFixed(2) + '

' // as percentage of 5
    }
    if (evals == "") {
      $('#evals').append('No course evaluations available.')
    }
    else {
      $('#evals').append(evals)
    }

    // stuff for student comments
    var comments = ""
    for (var studentComment in thisCourse.evaluations.studentComments) {
        var val = thisCourse.evaluations.studentComments[studentComment]
        comments += '
' + val + '
'
    }
    if (comments == "") {
      $('#comments').append('No student comments available.')
    }
    else {
      $('#comments').append(comments)
    }

    var dispbody = ''
    dispbody += '

' +
                '
' + thisCourse.description + '
'
                + (thisCourse.prerequisites == undefined ? '' :
                '
Prerequisites

' + thisCourse.prerequisites + '
')
                + (thisCourse.equivalentcourses == undefined ? '' :
                '
Equivalent Courses

' + thisCourse.equivalentcourses + '
')
                + (thisCourse.otherinformation == undefined ? '' :
                '
Other Information

' + thisCourse.otherinformation + '
')
                + (thisCourse.otherrequirements == undefined ? '' :
                '
Equivalent Courses

' + thisCourse.otherrequirements + '
')
                '
Classes

' + thisCourse.classes[0] + '
'

    var classes = ''
    for (var field in thisCourse.classes) {
      var val = thisCourse.classes[field]
      classes += '
'
              + '' + val['section'] + '	'
              + ''
      for (var day in val.schedule.meetings[0].days) {
        //classes += thisCourse.classes.schedule.meetings[0].days[day]
        classes += val.schedule.meetings[0].days[day] + ' '
      }
      classes += '	'
      classes += (val.schedule.meetings[0] == undefined ? '' :
                '' + (val.schedule.meetings[0].start_time == undefined ? '' :
                  val.schedule.meetings[0].start_time) + ' - '
                + (val.schedule.meetings[0].end_time == undefined ? '' :
                  val.schedule.meetings[0].end_time) + '	'
                + '' + (val.schedule.meetings[0].building == undefined ? '' :
                  val.schedule.meetings[0].building.short_name) + ' '
                + (val.schedule.meetings[0].room == undefined ? '' :
                  val.schedule.meetings[0].room) + '	'
              )
      classes += '' + val['enrollment'] + ' / ' + val['capacity'] + '	'
                + '' + val['status'] + '	'
                + '
'
    }
    dispbody += (classes == ''? '' :
                '
' +
                'Section	Days	Time	Room	Enrolled	Status	' +
                '
Classes
' + classes + '
')

    $('#disp-body').append(dispbody)

    for (var instructor in thisCourse.instructors) {
      var name = thisCourse.instructors[instructor].name['first'] + ' '
               + thisCourse.instructors[instructor].name['last']
        if ($('#disp-profs').html() !== '') {
          $('#disp-profs').append(', ')
        }
        $('#disp-profs').append(name)
    }

    // favorite a course
    $("#fav-button").click(function() {
      // var thisCourseId = thisCourse._id;
      // if (thisUser.favoriteCourses == undefined) {
      //   thisUser.favoriteCourses = [];
      // }
      // if (!thisUser.favoriteCourses.includes(thisCourseId))
      // {
      //   thisUser.favoriteCourses.push(thisCourseId);
      // }
      // else {
      //   var i = thisUser.favoriteCourses.indexOf(thisCourseId);
      //   if(i != -1) {
      //     thisUser.favoriteCourses.splice(i, 1);
      //   }
      // }
      // getFavorites();

      var thisCourseId = this.course["_id"];

      // update database
      $.ajax({
        url: '/api/user/favorite',
        type: 'PUT',
        data: {'course': thisCourseId},
        success: function() {dispFavorites();}
      });
    })
    /*
    $.ajax({
      url: '/api/user/favorite/',
      type: 'PUT',
      data: {course: $('#results').children()[0].course},
      success: function (arg, status) {console.log(arg);}})*/
  }

  // Every time a key is pressed inside the #searchbox, call the getCourseData function
  $('#searchbox').keyup(getCourseData)
  $('#semester, #sort').change(getCourseData)

  // displays information in right pane on click of search result
  $('#results').on('click', 'a.search-result', dispCourseData)
  $('#favs').on('click', 'a.search-result', dispCourseData)

  /* SEB' EXAMPLE

  // $('#results div').click(function () {
  //   console.log($(this).text())
  // })

  $('#results').on('click', 'div.course', function () {
    // window.alert('Handler for .click() called.')
    // console.log(this.course)
    window.alert('You just clicked on the course ' + this.course.title + '!')
  })

  */

  // feedback form toggling
  var toggleFeedback = function() {
   $('#feedback-form').slideToggle()
   if ($('#feedback-toggle').hasClass("active")) {
     $('#feedback-toggle').removeClass("active")
   } else {
     $('#feedback-toggle').addClass("active")
   }
 }

 $('#feedback-toggle').click(toggleFeedback)

  // load the semesters for the dropdown
  $.get('/api/semesters', function (semesters) {
    for (var semesterIndex in semesters) {
      var thisSemester = semesters[semesterIndex]
      $('#semester').append('<option value="' + thisSemester.code + '">' + thisSemester.name + '</select>')
    }
  })

  for (var i = 1.0; i < 5.05; i += 0.1) {
    $('#disp-body').append(''
      + i.toFixed(2)
      + '')
  }
  $('#disp-body').append('N/A')

  dispFavorites();
})
<commit_msg>hide favorites list if it's empty<commit_after>// when document loads
$(document).ready(function () {

  // get User
  var thisUser = this.User
  $.get('/api/whoami', function (data) {
    thisUser = data;
    if (typeof(thisUser.favoriteCourses) != 'undefined')
    {
      getFavorites();
    }
  })

  var dispFavorites = function() {
    $('#favs').html('');

    // call api to get favorites and display
    $.get('/api/user/favorites', function(courses) {
      if (courses == undefined)
      {
        document.getElementById("favorite-courses").style.display = "none";
      }
      else {
        document.getElementById("favorite-courses").style.display = "inline";
      }
      for (var courseIndex in courses) {
        var thisCourse = courses[courseIndex];

        // append favorite into favs pane
        $('#favs').append(newResultEntry(thisCourse));

        // attach object to DOM element
        $('#favs').children().last()[0].course = thisCourse;
      }
    });
    /*
    // display favorites
    var toalert = '';
    for (var courseIndex in thisUser.favoriteCourses) {
      var favCourse = thisUser.favoriteCourses[courseIndex];
      toalert.append(favCourse + ' ');
      alert(favCourse);
      // Bensu TODO: append result into #favs list
    }
    alert("Favorite courses are :" + toalert);
    */
  }

  // function for updating search results
  var getCourseData = function () {
    var query = {
      $text: {
        $search: $('#searchbox').val()
      },
      semester: $('#semester').val()
    }

    $.post('/api/courses',
    {
      query: JSON.stringify(query),
      sort: $('#sort').val()
    }, function (courses, status) {
      // Basic error handling
      if (status !== 'success') {
        window.alert('An error occured and your search could not be completed.')
      }

      // clear results
      $('#results').html('')
      $('#search-title').html('')

      $('#search-title').append(courses.length + ' Search Results')

      for (var courseIndex in courses) {
        var thisCourse = courses[courseIndex]

        // append result into results pane
        $('#results').append(newResultEntry(thisCourse))
        //$('#results')[0].appendChild(newResultEntry(thisCourse))

        // attach object to DOM element
        $('#results').children().last()[0].course = thisCourse
      }
    })
  }

  // function for displaying course details
  var dispCourseData = function() {
    $(".search-result").removeClass("active");
    $(this).addClass("active")

    $('#disp-title').html('')
    $('#disp-subtitle').html('')
    $('#disp-profs').html('')
    $('#disp-body').html('')
    $('#evals').html('')
    $('#comments').html('')

    var thisCourse = this.course

    // string for course listings
    var listings =  getListings(thisCourse)

    $('#disp-title').append(thisCourse.title)

    $('#disp-subtitle').append(listings + ' '
                            + (thisCourse.distribution == undefined ? '' : ' ' + thisCourse.distribution + '')
                            + (thisCourse.pdf["required"]  ? ' PDF ONLY'
                            : (thisCourse.pdf["permitted"] ? ' PDF'
                                                                   : ' NPDF'))
                            + (thisCourse.audit ? ' AUDIT' : '')
                            + ' Favorite'
                            + (thisCourse.website == undefined ? '' : ' '))

    $('#fav-button')[0].course = thisCourse;

    //$('#comments').append(thisCourse.evaluations.studentComments)

    // stuff for course evaluations
    var evals = ""
    for (var field in thisCourse.evaluations.scores) {
      var val = thisCourse.evaluations.scores[field]
      evals += '
' + field + '
'
             + '

'
             + val.toFixed(2) + '

' // as percentage of 5
    }
    if (evals == "") {
      $('#evals').append('No course evaluations available.')
    }
    else {
      $('#evals').append(evals)
    }

    // stuff for student comments
    var comments = ""
    for (var studentComment in thisCourse.evaluations.studentComments) {
        var val = thisCourse.evaluations.studentComments[studentComment]
        comments += '
' + val + '
'
    }
    if (comments == "") {
      $('#comments').append('No student comments available.')
    }
    else {
      $('#comments').append(comments)
    }

    var dispbody = ''
    dispbody += '

' +
                '
' + thisCourse.description + '
'
                + (thisCourse.prerequisites == undefined ? '' :
                '
Prerequisites

' + thisCourse.prerequisites + '
')
                + (thisCourse.equivalentcourses == undefined ? '' :
                '
Equivalent Courses

' + thisCourse.equivalentcourses + '
')
                + (thisCourse.otherinformation == undefined ? '' :
                '
Other Information

' + thisCourse.otherinformation + '
')
                + (thisCourse.otherrequirements == undefined ? '' :
                '
Equivalent Courses

' + thisCourse.otherrequirements + '
')
                '
Classes

' + thisCourse.classes[0] + '
'

    var classes = ''
    for (var field in thisCourse.classes) {
      var val = thisCourse.classes[field]
      classes += '
'
              + '' + val['section'] + '	'
              + ''
      for (var day in val.schedule.meetings[0].days) {
        //classes += thisCourse.classes.schedule.meetings[0].days[day]
        classes += val.schedule.meetings[0].days[day] + ' '
      }
      classes += '	'
      classes += (val.schedule.meetings[0] == undefined ? '' :
                '' + (val.schedule.meetings[0].start_time == undefined ? '' :
                  val.schedule.meetings[0].start_time) + ' - '
                + (val.schedule.meetings[0].end_time == undefined ? '' :
                  val.schedule.meetings[0].end_time) + '	'
                + '' + (val.schedule.meetings[0].building == undefined ? '' :
                  val.schedule.meetings[0].building.short_name) + ' '
                + (val.schedule.meetings[0].room == undefined ? '' :
                  val.schedule.meetings[0].room) + '	'
              )
      classes += '' + val['enrollment'] + ' / ' + val['capacity'] + '	'
                + '' + val['status'] + '	'
                + '
'
    }
    dispbody += (classes == ''? '' :
                '
' +
                'Section	Days	Time	Room	Enrolled	Status	' +
                '
Classes
' + classes + '
')

    $('#disp-body').append(dispbody)

    for (var instructor in thisCourse.instructors) {
      var name = thisCourse.instructors[instructor].name['first'] + ' '
               + thisCourse.instructors[instructor].name['last']
        if ($('#disp-profs').html() !== '') {
          $('#disp-profs').append(', ')
        }
        $('#disp-profs').append(name)
    }

    // favorite a course
    $("#fav-button").click(function() {
      var thisCourseId = this.course["_id"];
        //thisUser.favoriteCourses.push(thisCourseId);
        // update database
        $.ajax({
          url: '/api/user/favorite',
          type: 'PUT',
          data: {'course': thisCourseId},
          success: function() {dispFavorites();}
        });
    })
    /*
    $.ajax({
      url: '/api/user/favorite/',
      type: 'PUT',
      data: {course: $('#results').children()[0].course},
      success: function (arg, status) {console.log(arg);}})*/
  }

  // Every time a key is pressed inside the #searchbox, call the getCourseData function
  $('#searchbox').keyup(getCourseData)
  $('#semester, #sort').change(getCourseData)

  // displays information in right pane on click of search result
  $('#results').on('click', 'a.search-result', dispCourseData)
  $('#favs').on('click', 'a.search-result', dispCourseData)

  /* SEB' EXAMPLE

  // $('#results div').click(function () {
  //   console.log($(this).text())
  // })

  $('#results').on('click', 'div.course', function () {
    // window.alert('Handler for .click() called.')
    // console.log(this.course)
    window.alert('You just clicked on the course ' + this.course.title + '!')
  })

  */

  // feedback form toggling
  var toggleFeedback = function() {
   $('#feedback-form').slideToggle()
   if ($('#feedback-toggle').hasClass("active")) {
     $('#feedback-toggle').removeClass("active")
   } else {
     $('#feedback-toggle').addClass("active")
   }
 }

 $('#feedback-toggle').click(toggleFeedback)

  // load the semesters for the dropdown
  $.get('/api/semesters', function (semesters) {
    for (var semesterIndex in semesters) {
      var thisSemester = semesters[semesterIndex]
      $('#semester').append('<option value="' + thisSemester.code + '">' + thisSemester.name + '</select>')
    }
  })

  for (var i = 1.0; i < 5.05; i += 0.1) {
    $('#disp-body').append(''
      + i.toFixed(2)
      + '')
  }
  $('#disp-body').append('N/A')

  dispFavorites();
})
<|endoftext|>


<commit_before>/*
 * (C) Copyright 2014 Kurento (http://kurento.org/)
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-2.1.html
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 */

var freeice  = require('freeice');
var inherits = require('inherits');

var EventEmitter = require('events').EventEmitter;

var recursive = require('merge').recursive

function noop(error)
{
  if(error) console.trace(error)
}

function trackStop(track)
{
  track.stop && track.stop()
}

function streamStop(stream)
{
  stream.getTracks().forEach(trackStop)
}

/**
 * @classdesc Wrapper object of an RTCPeerConnection. This object is aimed to
 *            simplify the development of WebRTC-based applications.
 *
 * @constructor module:kurentoUtils.WebRtcPeer
 *
 * @param mode -
 *            {String} Mode in which the PeerConnection will be configured.
 *            Valid values are: 'recv', 'send', and 'sendRecv'
 * @param localVideo -
 *            {Object} Video tag for the local stream
 * @param remoteVideo -
 *            {Object} Video tag for the remote stream
 * @param onsdpoffer -
 *            {Function} Callback executed when a SDP offer has been generated
 * @param onerror -
 *            {Function} Callback executed when an error happens generating an
 *            SDP offer
 * @param videoStream -
 *            {Object} MediaStream to be used as primary source (typically video
 *            and audio, or only video if combined with audioStream) for
 *            localVideo and to be added as stream to the RTCPeerConnection
 * @param audioStream -
 *            {Object} MediaStream to be used as second source (typically for
 *            audio) for localVideo and to be added as stream to the
 *            RTCPeerConnection
 */
function WebRtcPeer(mode, options, callback)
{
  WebRtcPeer.super_.call(this)

  var localVideo, remoteVideo, onsdpoffer, onerror, mediaConstraints;
  var videoStream, audioStream, connectionConstraints;

  while(arguments.length && !arguments[arguments.length-1]) arguments.length--;

  if(arguments.length > 2)  // Deprecated mode
  {
    console.warn('Positional parameters are deprecated for WebRtcPeer')

    localVideo       = arguments[1];
    remoteVideo      = arguments[2];
    onsdpoffer       = arguments[3];
    onerror          = arguments[4];
    mediaConstraints = arguments[5];
    videoStream      = arguments[6];
    audioStream      = arguments[7];
  }
  else
  {
    localVideo       = options.localVideo;
    remoteVideo      = options.remoteVideo;
    onsdpoffer       = options.onsdpoffer;
    onerror          = options.onerror;
    mediaConstraints = options.mediaConstraints;
    videoStream      = options.videoStream;
    audioStream      = options.audioStream;

    connectionConstraints = options.connectionConstraints;
  }

  if(onerror)    this.on('error',    onerror);
  if(onsdpoffer) this.on('sdpoffer', onsdpoffer);

  // Init PeerConnection

  var pc = options.peerConnection
  if(!pc)
  {
    var configuration = recursive(
    {
      iceServers : freeice()
    },
    WebRtcPeer.prototype.server,
    options.configuration);

    pc = new RTCPeerConnection(configuration);
  }

  Object.defineProperty(this, 'peerConnection', {get: function(){return pc;}});

  var self = this;

  function onSdpOffer_callback(error, sdpAnswer, callback)
  {
    if(error) return console.error(error)

    self.processSdpAnswer(sdpAnswer, callback)
  }

  var ended = false;
  pc.addEventListener('icecandidate', function(event)
  {
    // candidate exists in event.candidate
    if(event.candidate)
    {
      ended = false;
      return;
    }

    // [Hack] On old Chrome, when there's no Internet connection onicecandidate
    // event is being called infinitelly (bug). This prevent it.
    if(ended) return;
    ended = true;

    self.emit('sdpoffer', pc.localDescription.sdp, onSdpOffer_callback);
    console.log('ICE negotiation completed');
  });

  //
  // Priviledged methods
  //

  /**
  * @description This method creates the RTCPeerConnection object taking into
  *              account the properties received in the constructor. It starts
  *              the SDP negotiation process: generates the SDP offer and invokes
  *              the onsdpoffer callback. This callback is expected to send the
  *              SDP offer, in order to obtain an SDP answer from another peer.
  *
  * @function module:kurentoUtils.WebRtcPeer.prototype.start
  */
  this.start = function(constraints, callback)
  {
    if(videoStream && localVideo)
    {
      localVideo.src = URL.createObjectURL(videoStream);
      localVideo.muted = true;
    }

    if(videoStream) pc.addStream(videoStream);
    if(audioStream) pc.addStream(audioStream);

    // Adjust arguments

    if(constraints instanceof Function)
    {
      if(callback) throw new Error('Nothing can be defined after the callback')

      callback    = constraints
      constraints = undefined
    }

    // [Hack] https://code.google.com/p/chromium/issues/detail?id=443558
    if(mode == 'sendonly') mode = 'sendrecv';

    constraints = recursive(
    {
      mandatory:
      {
        OfferToReceiveAudio: (mode !== 'sendonly'),
        OfferToReceiveVideo: (mode !== 'sendonly')
      },
      optional:
      [
        {DtlsSrtpKeyAgreement: true}
      ]
    }, constraints);

    callback = callback || noop;

    // Create the offer with the required constrains

    pc.createOffer(function(offer)
    {
      console.log('Created SDP offer');

      pc.setLocalDescription(offer, function()
      {
        console.log('Local description set', offer);

        callback(null, self);
      },
      callback);
    },
    callback, constraints);
  }

  if(mode !== 'recvonly' && !videoStream)
  {
    mediaConstraints = recursive(
    {
      audio: true,
      video:
      {
        mandatory:
        {
          maxWidth: 640,
          maxFrameRate: 15,
          minFrameRate: 15
        }
      }
    }, mediaConstraints);

    getUserMedia(mediaConstraints, function(stream)
    {
      videoStream = stream;

      self.start(options.connectionConstraints, callback)
    },
    callback || noop);
  }
  else
    self.start(options.connectionConstraints, callback)

  this.on('_dispose', function()
  {
    if(localVideo)  localVideo.src  = '';
    if(remoteVideo) remoteVideo.src = '';
  })

  this.on('_processSdpAnswer', function(url)
  {
    if(remoteVideo)
    {
      remoteVideo.src = url;

      console.log('Remote URL:', url)
    }
  })
}
inherits(WebRtcPeer, EventEmitter)

WebRtcPeer.prototype.server = {}

WebRtcPeer.prototype.getLocalStream = function(index)
{
  if(this.peerConnection)
    return this.peerConnection.getLocalStreams()[index || 0]
}

WebRtcPeer.prototype.getRemoteStream = function(index)
{
  if(this.peerConnection)
    return this.peerConnection.getRemoteStreams()[index || 0]
}

/**
* @description This method frees the resources used by WebRtcPeer.
*
* @function module:kurentoUtils.WebRtcPeer.prototype.dispose
*/
WebRtcPeer.prototype.dispose = function()
{
  console.log('Disposing WebRtcPeer');

  var pc = this.peerConnection;
  if(pc)
  {
    // FIXME This is not yet implemented in firefox
    // if(videoStream) pc.removeStream(videoStream);
    // if(audioStream) pc.removeStream(audioStream);

    // For old browsers, PeerConnection.close() is NOT idempotent and raise
    // error. We check its signaling state and don't close it if it's already
    // closed
    if(pc.signalingState != 'closed') pc.close();

    pc.getLocalStreams().forEach(streamStop)
  }

  this.emit('_dispose');
};

/**
 * @description Callback function invoked when and SDP answer is received.
 *              Developers are expected to invoke this function in order to
 *              complete the SDP negotiation.
 *
 * @function module:kurentoUtils.WebRtcPeer.prototype.processSdpAnswer
 *
 * @param sdpAnswer -
 *            Description of sdpAnswer
 * @param callback -
 *            Invoked after the SDP answer is processed, or there is an error.
 */
WebRtcPeer.prototype.processSdpAnswer = function(sdpAnswer, callback)
{
  var answer = new RTCSessionDescription(
  {
    type : 'answer',
    sdp : sdpAnswer,
  });

  console.log('SDP answer received, setting remote description');

  callback = callback || noop

  var self = this;

  this.peerConnection.setRemoteDescription(answer, function()
  {
    var url = URL.createObjectURL(this.getRemoteStreams()[0]);

    self.emit('_processSdpAnswer', url);

    callback();
  },
  callback);
}

//
// Static factory functions
//

/**
 * @description This method creates the WebRtcPeer object and obtain userMedia
 *              if needed.
 *
 * @function module:kurentoUtils.WebRtcPeer.start
 *
 * @param mode -
 *            {String} Mode in which the PeerConnection will be configured.
 *            Valid values are: 'recv', 'send', and 'sendRecv'
 * @param localVideo -
 *            {Object} Video tag for the local stream
 * @param remoteVideo -
 *            {Object} Video tag for the remote stream
 * @param onSdp -
 *            {Function} Callback executed when a SDP offer has been generated
 * @param onerror -
 *            {Function} Callback executed when an error happens generating an
 *            SDP offer
 * @param mediaConstraints -
 *            {Object[]} Constraints used to create RTCPeerConnection
 * @param videoStream -
 *            {Object} MediaStream to be used as primary source (typically video
 *            and audio, or only video if combined with audioStream) for
 *            localVideo and to be added as stream to the RTCPeerConnection
 * @param videoStream -
 *            {Object} MediaStream to be used as primary source (typically video
 *            and audio, or only video if combined with audioStream) for
 *            localVideo and to be added as stream to the RTCPeerConnection
 * @param audioStream -
 *            {Object} MediaStream to be used as second source (typically for
 *            audio) for localVideo and to be added as stream to the
 *            RTCPeerConnection
 *
 * @return {module:kurentoUtils.WebRtcPeer}
 */
WebRtcPeer.start = function(mode, localVideo, remoteVideo, onsdpoffer, onerror,
    mediaConstraints, videoStream, audioStream, configuration,
    connectionConstraints, callback)
{
  var options =
  {
    localVideo      : localVideo,
    remoteVideo     : remoteVideo,
    onsdpoffer      : onsdpoffer,
    onerror         : onerror,
    mediaConstraints: mediaConstraints,
    videoStream     : videoStream,
    audioStream     : audioStream,
    configuration   : configuration,

    connectionConstraints: connectionConstraints
  };

  return new WebRtcPeer(mode, options, callback);
};

/**
 * @description This methods creates a WebRtcPeer to receive video.
 *
 * @function module:kurentoUtils.WebRtcPeer.startRecvOnly
 *
 * @param remoteVideo -
 *            {Object} Video tag for the remote stream
 * @param onSdp -
 *            {Function} Callback executed when a SDP offer has been generated
 * @param onerror -
 *            {Function} Callback executed when an error happens generating an
 *            SDP offer
 * @param mediaConstraints -
 *            {Object[]} Constraints used to create RTCPeerConnection
 *
 * @return {module:kurentoUtils.WebRtcPeer}
 */
WebRtcPeer.startRecvOnly = function(remoteVideo, onSdp, onError,
  mediaConstraints, configuration, connectionConstraints, callback)
{
  return WebRtcPeer.start('recvonly', null, remoteVideo, onSdp, onError,
      mediaConstraints, null, null, configuration, connectionConstraints,
      callback);
};

/**
 * @description This methods creates a WebRtcPeer to send video.
 *
 * @function module:kurentoUtils.WebRtcPeer.startSendOnly
 *
 * @param localVideo -
 *            {Object} Video tag for the local stream
 * @param onSdp -
 *            {Function} Callback executed when a SDP offer has been generated
 * @param onerror -
 *            {Function} Callback executed when an error happens generating an
 *            SDP offer
 * @param mediaConstraints -
 *            {Object[]} Constraints used to create RTCPeerConnection
 *
 * @return {module:kurentoUtils.WebRtcPeer}
 */
WebRtcPeer.startSendOnly = function(localVideo, onSdp, onError,
  mediaConstraints, configuration, connectionConstraints, callback)
{
  return WebRtcPeer.start('sendonly', localVideo, null, onSdp, onError,
      mediaConstraints, null, null, configuration, connectionConstraints,
      callback);
};

/**
 * @description This methods creates a WebRtcPeer to send and receive video.
 *
 * @function module:kurentoUtils.WebRtcPeer.startSendRecv
 *
 * @param localVideo -
 *            {Object} Video tag for the local stream
 * @param remoteVideo -
 *            {Object} Video tag for the remote stream
 * @param onSdp -
 *            {Function} Callback executed when a SDP offer has been generated
 * @param onerror -
 *            {Function} Callback executed when an error happens generating an
 *            SDP offer
 * @param mediaConstraints -
 *            {Object[]} Constraints used to create RTCPeerConnection
 *
 * @return {module:kurentoUtils.WebRtcPeer}
 */
WebRtcPeer.startSendRecv = function(localVideo, remoteVideo, onSdp, onError,
  mediaConstraints, configuration, connectionConstraints, callback)
{
  return WebRtcPeer.start('sendrecv', localVideo, remoteVideo, onSdp,
      onError, mediaConstraints, null, null, configuration,
      connectionConstraints, callback);
};

//
// Specialized child classes
//

function WebRtcPeerRecvonly(options)
{
  WebRtcPeerRecvonly.super_.call(this, 'recvonly', options)
}
inherits(WebRtcPeerRecvonly, WebRtcPeer)

function WebRtcPeerSendonly(options)
{
  WebRtcPeerSendonly.super_.call(this, 'sendonly', options)
}
inherits(WebRtcPeerSendonly, WebRtcPeer)

function WebRtcPeerSendrecv(options)
{
  WebRtcPeerSendrecv.super_.call(this, 'sendrecv', options)
}
inherits(WebRtcPeerSendrecv, WebRtcPeer)

module.exports = WebRtcPeer;

WebRtcPeer.WebRtcPeer         = WebRtcPeer;
WebRtcPeer.WebRtcPeerRecvonly = WebRtcPeerRecvonly;
WebRtcPeer.WebRtcPeerSendonly = WebRtcPeerSendonly;
WebRtcPeer.WebRtcPeerSendrecv = WebRtcPeerSendrecv;
<commit_msg>enabled, audioEnabled & videoEnabled attributes (silence & black video)<commit_after>/*
 * (C) Copyright 2014 Kurento (http://kurento.org/)
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-2.1.html
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 */

var freeice  = require('freeice');
var inherits = require('inherits');

var EventEmitter = require('events').EventEmitter;

var recursive = require('merge').recursive

function noop(error)
{
  if(error) console.trace(error)
}

function trackStop(track)
{
  track.stop && track.stop()
}

function streamStop(stream)
{
  stream.getTracks().forEach(trackStop)
}

/**
 * @classdesc Wrapper object of an RTCPeerConnection. This object is aimed to
 *            simplify the development of WebRTC-based applications.
 *
 * @constructor module:kurentoUtils.WebRtcPeer
 *
 * @param mode -
 *            {String} Mode in which the PeerConnection will be configured.
 *            Valid values are: 'recv', 'send', and 'sendRecv'
 * @param localVideo -
 *            {Object} Video tag for the local stream
 * @param remoteVideo -
 *            {Object} Video tag for the remote stream
 * @param onsdpoffer -
 *            {Function} Callback executed when a SDP offer has been generated
 * @param onerror -
 *            {Function} Callback executed when an error happens generating an
 *            SDP offer
 * @param videoStream -
 *            {Object} MediaStream to be used as primary source (typically video
 *            and audio, or only video if combined with audioStream) for
 *            localVideo and to be added as stream to the RTCPeerConnection
 * @param audioStream -
 *            {Object} MediaStream to be used as second source (typically for
 *            audio) for localVideo and to be added as stream to the
 *            RTCPeerConnection
 */
function WebRtcPeer(mode, options, callback)
{
  WebRtcPeer.super_.call(this)

  var localVideo, remoteVideo, onsdpoffer, onerror, mediaConstraints;
  var videoStream, audioStream, connectionConstraints;

  while(arguments.length && !arguments[arguments.length-1]) arguments.length--;

  if(arguments.length > 2)  // Deprecated mode
  {
    console.warn('Positional parameters are deprecated for WebRtcPeer')

    localVideo       = arguments[1];
    remoteVideo      = arguments[2];
    onsdpoffer       = arguments[3];
    onerror          = arguments[4];
    mediaConstraints = arguments[5];
    videoStream      = arguments[6];
    audioStream      = arguments[7];
  }
  else
  {
    localVideo       = options.localVideo;
    remoteVideo      = options.remoteVideo;
    onsdpoffer       = options.onsdpoffer;
    onerror          = options.onerror;
    mediaConstraints = options.mediaConstraints;
    videoStream      = options.videoStream;
    audioStream      = options.audioStream;

    connectionConstraints = options.connectionConstraints;
  }

  if(onerror)    this.on('error',    onerror);
  if(onsdpoffer) this.on('sdpoffer', onsdpoffer);

  // Init PeerConnection

  var pc = options.peerConnection
  if(!pc)
  {
    var configuration = recursive(
    {
      iceServers : freeice()
    },
    WebRtcPeer.prototype.server,
    options.configuration);

    pc = new RTCPeerConnection(configuration);
  }

  Object.defineProperty(this, 'peerConnection', {get: function(){return pc;}});

  var self = this;

  function onSdpOffer_callback(error, sdpAnswer, callback)
  {
    if(error) return console.error(error)

    self.processSdpAnswer(sdpAnswer, callback)
  }

  var ended = false;
  pc.addEventListener('icecandidate', function(event)
  {
    // candidate exists in event.candidate
    if(event.candidate)
    {
      ended = false;
      return;
    }

    // [Hack] On old Chrome, when there's no Internet connection onicecandidate
    // event is being called infinitelly (bug). This prevent it.
    if(ended) return;
    ended = true;

    self.emit('sdpoffer', pc.localDescription.sdp, onSdpOffer_callback);
    console.log('ICE negotiation completed');
  });

  //
  // Priviledged methods
  //

  /**
  * @description This method creates the RTCPeerConnection object taking into
  *              account the properties received in the constructor. It starts
  *              the SDP negotiation process: generates the SDP offer and invokes
  *              the onsdpoffer callback. This callback is expected to send the
  *              SDP offer, in order to obtain an SDP answer from another peer.
  *
  * @function module:kurentoUtils.WebRtcPeer.prototype.start
  */
  this.start = function(constraints, callback)
  {
    if(videoStream && localVideo)
    {
      localVideo.src = URL.createObjectURL(videoStream);
      localVideo.muted = true;
    }

    if(videoStream) pc.addStream(videoStream);
    if(audioStream) pc.addStream(audioStream);

    // Adjust arguments

    if(constraints instanceof Function)
    {
      if(callback) throw new Error('Nothing can be defined after the callback')

      callback    = constraints
      constraints = undefined
    }

    // [Hack] https://code.google.com/p/chromium/issues/detail?id=443558
    if(mode == 'sendonly') mode = 'sendrecv';

    constraints = recursive(
    {
      mandatory:
      {
        OfferToReceiveAudio: (mode !== 'sendonly'),
        OfferToReceiveVideo: (mode !== 'sendonly')
      },
      optional:
      [
        {DtlsSrtpKeyAgreement: true}
      ]
    }, constraints);

    callback = callback || noop;

    // Create the offer with the required constrains

    pc.createOffer(function(offer)
    {
      console.log('Created SDP offer');

      pc.setLocalDescription(offer, function()
      {
        console.log('Local description set', offer);

        callback(null, self);
      },
      callback);
    },
    callback, constraints);
  }

  if(mode !== 'recvonly' && !videoStream)
  {
    mediaConstraints = recursive(
    {
      audio: true,
      video:
      {
        mandatory:
        {
          maxWidth: 640,
          maxFrameRate: 15,
          minFrameRate: 15
        }
      }
    }, mediaConstraints);

    getUserMedia(mediaConstraints, function(stream)
    {
      videoStream = stream;

      self.start(options.connectionConstraints, callback)
    },
    callback || noop);
  }
  else
    self.start(options.connectionConstraints, callback)

  this.on('_dispose', function()
  {
    if(localVideo)  localVideo.src  = '';
    if(remoteVideo) remoteVideo.src = '';
  })

  this.on('_processSdpAnswer', function(url)
  {
    if(remoteVideo)
    {
      remoteVideo.src = url;

      console.log('Remote URL:', url)
    }
  })
}
inherits(WebRtcPeer, EventEmitter)

WebRtcPeer.prototype.server = {}

WebRtcPeer.prototype.getLocalStream = function(index)
{
  if(this.peerConnection)
    return this.peerConnection.getLocalStreams()[index || 0]
}

WebRtcPeer.prototype.getRemoteStream = function(index)
{
  if(this.peerConnection)
    return this.peerConnection.getRemoteStreams()[index || 0]
}

/**
* @description This method frees the resources used by WebRtcPeer.
*
* @function module:kurentoUtils.WebRtcPeer.prototype.dispose
*/
WebRtcPeer.prototype.dispose = function()
{
  console.log('Disposing WebRtcPeer');

  var pc = this.peerConnection;
  if(pc)
  {
    // FIXME This is not yet implemented in firefox
    // if(videoStream) pc.removeStream(videoStream);
    // if(audioStream) pc.removeStream(audioStream);

    // For old browsers, PeerConnection.close() is NOT idempotent and raise
    // error. We check its signaling state and don't close it if it's already
    // closed
    if(pc.signalingState != 'closed') pc.close();

    pc.getLocalStreams().forEach(streamStop)
  }

  this.emit('_dispose');
};

/**
 * @description Callback function invoked when and SDP answer is received.
 *              Developers are expected to invoke this function in order to
 *              complete the SDP negotiation.
 *
 * @function module:kurentoUtils.WebRtcPeer.prototype.processSdpAnswer
 *
 * @param sdpAnswer -
 *            Description of sdpAnswer
 * @param callback -
 *            Invoked after the SDP answer is processed, or there is an error.
 */
WebRtcPeer.prototype.processSdpAnswer = function(sdpAnswer, callback)
{
  var answer = new RTCSessionDescription(
  {
    type : 'answer',
    sdp : sdpAnswer,
  });

  console.log('SDP answer received, setting remote description');

  callback = callback || noop

  var self = this;

  var pc = this.peerConnection;
  pc.setRemoteDescription(answer, function()
  {
    var url = URL.createObjectURL(pc.getRemoteStreams()[0]);

    self.emit('_processSdpAnswer', url);

    callback();
  },
  callback);
}

Object.defineProperty(WebRtcPeer.prototype, 'enabled',
{
  enumerable: true,
  get: function()
  {
    return this.audioEnabled && this.videoEnabled;
  },
  set: function(value)
  {
    this.audioEnabled = this.videoEnabled = value
  }
})

Object.defineProperty(WebRtcPeer.prototype, 'audioEnabled',
{
  enumerable: true,
  get: function()
  {
    if(!this.peerConnection) return;

    var streams = this.peerConnection.getLocalStreams();
    if(!streams.length) return;

    for(var i=0,stream; stream=streams[i]; i++)
      for(var j=0,track; track=stream.getAudioTracks()[j]; j++)
        if(!track.enabled)
          return false;

    return true;
  },
  set: function(value)
  {
    this.peerConnection.getLocalStreams().forEach(function(stream)
    {
      stream.getAudioTracks().forEach(function(track)
      {
        track.enabled = value;
      })
    })
  }
})

Object.defineProperty(WebRtcPeer.prototype, 'videoEnabled',
{
  enumerable: true,
  get: function()
  {
    if(!this.peerConnection) return;

    var streams = this.peerConnection.getLocalStreams();
    if(!streams.length) return;

    for(var i=0,stream; stream=streams[i]; i++)
      for(var j=0,track; track=stream.getVideoTracks()[j]; j++)
        if(!track.enabled)
          return false;

    return true;
  },
  set: function(value)
  {
    this.peerConnection.getLocalStreams().forEach(function(stream)
    {
      stream.getVideoTracks().forEach(function(track)
      {
        track.enabled = value;
      })
    })
  }
})

//
// Static factory functions
//

/**
 * @description This method creates the WebRtcPeer object and obtain userMedia
 *              if needed.
 *
 * @function module:kurentoUtils.WebRtcPeer.start
 *
 * @param mode -
 *            {String} Mode in which the PeerConnection will be configured.
 *            Valid values are: 'recv', 'send', and 'sendRecv'
 * @param localVideo -
 *            {Object} Video tag for the local stream
 * @param remoteVideo -
 *            {Object} Video tag for the remote stream
 * @param onSdp -
 *            {Function} Callback executed when a SDP offer has been generated
 * @param onerror -
 *            {Function} Callback executed when an error happens generating an
 *            SDP offer
 * @param mediaConstraints -
 *            {Object[]} Constraints used to create RTCPeerConnection
 * @param videoStream -
 *            {Object} MediaStream to be used as primary source (typically video
 *            and audio, or only video if combined with audioStream) for
 *            localVideo and to be added as stream to the RTCPeerConnection
 * @param videoStream -
 *            {Object} MediaStream to be used as primary source (typically video
 *            and audio, or only video if combined with audioStream) for
 *            localVideo and to be added as stream to the RTCPeerConnection
 * @param audioStream -
 *            {Object} MediaStream to be used as second source (typically for
 *            audio) for localVideo and to be added as stream to the
 *            RTCPeerConnection
 *
 * @return {module:kurentoUtils.WebRtcPeer}
 */
WebRtcPeer.start = function(mode, localVideo, remoteVideo, onsdpoffer, onerror,
    mediaConstraints, videoStream, audioStream, configuration,
    connectionConstraints, callback)
{
  var options =
  {
    localVideo      : localVideo,
    remoteVideo     : remoteVideo,
    onsdpoffer      : onsdpoffer,
    onerror         : onerror,
    mediaConstraints: mediaConstraints,
    videoStream     : videoStream,
    audioStream     : audioStream,
    configuration   : configuration,

    connectionConstraints: connectionConstraints
  };

  return new WebRtcPeer(mode, options, callback);
};

/**
 * @description This methods creates a WebRtcPeer to receive video.
 *
 * @function module:kurentoUtils.WebRtcPeer.startRecvOnly
 *
 * @param remoteVideo -
 *            {Object} Video tag for the remote stream
 * @param onSdp -
 *            {Function} Callback executed when a SDP offer has been generated
 * @param onerror -
 *            {Function} Callback executed when an error happens generating an
 *            SDP offer
 * @param mediaConstraints -
 *            {Object[]} Constraints used to create RTCPeerConnection
 *
 * @return {module:kurentoUtils.WebRtcPeer}
 */
WebRtcPeer.startRecvOnly = function(remoteVideo, onSdp, onError,
  mediaConstraints, configuration, connectionConstraints, callback)
{
  return WebRtcPeer.start('recvonly', null, remoteVideo, onSdp, onError,
      mediaConstraints, null, null, configuration, connectionConstraints,
      callback);
};

/**
 * @description This methods creates a WebRtcPeer to send video.
 *
 * @function module:kurentoUtils.WebRtcPeer.startSendOnly
 *
 * @param localVideo -
 *            {Object} Video tag for the local stream
 * @param onSdp -
 *            {Function} Callback executed when a SDP offer has been generated
 * @param onerror -
 *            {Function} Callback executed when an error happens generating an
 *            SDP offer
 * @param mediaConstraints -
 *            {Object[]} Constraints used to create RTCPeerConnection
 *
 * @return {module:kurentoUtils.WebRtcPeer}
 */
WebRtcPeer.startSendOnly = function(localVideo, onSdp, onError,
  mediaConstraints, configuration, connectionConstraints, callback)
{
  return WebRtcPeer.start('sendonly', localVideo, null, onSdp, onError,
      mediaConstraints, null, null, configuration, connectionConstraints,
      callback);
};

/**
 * @description This methods creates a WebRtcPeer to send and receive video.
 *
 * @function module:kurentoUtils.WebRtcPeer.startSendRecv
 *
 * @param localVideo -
 *            {Object} Video tag for the local stream
 * @param remoteVideo -
 *            {Object} Video tag for the remote stream
 * @param onSdp -
 *            {Function} Callback executed when a SDP offer has been generated
 * @param onerror -
 *            {Function} Callback executed when an error happens generating an
 *            SDP offer
 * @param mediaConstraints -
 *            {Object[]} Constraints used to create RTCPeerConnection
 *
 * @return {module:kurentoUtils.WebRtcPeer}
 */
WebRtcPeer.startSendRecv = function(localVideo, remoteVideo, onSdp, onError,
  mediaConstraints, configuration, connectionConstraints, callback)
{
  return WebRtcPeer.start('sendrecv', localVideo, remoteVideo, onSdp,
      onError, mediaConstraints, null, null, configuration,
      connectionConstraints, callback);
};

//
// Specialized child classes
//

function WebRtcPeerRecvonly(options)
{
  WebRtcPeerRecvonly.super_.call(this, 'recvonly', options)
}
inherits(WebRtcPeerRecvonly, WebRtcPeer)

function WebRtcPeerSendonly(options)
{
  WebRtcPeerSendonly.super_.call(this, 'sendonly', options)
}
inherits(WebRtcPeerSendonly, WebRtcPeer)

function WebRtcPeerSendrecv(options)
{
  WebRtcPeerSendrecv.super_.call(this, 'sendrecv', options)
}
inherits(WebRtcPeerSendrecv, WebRtcPeer)

module.exports = WebRtcPeer;

WebRtcPeer.WebRtcPeer         = WebRtcPeer;
WebRtcPeer.WebRtcPeerRecvonly = WebRtcPeerRecvonly;
WebRtcPeer.WebRtcPeerSendonly = WebRtcPeerSendonly;
WebRtcPeer.WebRtcPeerSendrecv = WebRtcPeerSendrecv;
<|endoftext|>


<commit_before>var util = require('util');
var AbstractGeocoder = require('./abstractgeocoder');

/**
 * Constructor
 * @param <object> httpAdapter Http Adapter
 * @param <object> options     Options (language, clientId, apiKey)
 */
var VirtualEarthGeocoder = function VirtualEarthGeocoder(httpAdapter, options) {

  VirtualEarthGeocoder.super_.call(this, httpAdapter, options);

  if (!this.options.apiKey || this.options.apiKey == 'undefined') {
    throw new Error('You must specify an apiKey');
  }
};

util.inherits(VirtualEarthGeocoder, AbstractGeocoder);

// TomTom geocoding API endpoint
VirtualEarthGeocoder.prototype._endpoint = 'http://dev.virtualearth.net/REST/v1/Locations';

/**
* Geocode
* @param <string>   value    Value to geocode (Address)
* @param <function> callback Callback method
*/
VirtualEarthGeocoder.prototype._geocode = function(value, callback) {

  var _this = this;

  var params = {
    q : value,
    key   : this.options.apiKey
  };

  this.httpAdapter.get(this._endpoint, params, function(err, result) {
    if (err) {
      return callback(err);
    } else {
      var results = [];

      for(var i = 0; i < result.resourceSets[0].resources.length; i++) {
          results.push(_this._formatResult(result.resourceSets[0].resources[i]));
      }

      results.raw = result;
      callback(false, results);
    }
  });
};

VirtualEarthGeocoder.prototype._formatResult = function(result) {
  return {
    'latitude' : result.point.coordinates[0],
    'longitude' : result.point.coordinates[1],
    'country' : result.address.countryRegion,
    'city' : result.address.locality,
    'state' : result.address.adminDistrict,
    'zipcode' : result.address.postalCode,
    'streetName': result.address.addressLine
  };
};

module.exports = VirtualEarthGeocoder;
<commit_msg>Add Bing reverse geocoding support<commit_after>var util = require('util');
var AbstractGeocoder = require('./abstractgeocoder');

/**
 * Constructor
 * @param <object> httpAdapter Http Adapter
 * @param <object> options     Options (language, clientId, apiKey)
 */
var VirtualEarthGeocoder = function VirtualEarthGeocoder(httpAdapter, options) {

  VirtualEarthGeocoder.super_.call(this, httpAdapter, options);

  if (!this.options.apiKey || this.options.apiKey == 'undefined') {
    throw new Error('You must specify an apiKey');
  }
};

util.inherits(VirtualEarthGeocoder, AbstractGeocoder);

// TomTom geocoding API endpoint
VirtualEarthGeocoder.prototype._endpoint = 'http://dev.virtualearth.net/REST/v1/Locations';

/**
* Geocode
* @param <string>   value    Value to geocode (Address)
* @param <function> callback Callback method
*/
VirtualEarthGeocoder.prototype._geocode = function(value, callback) {

  var _this = this;

  var params = {
    q : value,
    key   : this.options.apiKey
  };

  this.httpAdapter.get(this._endpoint, params, function(err, result) {
    if (err) {
      return callback(err);
    } else {
      var results = [];

      for(var i = 0; i < result.resourceSets[0].resources.length; i++) {
          results.push(_this._formatResult(result.resourceSets[0].resources[i]));
      }

      results.raw = result;
      callback(false, results);
    }
  });
};

/**
* Reverse geocoding
* @param {lat:<number>, lon:<number>}  lat: Latitude, lon: Longitude
* @param <function> callback Callback method
*/
VirtualEarthGeocoder.prototype._reverse = function(value, callback) {

  var _this = this;

  var params = {
    key: this.options.apiKey
  };

  var endpoint = this._endpoint + "/" + value.lat + "," + value.lon;

  this.httpAdapter.get(endpoint, params, function(err, result) {
    if (err) {
      return callback(err);
    } else {
      var results = [];

      for(var i = 0; i < result.resourceSets[0].resources.length; i++) {
          results.push(_this._formatResult(result.resourceSets[0].resources[i]));
      }

      results.raw = result;
      callback(false, results);
    }
  });
}

VirtualEarthGeocoder.prototype._formatResult = function(result) {
  return {
    'latitude' : result.point.coordinates[0],
    'longitude' : result.point.coordinates[1],
    'country' : result.address.countryRegion,
    'city' : result.address.locality,
    'state' : result.address.adminDistrict,
    'zipcode' : result.address.postalCode,
    'streetName': result.address.addressLine
  };
};

module.exports = VirtualEarthGeocoder;
<|endoftext|>


<commit_before>var uuidGen = require('uuid')
var zmq = require('zmq')
var Logger = require('../logger')
var logHelper = require('./support/log_helper')

describe('Publisher Module', function () {
  var socket, publisher, log, config, clock

  before(function () {
    // since log is obtain on module loading this to the trick
    log = logHelper.getLogStub()
    sinon.stub(Logger, 'getLogger').returns(log)

    // we first should stub the logger because is required on module
    publisher = require('../lib/publisher')
  })

  after(function () {
    Logger.getLogger.restore()
  })

  beforeEach(function () {
    config = { producerId: 'producer' }
    socket = { connect: sinon.spy(), send: sinon.spy() }
    sinon.stub(zmq, 'socket').returns(socket)
    var ts = new Date('2016-11-10 16:00:00').getTime()
    clock = sinon.useFakeTimers(ts)
    sinon.stub(uuidGen, "v4").returns('uuid')
  })

  afterEach(function () {
    zmq.socket.restore()
    uuidGen.v4.restore()
    clock.restore()
  })

  describe('#getInstance', function () {
    it('should obtain the logger micro.bus.publisher', function() {
      publisher.getInstance(config)
      Logger.getLogger.should.have.been.calledWith('micro.bus.publisher')
    })

    it('should log stream opening information', function () {
      publisher.getInstance(config)
      log.info.should.have.been.calledWith(
        "Producer '%s' opened a publisher stream to %s",
        "producer", 'tcp://127.0.0.1:5558'
      )
    })

    it('should return a publisher instance', function () {
      publisher.getInstance(config).should.respondTo('send')
    })

    it('should enforce a valid producer id', function () {
      publisher.getInstance.should.throw(/Invalid producer id/)
    })

    it('should open a zmq dealer socket', function () {
      publisher.getInstance(config)
      zmq.socket.should.have.been.calledWith('dealer')
    })

    it('should connect socket to configuration address', function () {
      config.address = 'tcp://127.0.0.1:6668'
      publisher.getInstance(config)
      socket.connect.should.have.been.calledWith(config.address)
    })

    it('should connect socket to default address when not specified', function () {
      publisher.getInstance(config)
      socket.connect.should.have.been.calledWith('tcp://127.0.0.1:5558')
    })
  })

  describe('.send', function () {
    var target

    beforeEach(function () {
      target = publisher.getInstance(config)
    })

    it('should log that event was sent', function () {
      var evt = evt = {
        data: 'data',
        producer: 'producer',
        timestamp: new Date('2016-11-10 16:00:00'),
        topic: '/topic/test',
        uuid: 'uuid'
      }
      target.send('/topic/test', "data")
      log.debug.should.have.been.calledWith(
        evt, 'Published event to topic %s', '/topic/test'
      )
    })

    it('should send a event', function () {
      target.send('/topic/test', "data")
      socket.send.should.have.been.called
    })

    it('should send a 0MQ message with 6 frames', function () {
      target.send('/topic/test', "data")
      socket.send.should.have.been.calledWith(
        sinon.match.has('length', 6)
      )
    })

    it('should send a 0MQ message with RFC format', function () {
      target.send('/topic/test', "data")
      socket.send.should.have.been.calledWith(
        sinon.match(function (value) {
          return value.length == 6
            && value[0] === '/topic/test'
            && value[1] === null
            && value[2] === 'producer'
            && value[3] === '2016-11-10T16:00:00.000Z'
            && value[4] === 'uuid'
            && value[5] === 'data'
        }, 'matches RFC')
      )
    })
  })
})
<commit_msg>use socket helper to create a socket stub instead<commit_after>var uuidGen = require('uuid')
var zmq = require('zmq')
var Logger = require('../logger')
var logHelper = require('./support/log_helper')
var zmqHelper = require('./support/zmq_helper')

describe('Publisher Module', function () {
  var socket, publisher, log, config, clock

  before(function () {
    // since log is obtain on module loading this to the trick
    log = logHelper.getLogStub()
    sinon.stub(Logger, 'getLogger').returns(log)

    // we first should stub the logger because is required on module
    publisher = require('../lib/publisher')
  })

  after(function () {
    Logger.getLogger.restore()
  })

  beforeEach(function () {
    config = { producerId: 'producer' }
    socket = zmqHelper.getSocketStub()
    sinon.stub(zmq, 'socket').returns(socket)
    var ts = new Date('2016-11-10 16:00:00').getTime()
    clock = sinon.useFakeTimers(ts)
    sinon.stub(uuidGen, "v4").returns('uuid')
  })

  afterEach(function () {
    zmq.socket.restore()
    uuidGen.v4.restore()
    clock.restore()
  })

  describe('#getInstance', function () {
    it('should obtain the logger micro.bus.publisher', function() {
      publisher.getInstance(config)
      Logger.getLogger.should.have.been.calledWith('micro.bus.publisher')
    })

    it('should log stream opening information', function () {
      publisher.getInstance(config)
      log.info.should.have.been.calledWith(
        "Producer '%s' opened a publisher stream to %s",
        "producer", 'tcp://127.0.0.1:5558'
      )
    })

    it('should return a publisher instance', function () {
      publisher.getInstance(config).should.respondTo('send')
    })

    it('should enforce a valid producer id', function () {
      publisher.getInstance.should.throw(/Invalid producer id/)
    })

    it('should open a zmq dealer socket', function () {
      publisher.getInstance(config)
      zmq.socket.should.have.been.calledWith('dealer')
    })

    it('should connect socket to configuration address', function () {
      config.address = 'tcp://127.0.0.1:6668'
      publisher.getInstance(config)
      socket.connect.should.have.been.calledWith(config.address)
    })

    it('should connect socket to default address when not specified', function () {
      publisher.getInstance(config)
      socket.connect.should.have.been.calledWith('tcp://127.0.0.1:5558')
    })
  })

  describe('.send', function () {
    var target

    beforeEach(function () {
      target = publisher.getInstance(config)
    })

    it('should log that event was sent', function () {
      var evt = evt = {
        data: 'data',
        producer: 'producer',
        timestamp: new Date('2016-11-10 16:00:00'),
        topic: '/topic/test',
        uuid: 'uuid'
      }
      target.send('/topic/test', "data")
      log.debug.should.have.been.calledWith(
        evt, 'Published event to topic %s', '/topic/test'
      )
    })

    it('should send a event', function () {
      target.send('/topic/test', "data")
      socket.send.should.have.been.called
    })

    it('should send a 0MQ message with 6 frames', function () {
      target.send('/topic/test', "data")
      socket.send.should.have.been.calledWith(
        sinon.match.has('length', 6)
      )
    })

    it('should send a 0MQ message with RFC format', function () {
      target.send('/topic/test', "data")
      socket.send.should.have.been.calledWith(
        sinon.match(function (value) {
          return value.length == 6
            && value[0] === '/topic/test'
            && value[1] === null
            && value[2] === 'producer'
            && value[3] === '2016-11-10T16:00:00.000Z'
            && value[4] === 'uuid'
            && value[5] === 'data'
        }, 'matches RFC')
      )
    })
  })
})
<|endoftext|>